blob: 1de15c3ff7d0527eb5fa690965d8a53ce20a253e [file] [log] [blame]
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * insexpand.c: functions for Insert mode completion
12 */
13
14#include "vim.h"
15
Bram Moolenaar7591bb32019-03-30 13:53:47 +010016/*
17 * Definitions used for CTRL-X submode.
18 * Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] and
19 * ctrl_x_mode_names[] below.
20 */
21# define CTRL_X_WANT_IDENT 0x100
22
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +010023# define CTRL_X_NORMAL 0 // CTRL-N CTRL-P completion, default
Bram Moolenaar7591bb32019-03-30 13:53:47 +010024# define CTRL_X_NOT_DEFINED_YET 1
25# define CTRL_X_SCROLL 2
26# define CTRL_X_WHOLE_LINE 3
27# define CTRL_X_FILES 4
28# define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT)
29# define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT)
30# define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT)
31# define CTRL_X_FINISHED 8
32# define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT)
33# define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT)
34# define CTRL_X_CMDLINE 11
Bram Moolenaar9810cfb2019-12-11 21:23:00 +010035# define CTRL_X_FUNCTION 12
Bram Moolenaar7591bb32019-03-30 13:53:47 +010036# define CTRL_X_OMNI 13
37# define CTRL_X_SPELL 14
Bram Moolenaaraa2f0ee2019-12-21 18:47:26 +010038# define CTRL_X_LOCAL_MSG 15 // only used in "ctrl_x_msgs"
39# define CTRL_X_EVAL 16 // for builtin function complete()
zeertzjqdca29d92021-08-31 19:12:51 +020040# define CTRL_X_CMDLINE_CTRL_X 17 // CTRL-X typed in CTRL_X_CMDLINE
glepnir05460682025-05-26 18:23:27 +020041# define CTRL_X_REGISTER 18 // complete words from registers
Bram Moolenaar7591bb32019-03-30 13:53:47 +010042
43# define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
44
45// Message for CTRL-X mode, index is ctrl_x_mode.
46static char *ctrl_x_msgs[] =
47{
48 N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl.
glepnir05460682025-05-26 18:23:27 +020049 N_(" ^X mode (^]^D^E^F^I^K^L^N^O^P^Rs^U^V^Y)"),
Bram Moolenaar7591bb32019-03-30 13:53:47 +010050 NULL, // CTRL_X_SCROLL: depends on state
51 N_(" Whole line completion (^L^N^P)"),
52 N_(" File name completion (^F^N^P)"),
53 N_(" Tag completion (^]^N^P)"),
54 N_(" Path pattern completion (^N^P)"),
55 N_(" Definition completion (^D^N^P)"),
56 NULL, // CTRL_X_FINISHED
57 N_(" Dictionary completion (^K^N^P)"),
58 N_(" Thesaurus completion (^T^N^P)"),
59 N_(" Command-line completion (^V^N^P)"),
60 N_(" User defined completion (^U^N^P)"),
61 N_(" Omni completion (^O^N^P)"),
zeertzjq7002c052024-06-21 07:55:07 +020062 N_(" Spelling suggestion (^S^N^P)"),
Bram Moolenaar7591bb32019-03-30 13:53:47 +010063 N_(" Keyword Local completion (^N^P)"),
64 NULL, // CTRL_X_EVAL doesn't use msg.
zeertzjqdca29d92021-08-31 19:12:51 +020065 N_(" Command-line completion (^V^N^P)"),
glepnir05460682025-05-26 18:23:27 +020066 N_(" Register completion (^N^P)"),
Bram Moolenaar7591bb32019-03-30 13:53:47 +010067};
68
Bram Moolenaar9cb698d2019-08-21 15:30:45 +020069#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +010070static char *ctrl_x_mode_names[] = {
Hirohito Higashi355db992025-04-28 18:07:02 +020071 "keyword",
72 "ctrl_x",
73 "scroll",
74 "whole_line",
75 "files",
76 "tags",
77 "path_patterns",
78 "path_defines",
79 "unknown", // CTRL_X_FINISHED
80 "dictionary",
81 "thesaurus",
82 "cmdline",
83 "function",
84 "omni",
85 "spell",
86 NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
87 "eval",
88 "cmdline",
glepnir05460682025-05-26 18:23:27 +020089 "register",
Bram Moolenaar7591bb32019-03-30 13:53:47 +010090};
Bram Moolenaar9cb698d2019-08-21 15:30:45 +020091#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +010092
93/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +010094 * Structure used to store one match for insert completion.
95 */
96typedef struct compl_S compl_T;
97struct compl_S
98{
99 compl_T *cp_next;
100 compl_T *cp_prev;
glepnir80b66202024-11-27 21:53:53 +0100101 compl_T *cp_match_next; // matched next compl_T
John Marriott5e6ea922024-11-23 14:01:57 +0100102 string_T cp_str; // matched text
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200103 char_u *(cp_text[CPT_COUNT]); // text for the menu
Bram Moolenaarab782c52020-01-04 19:00:11 +0100104#ifdef FEAT_EVAL
Bram Moolenaar08928322020-01-04 14:32:48 +0100105 typval_T cp_user_data;
Bram Moolenaarab782c52020-01-04 19:00:11 +0100106#endif
glepnir0fe17f82024-10-08 22:26:44 +0200107 char_u *cp_fname; // file containing the match, allocated when
108 // cp_flags has CP_FREE_FNAME
109 int cp_flags; // CP_ values
110 int cp_number; // sequence number
Girish Palyab1565882025-04-15 20:16:00 +0200111 int cp_score; // fuzzy match score or proximity score
glepnird4088ed2024-12-31 10:55:22 +0100112 int cp_in_match_array; // collected by compl_match_array
glepnir7baa0142024-10-09 20:19:25 +0200113 int cp_user_abbr_hlattr; // highlight attribute for abbr
glepnir0fe17f82024-10-08 22:26:44 +0200114 int cp_user_kind_hlattr; // highlight attribute for kind
Girish Palya0ac1eb32025-04-16 20:18:33 +0200115 int cp_cpt_source_idx; // index of this match's source in 'cpt' option
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100116};
117
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200118// values for cp_flags
119# define CP_ORIGINAL_TEXT 1 // the original text when the expansion begun
120# define CP_FREE_FNAME 2 // cp_fname is allocated
121# define CP_CONT_S_IPOS 4 // use CONT_S_IPOS for compl_cont_status
122# define CP_EQUAL 8 // ins_compl_equal() always returns TRUE
123# define CP_ICASE 16 // ins_compl_equal() ignores case
Bram Moolenaar440cf092021-04-03 20:13:30 +0200124# define CP_FAST 32 // use fast_breakcheck instead of ui_breakcheck
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100125
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100126/*
127 * All the current matches are stored in a list.
128 * "compl_first_match" points to the start of the list.
129 * "compl_curr_match" points to the currently selected entry.
130 * "compl_shown_match" is different from compl_curr_match during
Girish Palyacbe53192025-04-14 22:13:15 +0200131 * ins_compl_get_exp(), when new matches are added to the list.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000132 * "compl_old_match" points to previous "compl_curr_match".
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100133 */
134static compl_T *compl_first_match = NULL;
135static compl_T *compl_curr_match = NULL;
136static compl_T *compl_shown_match = NULL;
137static compl_T *compl_old_match = NULL;
138
glepnirf31cfa22025-03-06 21:59:13 +0100139// list used to store the compl_T which have the max score
140// used for completefuzzycollect
141static compl_T **compl_best_matches = NULL;
142static int compl_num_bests = 0;
143// inserted a longest when completefuzzycollect enabled
144static int compl_cfc_longest_ins = FALSE;
145
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100146// After using a cursor key <Enter> selects a match in the popup menu,
147// otherwise it inserts a line break.
148static int compl_enter_selects = FALSE;
149
150// When "compl_leader" is not NULL only matches that start with this string
151// are used.
John Marriott5e6ea922024-11-23 14:01:57 +0100152static string_T compl_leader = {NULL, 0};
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100153
154static int compl_get_longest = FALSE; // put longest common string
155 // in compl_leader
156
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100157// Selected one of the matches. When FALSE the match was edited or using the
158// longest common string.
159static int compl_used_match;
160
161// didn't finish finding completions.
162static int compl_was_interrupted = FALSE;
163
164// Set when character typed while looking for matches and it means we should
165// stop looking for matches.
166static int compl_interrupted = FALSE;
167
168static int compl_restarting = FALSE; // don't insert match
169
170// When the first completion is done "compl_started" is set. When it's
171// FALSE the word to be completed must be located.
172static int compl_started = FALSE;
173
174// Which Ctrl-X mode are we in?
175static int ctrl_x_mode = CTRL_X_NORMAL;
176
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000177static int compl_matches = 0; // number of completion matches
Girish Palyacbe53192025-04-14 22:13:15 +0200178static string_T compl_pattern = {NULL, 0}; // search pattern for matching items
179#ifdef FEAT_COMPL_FUNC
180static string_T cpt_compl_pattern = {NULL, 0}; // pattern returned by func in 'cpt'
181#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100182static int compl_direction = FORWARD;
183static int compl_shows_dir = FORWARD;
184static int compl_pending = 0; // > 1 for postponed CTRL-N
185static pos_T compl_startpos;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000186// Length in bytes of the text being completed (this is deleted to be replaced
187// by the match.)
188static int compl_length = 0;
glepnir76bdb822025-02-08 19:04:51 +0100189static linenr_T compl_lnum = 0; // lnum where the completion start
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100190static colnr_T compl_col = 0; // column where the text starts
191 // that is being completed
glepnir6a38aff2024-12-16 21:56:16 +0100192static colnr_T compl_ins_end_col = 0;
John Marriott5e6ea922024-11-23 14:01:57 +0100193static string_T compl_orig_text = {NULL, 0}; // text as it was before
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100194 // completion started
195static int compl_cont_mode = 0;
196static expand_T compl_xp;
197
glepnircf7f0122025-04-15 19:02:00 +0200198static win_T *compl_curr_win = NULL; // win where completion is active
199static buf_T *compl_curr_buf = NULL; // buf where completion is active
200
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000201// List of flags for method of completion.
202static int compl_cont_status = 0;
203# define CONT_ADDING 1 // "normal" or "adding" expansion
204# define CONT_INTRPT (2 + 4) // a ^X interrupted the current expansion
205 // it's set only iff N_ADDS is set
206# define CONT_N_ADDS 4 // next ^X<> will add-new or expand-current
207# define CONT_S_IPOS 8 // next ^X<> will set initial_pos?
208 // if so, word-wise-expansion will set SOL
209# define CONT_SOL 16 // pattern includes start of line, just for
210 // word-wise expansion, not set for ^X^L
211# define CONT_LOCAL 32 // for ctrl_x_mode 0, ^X^P/^X^N do a local
212 // expansion, (eg use complete=.)
213
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100214static int compl_opt_refresh_always = FALSE;
215static int compl_opt_suppress_empty = FALSE;
216
glepnira218cc62024-06-03 19:32:39 +0200217static int compl_selected_item = -1;
218
glepnir8159fb12024-07-17 20:32:54 +0200219static int *compl_fuzzy_scores;
220
Girish Palya0ac1eb32025-04-16 20:18:33 +0200221// Define the structure for completion source (in 'cpt' option) information
222typedef struct cpt_source_T
223{
224 int refresh_always; // Flag array to indicate which 'cpt' functions have 'refresh:always' set
225 int max_matches; // Maximum number of items to display in the menu from the source
226} cpt_source_T;
227
228static cpt_source_T *cpt_sources_array; // Pointer to the array of completion sources
229static int cpt_sources_count; // Total number of completion sources specified in the 'cpt' option
Girish Palya7c621052025-05-26 19:41:59 +0200230static int cpt_sources_index = -1; // Index of the current completion source being expanded
Girish Palyacbe53192025-04-14 22:13:15 +0200231
glepnir6a38aff2024-12-16 21:56:16 +0100232// "compl_match_array" points the currently displayed list of entries in the
233// popup menu. It is NULL when there is no popup menu.
234static pumitem_T *compl_match_array = NULL;
235static int compl_match_arraysize;
236
glepnirf31cfa22025-03-06 21:59:13 +0100237static int ins_compl_add(char_u *str, int len, char_u *fname, char_u **cptext, typval_T *user_data, int cdir, int flags, int adup, int *user_hl, int score);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100238static void ins_compl_longest_match(compl_T *match);
239static void ins_compl_del_pum(void);
240static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100241static void ins_compl_free(void);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100242static int ins_compl_need_restart(void);
243static void ins_compl_new_leader(void);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000244static int get_compl_len(void);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100245static void ins_compl_restart(void);
John Marriott5e6ea922024-11-23 14:01:57 +0100246static void ins_compl_set_original_text(char_u *str, size_t len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100247static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg);
248# if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
249static void ins_compl_add_list(list_T *list);
250static void ins_compl_add_dict(dict_T *dict);
Girish Palyacbe53192025-04-14 22:13:15 +0200251static int get_userdefined_compl_info(colnr_T curs_col, callback_T *cb, int *startcol);
Girish Palyacbe53192025-04-14 22:13:15 +0200252static void get_cpt_func_completion_matches(callback_T *cb);
Girish Palya0ac1eb32025-04-16 20:18:33 +0200253static callback_T *get_cpt_func_callback(char_u *funcname);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100254# endif
Girish Palya0ac1eb32025-04-16 20:18:33 +0200255static int cpt_sources_init(void);
Girish Palyacbe53192025-04-14 22:13:15 +0200256static int is_cpt_func_refresh_always(void);
Girish Palya0ac1eb32025-04-16 20:18:33 +0200257static void cpt_sources_clear(void);
Girish Palyacbe53192025-04-14 22:13:15 +0200258static void cpt_compl_refresh(void);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100259static int ins_compl_key2dir(int c);
260static int ins_compl_pum_key(int c);
261static int ins_compl_key2count(int c);
262static void show_pum(int prev_w_wrow, int prev_w_leftcol);
263static unsigned quote_meta(char_u *dest, char_u *str, int len);
glepnir76bdb822025-02-08 19:04:51 +0100264static int ins_compl_has_multiple(void);
265static void ins_compl_expand_multiple(char_u *str);
glepnirf31cfa22025-03-06 21:59:13 +0100266static void ins_compl_longest_insert(char_u *prefix);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100267
268#ifdef FEAT_SPELL
269static void spell_back_to_badword(void);
270static int spell_bad_len = 0; // length of located bad word
271#endif
272
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100273/*
274 * CTRL-X pressed in Insert mode.
275 */
276 void
277ins_ctrl_x(void)
278{
zeertzjqdca29d92021-08-31 19:12:51 +0200279 if (!ctrl_x_mode_cmdline())
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100280 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000281 // if the next ^X<> won't ADD nothing, then reset compl_cont_status
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100282 if (compl_cont_status & CONT_N_ADDS)
283 compl_cont_status |= CONT_INTRPT;
284 else
285 compl_cont_status = 0;
286 // We're not sure which CTRL-X mode it will be yet
287 ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
288 edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
289 edit_submode_pre = NULL;
290 showmode();
291 }
zeertzjqdca29d92021-08-31 19:12:51 +0200292 else
293 // CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X
294 // CTRL-V look like CTRL-N
295 ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +0100296
LemonBoy2bf52dd2022-04-09 18:17:34 +0100297 may_trigger_modechanged();
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100298}
299
300/*
301 * Functions to check the current CTRL-X mode.
302 */
Bram Moolenaarebfec1c2023-01-22 21:14:53 +0000303int ctrl_x_mode_none(void)
304 { return ctrl_x_mode == 0; }
305int ctrl_x_mode_normal(void)
306 { return ctrl_x_mode == CTRL_X_NORMAL; }
307int ctrl_x_mode_scroll(void)
308 { return ctrl_x_mode == CTRL_X_SCROLL; }
309int ctrl_x_mode_whole_line(void)
310 { return ctrl_x_mode == CTRL_X_WHOLE_LINE; }
311int ctrl_x_mode_files(void)
312 { return ctrl_x_mode == CTRL_X_FILES; }
313int ctrl_x_mode_tags(void)
314 { return ctrl_x_mode == CTRL_X_TAGS; }
315int ctrl_x_mode_path_patterns(void)
316 { return ctrl_x_mode == CTRL_X_PATH_PATTERNS; }
317int ctrl_x_mode_path_defines(void)
318 { return ctrl_x_mode == CTRL_X_PATH_DEFINES; }
319int ctrl_x_mode_dictionary(void)
320 { return ctrl_x_mode == CTRL_X_DICTIONARY; }
321int ctrl_x_mode_thesaurus(void)
322 { return ctrl_x_mode == CTRL_X_THESAURUS; }
323int ctrl_x_mode_cmdline(void)
324 { return ctrl_x_mode == CTRL_X_CMDLINE
zeertzjqdca29d92021-08-31 19:12:51 +0200325 || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X; }
Bram Moolenaarebfec1c2023-01-22 21:14:53 +0000326int ctrl_x_mode_function(void)
327 { return ctrl_x_mode == CTRL_X_FUNCTION; }
328int ctrl_x_mode_omni(void)
329 { return ctrl_x_mode == CTRL_X_OMNI; }
330int ctrl_x_mode_spell(void)
331 { return ctrl_x_mode == CTRL_X_SPELL; }
332static int ctrl_x_mode_eval(void)
333 { return ctrl_x_mode == CTRL_X_EVAL; }
334int ctrl_x_mode_line_or_eval(void)
335 { return ctrl_x_mode == CTRL_X_WHOLE_LINE || ctrl_x_mode == CTRL_X_EVAL; }
glepnir05460682025-05-26 18:23:27 +0200336int ctrl_x_mode_register(void)
337 { return ctrl_x_mode == CTRL_X_REGISTER; }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100338
339/*
340 * Whether other than default completion has been selected.
341 */
342 int
343ctrl_x_mode_not_default(void)
344{
345 return ctrl_x_mode != CTRL_X_NORMAL;
346}
347
348/*
zeertzjqdca29d92021-08-31 19:12:51 +0200349 * Whether CTRL-X was typed without a following character,
350 * not including when in CTRL-X CTRL-V mode.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100351 */
352 int
353ctrl_x_mode_not_defined_yet(void)
354{
355 return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET;
356}
357
358/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000359 * Return TRUE if currently in "normal" or "adding" insert completion matches
360 * state
361 */
362 int
363compl_status_adding(void)
364{
365 return compl_cont_status & CONT_ADDING;
366}
367
368/*
369 * Return TRUE if the completion pattern includes start of line, just for
370 * word-wise expansion.
371 */
372 int
373compl_status_sol(void)
374{
375 return compl_cont_status & CONT_SOL;
376}
377
378/*
379 * Return TRUE if ^X^P/^X^N will do a local completion (i.e. use complete=.)
380 */
381 int
382compl_status_local(void)
383{
384 return compl_cont_status & CONT_LOCAL;
385}
386
387/*
388 * Clear the completion status flags
389 */
390 void
391compl_status_clear(void)
392{
393 compl_cont_status = 0;
394}
395
396/*
397 * Return TRUE if completion is using the forward direction matches
398 */
399 static int
400compl_dir_forward(void)
401{
402 return compl_direction == FORWARD;
403}
404
405/*
406 * Return TRUE if currently showing forward completion matches
407 */
408 static int
409compl_shows_dir_forward(void)
410{
411 return compl_shows_dir == FORWARD;
412}
413
414/*
415 * Return TRUE if currently showing backward completion matches
416 */
417 static int
418compl_shows_dir_backward(void)
419{
420 return compl_shows_dir == BACKWARD;
421}
422
423/*
424 * Return TRUE if the 'dictionary' or 'thesaurus' option can be used.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100425 */
426 int
427has_compl_option(int dict_opt)
428{
429 if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL
Bram Moolenaare2c453d2019-08-21 14:37:09 +0200430#ifdef FEAT_SPELL
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100431 && !curwin->w_p_spell
Bram Moolenaare2c453d2019-08-21 14:37:09 +0200432#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100433 )
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +0100434 : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL
435#ifdef FEAT_COMPL_FUNC
Bram Moolenaarf4d8b762021-10-17 14:13:09 +0100436 && *curbuf->b_p_tsrfu == NUL && *p_tsrfu == NUL
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +0100437#endif
438 ))
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100439 {
440 ctrl_x_mode = CTRL_X_NORMAL;
441 edit_submode = NULL;
442 msg_attr(dict_opt ? _("'dictionary' option is empty")
443 : _("'thesaurus' option is empty"),
444 HL_ATTR(HLF_E));
Bram Moolenaar28ee8922020-10-28 20:20:00 +0100445 if (emsg_silent == 0 && !in_assert_fails)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100446 {
447 vim_beep(BO_COMPL);
448 setcursor();
449 out_flush();
450#ifdef FEAT_EVAL
451 if (!get_vim_var_nr(VV_TESTING))
452#endif
Bram Moolenaareda1da02019-11-17 17:06:33 +0100453 ui_delay(2004L, FALSE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100454 }
455 return FALSE;
456 }
457 return TRUE;
458}
459
460/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000461 * Is the character "c" a valid key to go to or keep us in CTRL-X mode?
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100462 * This depends on the current mode.
463 */
464 int
465vim_is_ctrl_x_key(int c)
466{
467 // Always allow ^R - let its results then be checked
glepnir05460682025-05-26 18:23:27 +0200468 if (c == Ctrl_R && ctrl_x_mode != CTRL_X_REGISTER)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100469 return TRUE;
470
471 // Accept <PageUp> and <PageDown> if the popup menu is visible.
472 if (ins_compl_pum_key(c))
473 return TRUE;
474
475 switch (ctrl_x_mode)
476 {
477 case 0: // Not in any CTRL-X mode
478 return (c == Ctrl_N || c == Ctrl_P || c == Ctrl_X);
479 case CTRL_X_NOT_DEFINED_YET:
zeertzjqdca29d92021-08-31 19:12:51 +0200480 case CTRL_X_CMDLINE_CTRL_X:
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100481 return ( c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E
482 || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB
483 || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P
484 || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V
485 || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O
zeertzjqdca29d92021-08-31 19:12:51 +0200486 || c == Ctrl_S || c == Ctrl_K || c == 's'
glepnir05460682025-05-26 18:23:27 +0200487 || c == Ctrl_Z || c == Ctrl_R);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100488 case CTRL_X_SCROLL:
489 return (c == Ctrl_Y || c == Ctrl_E);
490 case CTRL_X_WHOLE_LINE:
491 return (c == Ctrl_L || c == Ctrl_P || c == Ctrl_N);
492 case CTRL_X_FILES:
493 return (c == Ctrl_F || c == Ctrl_P || c == Ctrl_N);
494 case CTRL_X_DICTIONARY:
495 return (c == Ctrl_K || c == Ctrl_P || c == Ctrl_N);
496 case CTRL_X_THESAURUS:
497 return (c == Ctrl_T || c == Ctrl_P || c == Ctrl_N);
498 case CTRL_X_TAGS:
499 return (c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N);
500#ifdef FEAT_FIND_ID
501 case CTRL_X_PATH_PATTERNS:
502 return (c == Ctrl_P || c == Ctrl_N);
503 case CTRL_X_PATH_DEFINES:
504 return (c == Ctrl_D || c == Ctrl_P || c == Ctrl_N);
505#endif
506 case CTRL_X_CMDLINE:
507 return (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N
508 || c == Ctrl_X);
509#ifdef FEAT_COMPL_FUNC
510 case CTRL_X_FUNCTION:
511 return (c == Ctrl_U || c == Ctrl_P || c == Ctrl_N);
512 case CTRL_X_OMNI:
513 return (c == Ctrl_O || c == Ctrl_P || c == Ctrl_N);
514#endif
515 case CTRL_X_SPELL:
516 return (c == Ctrl_S || c == Ctrl_P || c == Ctrl_N);
517 case CTRL_X_EVAL:
518 return (c == Ctrl_P || c == Ctrl_N);
glepnir05460682025-05-26 18:23:27 +0200519 case CTRL_X_REGISTER:
520 return (c == Ctrl_R || c == Ctrl_P || c == Ctrl_N);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100521 }
522 internal_error("vim_is_ctrl_x_key()");
523 return FALSE;
524}
525
526/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000527 * Return TRUE if "match" is the original text when the completion began.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000528 */
529 static int
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000530match_at_original_text(compl_T *match)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000531{
532 return match->cp_flags & CP_ORIGINAL_TEXT;
533}
534
535/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000536 * Returns TRUE if "match" is the first match in the completion list.
537 */
538 static int
539is_first_match(compl_T *match)
540{
541 return match == compl_first_match;
542}
543
544/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100545 * Return TRUE when character "c" is part of the item currently being
546 * completed. Used to decide whether to abandon complete mode when the menu
547 * is visible.
548 */
549 int
550ins_compl_accept_char(int c)
551{
552 if (ctrl_x_mode & CTRL_X_WANT_IDENT)
553 // When expanding an identifier only accept identifier chars.
554 return vim_isIDc(c);
555
556 switch (ctrl_x_mode)
557 {
558 case CTRL_X_FILES:
559 // When expanding file name only accept file name chars. But not
560 // path separators, so that "proto/<Tab>" expands files in
561 // "proto", not "proto/" as a whole
562 return vim_isfilec(c) && !vim_ispathsep(c);
563
564 case CTRL_X_CMDLINE:
zeertzjqdca29d92021-08-31 19:12:51 +0200565 case CTRL_X_CMDLINE_CTRL_X:
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100566 case CTRL_X_OMNI:
567 // Command line and Omni completion can work with just about any
568 // printable character, but do stop at white space.
569 return vim_isprintc(c) && !VIM_ISWHITE(c);
570
571 case CTRL_X_WHOLE_LINE:
572 // For while line completion a space can be part of the line.
573 return vim_isprintc(c);
574 }
575 return vim_iswordc(c);
576}
577
578/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000579 * Get the completed text by inferring the case of the originally typed text.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100580 * If the result is in allocated memory "tofree" is set to it.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000581 */
582 static char_u *
583ins_compl_infercase_gettext(
glepnir19e1dd62025-05-08 22:50:38 +0200584 char_u *str,
585 int char_len,
586 int compl_char_len,
587 int min_len,
588 char_u **tofree)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000589{
590 int *wca; // Wide character array.
591 char_u *p;
592 int i, c;
593 int has_lower = FALSE;
594 int was_letter = FALSE;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100595 garray_T gap;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000596
597 IObuff[0] = NUL;
598
599 // Allocate wide character array for the completion and fill it.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100600 wca = ALLOC_MULT(int, char_len);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000601 if (wca == NULL)
602 return IObuff;
603
604 p = str;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100605 for (i = 0; i < char_len; ++i)
glepnir6e199932024-12-14 21:13:27 +0100606 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000607 if (has_mbyte)
608 wca[i] = mb_ptr2char_adv(&p);
609 else
610 wca[i] = *(p++);
glepnir6e199932024-12-14 21:13:27 +0100611 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000612
613 // Rule 1: Were any chars converted to lower?
John Marriott5e6ea922024-11-23 14:01:57 +0100614 p = compl_orig_text.string;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000615 for (i = 0; i < min_len; ++i)
616 {
glepnir6e199932024-12-14 21:13:27 +0100617 c = has_mbyte ? mb_ptr2char_adv(&p) : *(p++);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000618 if (MB_ISLOWER(c))
619 {
620 has_lower = TRUE;
621 if (MB_ISUPPER(wca[i]))
622 {
623 // Rule 1 is satisfied.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100624 for (i = compl_char_len; i < char_len; ++i)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000625 wca[i] = MB_TOLOWER(wca[i]);
626 break;
627 }
628 }
629 }
630
631 // Rule 2: No lower case, 2nd consecutive letter converted to
632 // upper case.
633 if (!has_lower)
634 {
John Marriott5e6ea922024-11-23 14:01:57 +0100635 p = compl_orig_text.string;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000636 for (i = 0; i < min_len; ++i)
637 {
glepnir6e199932024-12-14 21:13:27 +0100638 c = has_mbyte ? mb_ptr2char_adv(&p) : *(p++);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000639 if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i]))
640 {
641 // Rule 2 is satisfied.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100642 for (i = compl_char_len; i < char_len; ++i)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000643 wca[i] = MB_TOUPPER(wca[i]);
644 break;
645 }
646 was_letter = MB_ISLOWER(c) || MB_ISUPPER(c);
647 }
648 }
649
650 // Copy the original case of the part we typed.
John Marriott5e6ea922024-11-23 14:01:57 +0100651 p = compl_orig_text.string;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000652 for (i = 0; i < min_len; ++i)
653 {
glepnir6e199932024-12-14 21:13:27 +0100654 c = has_mbyte ? mb_ptr2char_adv(&p) : *(p++);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000655 if (MB_ISLOWER(c))
656 wca[i] = MB_TOLOWER(wca[i]);
657 else if (MB_ISUPPER(c))
658 wca[i] = MB_TOUPPER(wca[i]);
659 }
660
661 // Generate encoding specific output from wide character array.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000662 p = IObuff;
663 i = 0;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100664 ga_init2(&gap, 1, 500);
665 while (i < char_len)
666 {
667 if (gap.ga_data != NULL)
668 {
669 if (ga_grow(&gap, 10) == FAIL)
670 {
671 ga_clear(&gap);
672 return (char_u *)"[failed]";
673 }
674 p = (char_u *)gap.ga_data + gap.ga_len;
675 if (has_mbyte)
676 gap.ga_len += (*mb_char2bytes)(wca[i++], p);
677 else
678 {
679 *p = wca[i++];
680 ++gap.ga_len;
681 }
682 }
683 else if ((p - IObuff) + 6 >= IOSIZE)
684 {
685 // Multi-byte characters can occupy up to five bytes more than
686 // ASCII characters, and we also need one byte for NUL, so when
687 // getting to six bytes from the edge of IObuff switch to using a
688 // growarray. Add the character in the next round.
689 if (ga_grow(&gap, IOSIZE) == FAIL)
zeertzjq70e566b2024-03-21 07:11:58 +0100690 {
691 vim_free(wca);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100692 return (char_u *)"[failed]";
zeertzjq70e566b2024-03-21 07:11:58 +0100693 }
Bram Moolenaarb9e71732022-07-23 06:53:08 +0100694 *p = NUL;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100695 STRCPY(gap.ga_data, IObuff);
John Marriott5e6ea922024-11-23 14:01:57 +0100696 gap.ga_len = (int)(p - IObuff);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100697 }
698 else if (has_mbyte)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000699 p += (*mb_char2bytes)(wca[i++], p);
700 else
701 *(p++) = wca[i++];
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100702 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000703 vim_free(wca);
704
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100705 if (gap.ga_data != NULL)
706 {
707 *tofree = gap.ga_data;
708 return gap.ga_data;
709 }
710
711 *p = NUL;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000712 return IObuff;
713}
714
715/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100716 * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
717 * case of the originally typed text is used, and the case of the completed
718 * text is inferred, ie this tries to work out what case you probably wanted
719 * the rest of the word to be in -- webb
720 */
721 int
722ins_compl_add_infercase(
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200723 char_u *str_arg,
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100724 int len,
725 int icase,
726 char_u *fname,
727 int dir,
glepnirf31cfa22025-03-06 21:59:13 +0100728 int cont_s_ipos, // next ^X<> will set initial_pos
729 int score)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100730{
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200731 char_u *str = str_arg;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100732 char_u *p;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100733 int char_len; // count multi-byte characters
734 int compl_char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100735 int min_len;
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200736 int flags = 0;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100737 int res;
738 char_u *tofree = NULL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100739
740 if (p_ic && curbuf->b_p_inf && len > 0)
741 {
742 // Infer case of completed part.
743
744 // Find actual length of completion.
745 if (has_mbyte)
746 {
747 p = str;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100748 char_len = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100749 while (*p != NUL)
750 {
751 MB_PTR_ADV(p);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100752 ++char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100753 }
754 }
755 else
glepnir6e199932024-12-14 21:13:27 +0100756 {
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100757 char_len = len;
glepnir6e199932024-12-14 21:13:27 +0100758 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100759
760 // Find actual length of original text.
761 if (has_mbyte)
762 {
John Marriott5e6ea922024-11-23 14:01:57 +0100763 p = compl_orig_text.string;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100764 compl_char_len = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100765 while (*p != NUL)
766 {
767 MB_PTR_ADV(p);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100768 ++compl_char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100769 }
770 }
771 else
glepnir6e199932024-12-14 21:13:27 +0100772 {
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100773 compl_char_len = compl_length;
glepnir6e199932024-12-14 21:13:27 +0100774 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100775
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100776 // "char_len" may be smaller than "compl_char_len" when using
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100777 // thesaurus, only use the minimum when comparing.
glepnir6e199932024-12-14 21:13:27 +0100778 min_len = MIN(char_len, compl_char_len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100779
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100780 str = ins_compl_infercase_gettext(str, char_len,
781 compl_char_len, min_len, &tofree);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100782 }
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200783 if (cont_s_ipos)
784 flags |= CP_CONT_S_IPOS;
785 if (icase)
786 flags |= CP_ICASE;
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200787
glepnirf31cfa22025-03-06 21:59:13 +0100788 res = ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE, NULL, score);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100789 vim_free(tofree);
790 return res;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100791}
792
793/*
glepnirf31cfa22025-03-06 21:59:13 +0100794 * Check if ctrl_x_mode has been configured in 'completefuzzycollect'
795 */
796 static int
797cfc_has_mode(void)
798{
glepnir58760162025-03-13 21:39:51 +0100799 if (ctrl_x_mode_normal() || ctrl_x_mode_dictionary())
800 return (cfc_flags & CFC_KEYWORD) != 0;
801 else if (ctrl_x_mode_files())
802 return (cfc_flags & CFC_FILES) != 0;
803 else if (ctrl_x_mode_whole_line())
804 return (cfc_flags & CFC_WHOLELINE) != 0;
805 else
806 return FALSE;
glepnirf31cfa22025-03-06 21:59:13 +0100807}
808
809/*
Girish Palyab1565882025-04-15 20:16:00 +0200810 * Returns TRUE if matches should be sorted based on proximity to the cursor.
811 */
812 static int
813is_nearest_active(void)
814{
glepnirc3fbaa02025-05-08 23:05:10 +0200815 return (get_cot_flags() & (COT_NEAREST | COT_FUZZY)) == COT_NEAREST;
Girish Palyab1565882025-04-15 20:16:00 +0200816}
817
818/*
819 * Repositions a match in the completion list based on its proximity score.
820 * If the match is at the head and has a higher score than the next node,
821 * or if it's in the middle/tail and has a lower score than the previous node,
822 * it is moved to the correct position while maintaining ascending order.
823 */
824 static void
825reposition_match(compl_T *match)
826{
827 compl_T *insert_before = NULL;
828 compl_T *insert_after = NULL;
829
830 // Node is at head and score is too big
831 if (!match->cp_prev)
832 {
833 if (match->cp_next && match->cp_next->cp_score > 0 &&
834 match->cp_next->cp_score < match->cp_score)
835 {
836 // <c-p>: compl_first_match is at head and newly inserted node
837 compl_first_match = compl_curr_match = match->cp_next;
838 // Find the correct position in ascending order
839 insert_before = match->cp_next;
840 do
841 {
842 insert_after = insert_before;
843 insert_before = insert_before->cp_next;
844 } while (insert_before && insert_before->cp_score > 0 &&
845 insert_before->cp_score < match->cp_score);
846 }
847 else
848 return;
849 }
850 // Node is at tail or in the middle but score is too small
851 else
852 {
853 if (match->cp_prev->cp_score > 0 && match->cp_prev->cp_score > match->cp_score)
854 {
855 // <c-n>: compl_curr_match (and newly inserted match) is at tail
856 if (!match->cp_next)
857 compl_curr_match = compl_curr_match->cp_prev;
858 // Find the correct position in ascending order
859 insert_after = match->cp_prev;
860 do
861 {
862 insert_before = insert_after;
863 insert_after = insert_after->cp_prev;
864 } while (insert_after && insert_after->cp_score > 0 &&
865 insert_after->cp_score > match->cp_score);
866 }
867 else
868 return;
869 }
870
871 if (insert_after)
872 {
873 // Remove the match from its current position
874 if (match->cp_prev)
875 match->cp_prev->cp_next = match->cp_next;
876 else
877 compl_first_match = match->cp_next;
878 if (match->cp_next)
879 match->cp_next->cp_prev = match->cp_prev;
880
881 // Insert the match at the correct position
882 match->cp_next = insert_before;
883 match->cp_prev = insert_after;
884 insert_after->cp_next = match;
885 insert_before->cp_prev = match;
886 }
887}
888
889/*
Yegappan Lakshmanan37079142022-01-08 10:38:48 +0000890 * Add a match to the list of matches. The arguments are:
891 * str - text of the match to add
892 * len - length of "str". If -1, then the length of "str" is
893 * computed.
894 * fname - file name to associate with this match.
895 * cptext - list of strings to use with this match (for abbr, menu, info
896 * and kind)
897 * user_data - user supplied data (any vim type) for this match
898 * cdir - match direction. If 0, use "compl_direction".
899 * flags_arg - match flags (cp_flags)
900 * adup - accept this match even if it is already present.
glepnir80b66202024-11-27 21:53:53 +0100901 * *user_hl - list of extra highlight attributes for abbr kind.
Yegappan Lakshmanan37079142022-01-08 10:38:48 +0000902 * If "cdir" is FORWARD, then the match is added after the current match.
903 * Otherwise, it is added before the current match.
904 *
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100905 * If the given string is already in the list of completions, then return
906 * NOTDONE, otherwise add it to the list and return OK. If there is an error,
907 * maybe because alloc() returns NULL, then FAIL is returned.
908 */
909 static int
910ins_compl_add(
911 char_u *str,
912 int len,
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100913 char_u *fname,
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100914 char_u **cptext, // extra text for popup menu or NULL
915 typval_T *user_data UNUSED, // "user_data" entry or NULL
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100916 int cdir,
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200917 int flags_arg,
glepnir508e7852024-07-25 21:39:08 +0200918 int adup, // accept duplicate match
glepnirf31cfa22025-03-06 21:59:13 +0100919 int *user_hl, // user abbr/kind hlattr
920 int score)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100921{
glepnirf31cfa22025-03-06 21:59:13 +0100922 compl_T *match, *current, *prev;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100923 int dir = (cdir == 0 ? compl_direction : cdir);
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200924 int flags = flags_arg;
glepnirf31cfa22025-03-06 21:59:13 +0100925 int inserted = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100926
Bram Moolenaarceb06192021-04-04 15:05:22 +0200927 if (flags & CP_FAST)
928 fast_breakcheck();
929 else
930 ui_breakcheck();
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100931 if (got_int)
932 return FAIL;
933 if (len < 0)
934 len = (int)STRLEN(str);
935
936 // If the same match is already present, don't add it.
937 if (compl_first_match != NULL && !adup)
938 {
939 match = compl_first_match;
940 do
941 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000942 if (!match_at_original_text(match)
John Marriott5e6ea922024-11-23 14:01:57 +0100943 && STRNCMP(match->cp_str.string, str, len) == 0
944 && ((int)match->cp_str.length <= len
945 || match->cp_str.string[len] == NUL))
Girish Palyab1565882025-04-15 20:16:00 +0200946 {
947 if (is_nearest_active() && score > 0 && score < match->cp_score)
948 {
949 match->cp_score = score;
950 reposition_match(match);
951 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100952 return NOTDONE;
Girish Palyab1565882025-04-15 20:16:00 +0200953 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100954 match = match->cp_next;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000955 } while (match != NULL && !is_first_match(match));
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100956 }
957
958 // Remove any popup menu before changing the list of matches.
959 ins_compl_del_pum();
960
961 // Allocate a new match structure.
962 // Copy the values to the new match structure.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200963 match = ALLOC_CLEAR_ONE(compl_T);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100964 if (match == NULL)
965 return FAIL;
glepnir40891ba2025-02-10 22:18:00 +0100966 match->cp_number = flags & CP_ORIGINAL_TEXT ? 0 : -1;
John Marriott5e6ea922024-11-23 14:01:57 +0100967 if ((match->cp_str.string = vim_strnsave(str, len)) == NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100968 {
969 vim_free(match);
970 return FAIL;
971 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100972
John Marriott5e6ea922024-11-23 14:01:57 +0100973 match->cp_str.length = len;
974
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100975 // match-fname is:
976 // - compl_curr_match->cp_fname if it is a string equal to fname.
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200977 // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100978 // - NULL otherwise. --Acevedo
979 if (fname != NULL
980 && compl_curr_match != NULL
981 && compl_curr_match->cp_fname != NULL
982 && STRCMP(fname, compl_curr_match->cp_fname) == 0)
983 match->cp_fname = compl_curr_match->cp_fname;
984 else if (fname != NULL)
985 {
986 match->cp_fname = vim_strsave(fname);
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200987 flags |= CP_FREE_FNAME;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100988 }
989 else
990 match->cp_fname = NULL;
991 match->cp_flags = flags;
glepnir80b66202024-11-27 21:53:53 +0100992 match->cp_user_abbr_hlattr = user_hl ? user_hl[0] : -1;
993 match->cp_user_kind_hlattr = user_hl ? user_hl[1] : -1;
glepnirf31cfa22025-03-06 21:59:13 +0100994 match->cp_score = score;
Girish Palya0ac1eb32025-04-16 20:18:33 +0200995 match->cp_cpt_source_idx = cpt_sources_index;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100996
997 if (cptext != NULL)
998 {
glepnir19e1dd62025-05-08 22:50:38 +0200999 for (int i = 0; i < CPT_COUNT; ++i)
glepnir6e199932024-12-14 21:13:27 +01001000 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001001 if (cptext[i] != NULL && *cptext[i] != NUL)
1002 match->cp_text[i] = vim_strsave(cptext[i]);
glepnir6e199932024-12-14 21:13:27 +01001003 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001004 }
Bram Moolenaarab782c52020-01-04 19:00:11 +01001005#ifdef FEAT_EVAL
Bram Moolenaar08928322020-01-04 14:32:48 +01001006 if (user_data != NULL)
1007 match->cp_user_data = *user_data;
Bram Moolenaarab782c52020-01-04 19:00:11 +01001008#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001009
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00001010 // Link the new match structure after (FORWARD) or before (BACKWARD) the
1011 // current match in the list of matches .
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001012 if (compl_first_match == NULL)
1013 match->cp_next = match->cp_prev = NULL;
glepnirf31cfa22025-03-06 21:59:13 +01001014 else if (cfc_has_mode() && score > 0 && compl_get_longest)
1015 {
1016 current = compl_first_match->cp_next;
1017 prev = compl_first_match;
1018 inserted = FALSE;
1019 // The direction is ignored when using longest and
1020 // completefuzzycollect, because matches are inserted
1021 // and sorted by score.
1022 while (current != NULL && current != compl_first_match)
1023 {
1024 if (current->cp_score < score)
1025 {
Hirohito Higashi355db992025-04-28 18:07:02 +02001026 match->cp_next = current;
1027 match->cp_prev = current->cp_prev;
1028 if (current->cp_prev)
glepnirf31cfa22025-03-06 21:59:13 +01001029 current->cp_prev->cp_next = match;
Hirohito Higashi355db992025-04-28 18:07:02 +02001030 current->cp_prev = match;
1031 inserted = TRUE;
1032 break;
glepnirf31cfa22025-03-06 21:59:13 +01001033 }
1034 prev = current;
1035 current = current->cp_next;
1036 }
1037 if (!inserted)
1038 {
1039 prev->cp_next = match;
1040 match->cp_prev = prev;
1041 match->cp_next = compl_first_match;
1042 compl_first_match->cp_prev = match;
1043 }
1044 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001045 else if (dir == FORWARD)
1046 {
1047 match->cp_next = compl_curr_match->cp_next;
1048 match->cp_prev = compl_curr_match;
1049 }
1050 else // BACKWARD
1051 {
1052 match->cp_next = compl_curr_match;
1053 match->cp_prev = compl_curr_match->cp_prev;
1054 }
1055 if (match->cp_next)
1056 match->cp_next->cp_prev = match;
1057 if (match->cp_prev)
1058 match->cp_prev->cp_next = match;
1059 else // if there's nothing before, it is the first match
1060 compl_first_match = match;
1061 compl_curr_match = match;
1062
Girish Palyab1565882025-04-15 20:16:00 +02001063 if (is_nearest_active() && score > 0)
1064 reposition_match(match);
1065
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001066 // Find the longest common string if still doing that.
glepnirf31cfa22025-03-06 21:59:13 +01001067 if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0 && !cfc_has_mode())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001068 ins_compl_longest_match(match);
1069
1070 return OK;
1071}
1072
1073/*
1074 * Return TRUE if "str[len]" matches with match->cp_str, considering
Bram Moolenaard9eefe32019-04-06 14:22:21 +02001075 * match->cp_flags.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001076 */
1077 static int
1078ins_compl_equal(compl_T *match, char_u *str, int len)
1079{
Bram Moolenaard9eefe32019-04-06 14:22:21 +02001080 if (match->cp_flags & CP_EQUAL)
Bram Moolenaar73655cf2019-04-06 13:45:55 +02001081 return TRUE;
Bram Moolenaard9eefe32019-04-06 14:22:21 +02001082 if (match->cp_flags & CP_ICASE)
John Marriott5e6ea922024-11-23 14:01:57 +01001083 return STRNICMP(match->cp_str.string, str, (size_t)len) == 0;
1084 return STRNCMP(match->cp_str.string, str, (size_t)len) == 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001085}
1086
1087/*
glepnir6a38aff2024-12-16 21:56:16 +01001088 * when len is -1 mean use whole length of p otherwise part of p
1089 */
1090 static void
1091ins_compl_insert_bytes(char_u *p, int len)
1092{
1093 if (len == -1)
1094 len = (int)STRLEN(p);
1095 ins_bytes_len(p, len);
zeertzjqf25d8f92024-12-18 21:12:25 +01001096 compl_ins_end_col = curwin->w_cursor.col;
glepnir6a38aff2024-12-16 21:56:16 +01001097}
1098
1099/*
zeertzjqd32bf0a2024-12-17 20:55:13 +01001100 * Checks if the column is within the currently inserted completion text
1101 * column range. If it is, it returns a special highlight attribute.
glepnir76bdb822025-02-08 19:04:51 +01001102 * -1 means normal item.
glepnir6a38aff2024-12-16 21:56:16 +01001103 */
1104 int
glepnir76bdb822025-02-08 19:04:51 +01001105ins_compl_col_range_attr(linenr_T lnum, int col)
glepnir6a38aff2024-12-16 21:56:16 +01001106{
glepnir76bdb822025-02-08 19:04:51 +01001107 int start_col;
1108 int attr;
1109
1110 if ((get_cot_flags() & COT_FUZZY)
1111 || (attr = syn_name2attr((char_u *)"ComplMatchIns")) == 0)
glepnire8908872025-01-08 18:30:45 +01001112 return -1;
1113
glepnir76bdb822025-02-08 19:04:51 +01001114 start_col = compl_col + (int)ins_compl_leader_len();
1115 if (!ins_compl_has_multiple())
1116 return (col >= start_col && col < compl_ins_end_col) ? attr : -1;
1117
1118 // Multiple lines
1119 if ((lnum == compl_lnum && col >= start_col && col < MAXCOL) ||
1120 (lnum > compl_lnum && lnum < curwin->w_cursor.lnum) ||
1121 (lnum == curwin->w_cursor.lnum && col <= compl_ins_end_col))
1122 return attr;
glepnir6a38aff2024-12-16 21:56:16 +01001123
1124 return -1;
1125}
1126
1127/*
glepnir76bdb822025-02-08 19:04:51 +01001128 * Returns TRUE if the current completion string contains newline characters,
1129 * indicating it's a multi-line completion.
1130 */
1131 static int
1132ins_compl_has_multiple(void)
1133{
1134 return vim_strchr(compl_shown_match->cp_str.string, '\n') != NULL;
1135}
1136
1137/*
1138 * Returns TRUE if the given line number falls within the range of a multi-line
1139 * completion, i.e. between the starting line (compl_lnum) and current cursor
1140 * line. Always returns FALSE for single-line completions.
1141 */
1142 int
1143ins_compl_lnum_in_range(linenr_T lnum)
1144{
1145 if (!ins_compl_has_multiple())
1146 return FALSE;
1147 return lnum >= compl_lnum && lnum <= curwin->w_cursor.lnum;
1148}
1149
1150/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001151 * Reduce the longest common string for match "match".
1152 */
1153 static void
1154ins_compl_longest_match(compl_T *match)
1155{
1156 char_u *p, *s;
1157 int c1, c2;
1158 int had_match;
1159
John Marriott5e6ea922024-11-23 14:01:57 +01001160 if (compl_leader.string == NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001161 {
1162 // First match, use it as a whole.
John Marriott5e6ea922024-11-23 14:01:57 +01001163 compl_leader.string = vim_strnsave(match->cp_str.string, match->cp_str.length);
1164 if (compl_leader.string == NULL)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001165 return;
1166
John Marriott5e6ea922024-11-23 14:01:57 +01001167 compl_leader.length = match->cp_str.length;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001168 had_match = (curwin->w_cursor.col > compl_col);
glepnirf31cfa22025-03-06 21:59:13 +01001169 ins_compl_longest_insert(compl_leader.string);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001170
1171 // When the match isn't there (to avoid matching itself) remove it
1172 // again after redrawing.
1173 if (!had_match)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001174 ins_compl_delete();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001175 compl_used_match = FALSE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001176
1177 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001178 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001179
1180 // Reduce the text if this match differs from compl_leader.
John Marriott5e6ea922024-11-23 14:01:57 +01001181 p = compl_leader.string;
1182 s = match->cp_str.string;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001183 while (*p != NUL)
1184 {
1185 if (has_mbyte)
1186 {
1187 c1 = mb_ptr2char(p);
1188 c2 = mb_ptr2char(s);
1189 }
1190 else
1191 {
1192 c1 = *p;
1193 c2 = *s;
1194 }
1195 if ((match->cp_flags & CP_ICASE)
1196 ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) : (c1 != c2))
1197 break;
1198 if (has_mbyte)
1199 {
1200 MB_PTR_ADV(p);
1201 MB_PTR_ADV(s);
1202 }
1203 else
1204 {
1205 ++p;
1206 ++s;
1207 }
1208 }
1209
1210 if (*p != NUL)
1211 {
1212 // Leader was shortened, need to change the inserted text.
1213 *p = NUL;
John Marriott5e6ea922024-11-23 14:01:57 +01001214 compl_leader.length = (size_t)(p - compl_leader.string);
1215
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001216 had_match = (curwin->w_cursor.col > compl_col);
glepnirf31cfa22025-03-06 21:59:13 +01001217 ins_compl_longest_insert(compl_leader.string);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001218
1219 // When the match isn't there (to avoid matching itself) remove it
1220 // again after redrawing.
1221 if (!had_match)
1222 ins_compl_delete();
1223 }
1224
1225 compl_used_match = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001226}
1227
1228/*
1229 * Add an array of matches to the list of matches.
1230 * Frees matches[].
1231 */
1232 static void
1233ins_compl_add_matches(
1234 int num_matches,
1235 char_u **matches,
1236 int icase)
1237{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001238 int add_r = OK;
1239 int dir = compl_direction;
1240
glepnir19e1dd62025-05-08 22:50:38 +02001241 for (int i = 0; i < num_matches && add_r != FAIL; i++)
glepnir6e199932024-12-14 21:13:27 +01001242 {
1243 add_r = ins_compl_add(matches[i], -1, NULL, NULL, NULL, dir,
glepnirf31cfa22025-03-06 21:59:13 +01001244 CP_FAST | (icase ? CP_ICASE : 0), FALSE, NULL, 0);
glepnir6e199932024-12-14 21:13:27 +01001245 if (add_r == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001246 // if dir was BACKWARD then honor it just once
1247 dir = FORWARD;
glepnir6e199932024-12-14 21:13:27 +01001248 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001249 FreeWild(num_matches, matches);
1250}
1251
1252/*
1253 * Make the completion list cyclic.
1254 * Return the number of matches (excluding the original).
1255 */
1256 static int
1257ins_compl_make_cyclic(void)
1258{
1259 compl_T *match;
1260 int count = 0;
1261
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001262 if (compl_first_match == NULL)
1263 return 0;
1264
1265 // Find the end of the list.
1266 match = compl_first_match;
1267 // there's always an entry for the compl_orig_text, it doesn't count.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001268 while (match->cp_next != NULL && !is_first_match(match->cp_next))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001269 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001270 match = match->cp_next;
1271 ++count;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001272 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001273 match->cp_next = compl_first_match;
1274 compl_first_match->cp_prev = match;
1275
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001276 return count;
1277}
1278
1279/*
1280 * Return whether there currently is a shown match.
1281 */
1282 int
1283ins_compl_has_shown_match(void)
1284{
1285 return compl_shown_match == NULL
1286 || compl_shown_match != compl_shown_match->cp_next;
1287}
1288
1289/*
1290 * Return whether the shown match is long enough.
1291 */
1292 int
1293ins_compl_long_shown_match(void)
1294{
John Marriott5e6ea922024-11-23 14:01:57 +01001295 return (int)compl_shown_match->cp_str.length
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001296 > curwin->w_cursor.col - compl_col;
1297}
1298
1299/*
zeertzjq529b9ad2024-06-05 20:27:06 +02001300 * Get the local or global value of 'completeopt' flags.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001301 */
zeertzjq529b9ad2024-06-05 20:27:06 +02001302 unsigned int
1303get_cot_flags(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001304{
zeertzjq529b9ad2024-06-05 20:27:06 +02001305 return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001306}
1307
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001308/*
1309 * Update the screen and when there is any scrolling remove the popup menu.
1310 */
1311 static void
1312ins_compl_upd_pum(void)
1313{
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001314 if (compl_match_array == NULL)
1315 return;
1316
glepnir19e1dd62025-05-08 22:50:38 +02001317 int h = curwin->w_cline_height;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001318 // Update the screen later, before drawing the popup menu over it.
1319 pum_call_update_screen();
1320 if (h != curwin->w_cline_height)
1321 ins_compl_del_pum();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001322}
1323
1324/*
1325 * Remove any popup menu.
1326 */
1327 static void
1328ins_compl_del_pum(void)
1329{
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001330 if (compl_match_array == NULL)
1331 return;
1332
1333 pum_undisplay();
1334 VIM_CLEAR(compl_match_array);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001335}
1336
1337/*
1338 * Return TRUE if the popup menu should be displayed.
1339 */
1340 int
1341pum_wanted(void)
1342{
1343 // 'completeopt' must contain "menu" or "menuone"
zeertzjq529b9ad2024-06-05 20:27:06 +02001344 if ((get_cot_flags() & COT_ANY_MENU) == 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001345 return FALSE;
1346
1347 // The display looks bad on a B&W display.
1348 if (t_colors < 8
1349#ifdef FEAT_GUI
1350 && !gui.in_use
1351#endif
1352 )
1353 return FALSE;
1354 return TRUE;
1355}
1356
1357/*
1358 * Return TRUE if there are two or more matches to be shown in the popup menu.
1359 * One if 'completopt' contains "menuone".
1360 */
1361 static int
1362pum_enough_matches(void)
1363{
1364 compl_T *compl;
glepnir6e199932024-12-14 21:13:27 +01001365 int i = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001366
1367 // Don't display the popup menu if there are no matches or there is only
1368 // one (ignoring the original text).
1369 compl = compl_first_match;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001370 do
1371 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001372 if (compl == NULL || (!match_at_original_text(compl) && ++i == 2))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001373 break;
1374 compl = compl->cp_next;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001375 } while (!is_first_match(compl));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001376
zeertzjq529b9ad2024-06-05 20:27:06 +02001377 if (get_cot_flags() & COT_MENUONE)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001378 return (i >= 1);
1379 return (i >= 2);
1380}
1381
Bram Moolenaar3075a452021-11-17 15:51:52 +00001382#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001383/*
1384 * Allocate Dict for the completed item.
1385 * { word, abbr, menu, kind, info }
1386 */
1387 static dict_T *
1388ins_compl_dict_alloc(compl_T *match)
1389{
1390 dict_T *dict = dict_alloc_lock(VAR_FIXED);
1391
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001392 if (dict == NULL)
1393 return NULL;
1394
John Marriott5e6ea922024-11-23 14:01:57 +01001395 dict_add_string(dict, "word", match->cp_str.string);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001396 dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]);
1397 dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
1398 dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
1399 dict_add_string(dict, "info", match->cp_text[CPT_INFO]);
1400 if (match->cp_user_data.v_type == VAR_UNKNOWN)
1401 dict_add_string(dict, "user_data", (char_u *)"");
1402 else
1403 dict_add_tv(dict, "user_data", &match->cp_user_data);
1404
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001405 return dict;
1406}
1407
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001408/*
1409 * Trigger the CompleteChanged autocmd event. Invoked each time the Insert mode
1410 * completion menu is changed.
1411 */
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001412 static void
1413trigger_complete_changed_event(int cur)
1414{
1415 dict_T *v_event;
1416 dict_T *item;
1417 static int recursive = FALSE;
Bram Moolenaar3075a452021-11-17 15:51:52 +00001418 save_v_event_T save_v_event;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001419
1420 if (recursive)
1421 return;
1422
glepnir40891ba2025-02-10 22:18:00 +01001423 item = cur < 0 ? dict_alloc() : ins_compl_dict_alloc(compl_curr_match);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001424 if (item == NULL)
1425 return;
Bram Moolenaar3075a452021-11-17 15:51:52 +00001426 v_event = get_v_event(&save_v_event);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001427 dict_add_dict(v_event, "completed_item", item);
1428 pum_set_event_info(v_event);
1429 dict_set_items_ro(v_event);
1430
1431 recursive = TRUE;
zeertzjqcfe45652022-05-27 17:26:55 +01001432 textlock++;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001433 apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, FALSE, curbuf);
zeertzjqcfe45652022-05-27 17:26:55 +01001434 textlock--;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001435 recursive = FALSE;
1436
Bram Moolenaar3075a452021-11-17 15:51:52 +00001437 restore_v_event(v_event, &save_v_event);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001438}
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001439#endif
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001440
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001441/*
Girish Palya19ef6b02025-05-28 20:28:21 +02001442 * Trim compl_match_array to enforce max_matches per completion source.
1443 *
1444 * Note: This special-case trimming is a workaround because compl_match_array
1445 * becomes inconsistent with compl_first_match (list) after former is sorted by
1446 * fuzzy score. The two structures end up in different orders.
1447 * Ideally, compl_first_match list should have been sorted instead.
1448 */
1449 static void
1450trim_compl_match_array(void)
1451{
1452 int i, src_idx, limit, new_size = 0, *match_counts = NULL;
1453 pumitem_T *trimmed = NULL;
1454 int trimmed_idx = 0;
1455
1456 // Count current matches per source.
1457 match_counts = ALLOC_CLEAR_MULT(int, cpt_sources_count);
1458 if (match_counts == NULL)
1459 return;
1460 for (i = 0; i < compl_match_arraysize; i++)
1461 {
1462 src_idx = compl_match_array[i].pum_cpt_source_idx;
1463 if (src_idx != -1)
1464 match_counts[src_idx]++;
1465 }
1466
1467 // Calculate size of trimmed array, respecting max_matches per source.
1468 for (i = 0; i < cpt_sources_count; i++)
1469 {
1470 limit = cpt_sources_array[i].max_matches;
1471 new_size += (limit > 0 && match_counts[i] > limit)
1472 ? limit : match_counts[i];
1473 }
1474
1475 if (new_size == compl_match_arraysize)
1476 goto theend;
1477
1478 // Create trimmed array while enforcing per-source limits
1479 trimmed = ALLOC_CLEAR_MULT(pumitem_T, new_size);
1480 if (trimmed == NULL)
1481 goto theend;
1482 vim_memset(match_counts, 0, sizeof(int) * cpt_sources_count);
1483 for (i = 0; i < compl_match_arraysize; i++)
1484 {
1485 src_idx = compl_match_array[i].pum_cpt_source_idx;
1486 if (src_idx != -1)
1487 {
1488 limit = cpt_sources_array[src_idx].max_matches;
1489 if (limit <= 0 || match_counts[src_idx] < limit)
1490 {
1491 trimmed[trimmed_idx++] = compl_match_array[i];
1492 match_counts[src_idx]++;
1493 }
1494 }
1495 else
1496 trimmed[trimmed_idx++] = compl_match_array[i];
1497 }
1498 vim_free(compl_match_array);
1499 compl_match_array = trimmed;
1500 compl_match_arraysize = new_size;
1501
1502theend:
1503 vim_free(match_counts);
1504}
1505
1506/*
glepnira218cc62024-06-03 19:32:39 +02001507 * pumitem qsort compare func
1508 */
1509 static int
zeertzjq8e567472024-06-14 20:04:42 +02001510ins_compl_fuzzy_cmp(const void *a, const void *b)
glepnira218cc62024-06-03 19:32:39 +02001511{
1512 const int sa = (*(pumitem_T *)a).pum_score;
1513 const int sb = (*(pumitem_T *)b).pum_score;
zeertzjq8e567472024-06-14 20:04:42 +02001514 const int ia = (*(pumitem_T *)a).pum_idx;
1515 const int ib = (*(pumitem_T *)b).pum_idx;
1516 return sa == sb ? (ia == ib ? 0 : (ia < ib ? -1 : 1)) : (sa < sb ? 1 : -1);
glepnira218cc62024-06-03 19:32:39 +02001517}
1518
1519/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001520 * Build a popup menu to show the completion matches.
1521 * Returns the popup menu entry that should be selected. Returns -1 if nothing
1522 * should be selected.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001523 */
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001524 static int
1525ins_compl_build_pum(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001526{
1527 compl_T *compl;
1528 compl_T *shown_compl = NULL;
1529 int did_find_shown_match = FALSE;
1530 int shown_match_ok = FALSE;
glepnira49c0772024-11-30 10:56:30 +01001531 int i = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001532 int cur = -1;
glepnira218cc62024-06-03 19:32:39 +02001533 int max_fuzzy_score = 0;
zeertzjqaa925ee2024-06-09 18:24:05 +02001534 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02001535 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
zeertzjqd65aa1b2025-01-25 15:29:03 +01001536 int fuzzy_filter = (cur_cot_flags & COT_FUZZY) != 0;
1537 int fuzzy_sort = fuzzy_filter && !(cur_cot_flags & COT_NOSORT);
glepnir80b66202024-11-27 21:53:53 +01001538 compl_T *match_head = NULL;
1539 compl_T *match_tail = NULL;
1540 compl_T *match_next = NULL;
glepnire4e4d1c2025-04-02 20:18:25 +02001541 int update_shown_match = fuzzy_filter;
Christian Brabandtb53d4fb2025-04-16 21:12:30 +02001542 int match_count = 0;
Girish Palya0ac1eb32025-04-16 20:18:33 +02001543 int cur_source = -1;
1544 int max_matches_found = FALSE;
Girish Palya19ef6b02025-05-28 20:28:21 +02001545 int is_forward = compl_shows_dir_forward();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001546
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001547 // Need to build the popup menu list.
1548 compl_match_arraysize = 0;
1549 compl = compl_first_match;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001550
glepnira49c0772024-11-30 10:56:30 +01001551 // If the current match is the original text don't find the first
1552 // match after it, don't highlight anything.
1553 if (match_at_original_text(compl_shown_match))
1554 shown_match_ok = TRUE;
1555
glepnire4e4d1c2025-04-02 20:18:25 +02001556 if (fuzzy_filter && ctrl_x_mode_normal() && compl_leader.string == NULL
1557 && compl_shown_match->cp_score > 0)
1558 update_shown_match = FALSE;
1559
glepnira49c0772024-11-30 10:56:30 +01001560 if (compl_leader.string != NULL
1561 && STRCMP(compl_leader.string, compl_orig_text.string) == 0
1562 && shown_match_ok == FALSE)
1563 compl_shown_match = compl_no_select ? compl_first_match
1564 : compl_first_match->cp_next;
1565
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001566 do
1567 {
glepnird4088ed2024-12-31 10:55:22 +01001568 compl->cp_in_match_array = FALSE;
zeertzjq551d8c32024-06-05 19:53:32 +02001569 // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
1570 // set the cp_score for later comparisons.
glepnirf400a0c2025-01-23 19:55:14 +01001571 if (fuzzy_filter && compl_leader.string != NULL && compl_leader.length > 0)
John Marriott5e6ea922024-11-23 14:01:57 +01001572 compl->cp_score = fuzzy_match_str(compl->cp_str.string, compl_leader.string);
glepnira218cc62024-06-03 19:32:39 +02001573
Girish Palya19ef6b02025-05-28 20:28:21 +02001574 if (is_forward && !fuzzy_sort && compl->cp_cpt_source_idx != -1)
Girish Palya0ac1eb32025-04-16 20:18:33 +02001575 {
1576 if (cur_source != compl->cp_cpt_source_idx)
1577 {
1578 cur_source = compl->cp_cpt_source_idx;
1579 match_count = 1;
1580 max_matches_found = FALSE;
1581 }
Girish Palyadc314052025-05-08 23:28:52 +02001582 else if (cpt_sources_array != NULL && !max_matches_found)
Girish Palya0ac1eb32025-04-16 20:18:33 +02001583 {
1584 int max_matches = cpt_sources_array[cur_source].max_matches;
1585 if (max_matches > 0 && match_count > max_matches)
1586 max_matches_found = TRUE;
1587 }
1588 }
1589
Girish Palyadc314052025-05-08 23:28:52 +02001590 // Apply 'smartcase' behavior during normal mode
1591 if (ctrl_x_mode_normal() && !p_inf && compl_leader.string
1592 && !ignorecase(compl_leader.string) && !fuzzy_filter)
1593 compl->cp_flags &= ~CP_ICASE;
1594
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001595 if (!match_at_original_text(compl)
Girish Palya0ac1eb32025-04-16 20:18:33 +02001596 && !max_matches_found
John Marriott5e6ea922024-11-23 14:01:57 +01001597 && (compl_leader.string == NULL
Girish Palyadc314052025-05-08 23:28:52 +02001598 || ins_compl_equal(compl, compl_leader.string,
1599 (int)compl_leader.length)
glepnirf400a0c2025-01-23 19:55:14 +01001600 || (fuzzy_filter && compl->cp_score > 0)))
glepnir80b66202024-11-27 21:53:53 +01001601 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001602 ++compl_match_arraysize;
glepnird4088ed2024-12-31 10:55:22 +01001603 compl->cp_in_match_array = TRUE;
glepnir80b66202024-11-27 21:53:53 +01001604 if (match_head == NULL)
1605 match_head = compl;
1606 else
glepnira49c0772024-11-30 10:56:30 +01001607 match_tail->cp_match_next = compl;
glepnir80b66202024-11-27 21:53:53 +01001608 match_tail = compl;
glepnira49c0772024-11-30 10:56:30 +01001609
glepnirf400a0c2025-01-23 19:55:14 +01001610 if (!shown_match_ok && !fuzzy_filter)
glepnira49c0772024-11-30 10:56:30 +01001611 {
1612 if (compl == compl_shown_match || did_find_shown_match)
1613 {
1614 // This item is the shown match or this is the
1615 // first displayed item after the shown match.
1616 compl_shown_match = compl;
1617 did_find_shown_match = TRUE;
1618 shown_match_ok = TRUE;
1619 }
1620 else
1621 // Remember this displayed match for when the
1622 // shown match is just below it.
1623 shown_compl = compl;
1624 cur = i;
1625 }
glepnirf400a0c2025-01-23 19:55:14 +01001626 else if (fuzzy_filter)
glepnira49c0772024-11-30 10:56:30 +01001627 {
1628 if (i == 0)
1629 shown_compl = compl;
1630 // Update the maximum fuzzy score and the shown match
1631 // if the current item's score is higher
glepnire4e4d1c2025-04-02 20:18:25 +02001632 if (fuzzy_sort && compl->cp_score > max_fuzzy_score
1633 && update_shown_match)
glepnira49c0772024-11-30 10:56:30 +01001634 {
1635 did_find_shown_match = TRUE;
1636 max_fuzzy_score = compl->cp_score;
1637 if (!compl_no_select)
1638 compl_shown_match = compl;
1639 }
1640
glepnir3af0a8d2025-02-20 22:06:16 +01001641 if (!shown_match_ok && compl == compl_shown_match)
glepnira49c0772024-11-30 10:56:30 +01001642 {
1643 cur = i;
1644 shown_match_ok = TRUE;
1645 }
1646 }
Girish Palya19ef6b02025-05-28 20:28:21 +02001647 if (is_forward && !fuzzy_sort && compl->cp_cpt_source_idx != -1)
Girish Palya0ac1eb32025-04-16 20:18:33 +02001648 match_count++;
glepnira49c0772024-11-30 10:56:30 +01001649 i++;
glepnir80b66202024-11-27 21:53:53 +01001650 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001651
glepnirf400a0c2025-01-23 19:55:14 +01001652 if (compl == compl_shown_match && !fuzzy_filter)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001653 {
1654 did_find_shown_match = TRUE;
1655
1656 // When the original text is the shown match don't set
1657 // compl_shown_match.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001658 if (match_at_original_text(compl))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001659 shown_match_ok = TRUE;
1660
1661 if (!shown_match_ok && shown_compl != NULL)
1662 {
1663 // The shown match isn't displayed, set it to the
1664 // previously displayed match.
1665 compl_shown_match = shown_compl;
1666 shown_match_ok = TRUE;
1667 }
1668 }
glepnira49c0772024-11-30 10:56:30 +01001669 compl = compl->cp_next;
1670 } while (compl != NULL && !is_first_match(compl));
1671
1672 if (compl_match_arraysize == 0)
1673 return -1;
1674
glepnirc0b7ca42025-02-13 20:27:44 +01001675 if (fuzzy_filter && !fuzzy_sort && !compl_no_select && !shown_match_ok)
1676 {
1677 compl_shown_match = shown_compl;
1678 shown_match_ok = TRUE;
1679 cur = 0;
1680 }
1681
glepnira49c0772024-11-30 10:56:30 +01001682 compl_match_array = ALLOC_CLEAR_MULT(pumitem_T, compl_match_arraysize);
1683 if (compl_match_array == NULL)
1684 return -1;
1685
1686 compl = match_head;
1687 i = 0;
1688 while (compl != NULL)
1689 {
glepnir6e199932024-12-14 21:13:27 +01001690 compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR] != NULL
1691 ? compl->cp_text[CPT_ABBR] : compl->cp_str.string;
glepnira49c0772024-11-30 10:56:30 +01001692 compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
1693 compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
1694 compl_match_array[i].pum_score = compl->cp_score;
Girish Palya19ef6b02025-05-28 20:28:21 +02001695 compl_match_array[i].pum_cpt_source_idx = compl->cp_cpt_source_idx;
glepnira49c0772024-11-30 10:56:30 +01001696 compl_match_array[i].pum_user_abbr_hlattr = compl->cp_user_abbr_hlattr;
1697 compl_match_array[i].pum_user_kind_hlattr = compl->cp_user_kind_hlattr;
glepnir6e199932024-12-14 21:13:27 +01001698 compl_match_array[i++].pum_extra = compl->cp_text[CPT_MENU] != NULL
1699 ? compl->cp_text[CPT_MENU] : compl->cp_fname;
glepnir80b66202024-11-27 21:53:53 +01001700 match_next = compl->cp_match_next;
1701 compl->cp_match_next = NULL;
1702 compl = match_next;
1703 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001704
zeertzjqd65aa1b2025-01-25 15:29:03 +01001705 if (fuzzy_sort && compl_leader.string != NULL && compl_leader.length > 0)
zeertzjq8e567472024-06-14 20:04:42 +02001706 {
1707 for (i = 0; i < compl_match_arraysize; i++)
1708 compl_match_array[i].pum_idx = i;
glepnira218cc62024-06-03 19:32:39 +02001709 // sort by the largest score of fuzzy match
zeertzjq8e567472024-06-14 20:04:42 +02001710 qsort(compl_match_array, (size_t)compl_match_arraysize,
1711 sizeof(pumitem_T), ins_compl_fuzzy_cmp);
glepnir65407ce2024-07-06 16:09:19 +02001712 shown_match_ok = TRUE;
zeertzjq8e567472024-06-14 20:04:42 +02001713 }
glepnira218cc62024-06-03 19:32:39 +02001714
Girish Palya19ef6b02025-05-28 20:28:21 +02001715 if (is_forward && fuzzy_sort && cpt_sources_array != NULL)
1716 trim_compl_match_array(); // Truncate by max_matches in 'cpt'
1717
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001718 if (!shown_match_ok) // no displayed match at all
1719 cur = -1;
1720
1721 return cur;
1722}
1723
1724/*
1725 * Show the popup menu for the list of matches.
1726 * Also adjusts "compl_shown_match" to an entry that is actually displayed.
1727 */
1728 void
1729ins_compl_show_pum(void)
1730{
1731 int i;
1732 int cur = -1;
1733 colnr_T col;
1734
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001735 if (!pum_wanted() || !pum_enough_matches())
1736 return;
1737
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001738 // Update the screen later, before drawing the popup menu over it.
1739 pum_call_update_screen();
1740
1741 if (compl_match_array == NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001742 // Need to build the popup menu list.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001743 cur = ins_compl_build_pum();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001744 else
1745 {
1746 // popup menu already exists, only need to find the current item.
1747 for (i = 0; i < compl_match_arraysize; ++i)
glepnir19e1dd62025-05-08 22:50:38 +02001748 {
John Marriott5e6ea922024-11-23 14:01:57 +01001749 if (compl_match_array[i].pum_text == compl_shown_match->cp_str.string
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001750 || compl_match_array[i].pum_text
1751 == compl_shown_match->cp_text[CPT_ABBR])
1752 {
1753 cur = i;
1754 break;
1755 }
glepnir19e1dd62025-05-08 22:50:38 +02001756 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001757 }
1758
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001759 if (compl_match_array == NULL)
glepnir0d3c0a62024-02-11 17:52:40 +01001760 {
1761#ifdef FEAT_EVAL
1762 if (compl_started && has_completechanged())
1763 trigger_complete_changed_event(cur);
1764#endif
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001765 return;
glepnir0d3c0a62024-02-11 17:52:40 +01001766 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001767
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001768 // In Replace mode when a $ is displayed at the end of the line only
1769 // part of the screen would be updated. We do need to redraw here.
1770 dollar_vcol = -1;
1771
1772 // Compute the screen column of the start of the completed text.
1773 // Use the cursor to get all wrapping and other settings right.
1774 col = curwin->w_cursor.col;
1775 curwin->w_cursor.col = compl_col;
glepnira218cc62024-06-03 19:32:39 +02001776 compl_selected_item = cur;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001777 pum_display(compl_match_array, compl_match_arraysize, cur);
1778 curwin->w_cursor.col = col;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001779
glepnircbb46b42024-02-03 18:11:13 +01001780 // After adding leader, set the current match to shown match.
1781 if (compl_started && compl_curr_match != compl_shown_match)
1782 compl_curr_match = compl_shown_match;
1783
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001784#ifdef FEAT_EVAL
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001785 if (has_completechanged())
1786 trigger_complete_changed_event(cur);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001787#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001788}
1789
1790#define DICT_FIRST (1) // use just first element in "dict"
1791#define DICT_EXACT (2) // "dict" is the exact name of a file
1792
1793/*
glepnir40c1c332024-06-11 19:37:04 +02001794 * Get current completion leader
1795 */
1796 char_u *
1797ins_compl_leader(void)
1798{
John Marriott5e6ea922024-11-23 14:01:57 +01001799 return compl_leader.string != NULL ? compl_leader.string : compl_orig_text.string;
1800}
1801
1802/*
1803 * Get current completion leader length
1804 */
1805 size_t
1806ins_compl_leader_len(void)
1807{
1808 return compl_leader.string != NULL ? compl_leader.length : compl_orig_text.length;
glepnir40c1c332024-06-11 19:37:04 +02001809}
1810
1811/*
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001812 * Add any identifiers that match the given pattern "pat" in the list of
1813 * dictionary files "dict_start" to the list of completions.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001814 */
1815 static void
1816ins_compl_dictionaries(
1817 char_u *dict_start,
1818 char_u *pat,
1819 int flags, // DICT_FIRST and/or DICT_EXACT
1820 int thesaurus) // Thesaurus completion
1821{
1822 char_u *dict = dict_start;
1823 char_u *ptr;
1824 char_u *buf;
1825 regmatch_T regmatch;
1826 char_u **files;
1827 int count;
1828 int save_p_scs;
1829 int dir = compl_direction;
1830
1831 if (*dict == NUL)
1832 {
1833#ifdef FEAT_SPELL
1834 // When 'dictionary' is empty and spell checking is enabled use
1835 // "spell".
1836 if (!thesaurus && curwin->w_p_spell)
1837 dict = (char_u *)"spell";
1838 else
1839#endif
1840 return;
1841 }
1842
1843 buf = alloc(LSIZE);
1844 if (buf == NULL)
1845 return;
1846 regmatch.regprog = NULL; // so that we can goto theend
1847
1848 // If 'infercase' is set, don't use 'smartcase' here
1849 save_p_scs = p_scs;
1850 if (curbuf->b_p_inf)
1851 p_scs = FALSE;
1852
1853 // When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern
1854 // to only match at the start of a line. Otherwise just match the
1855 // pattern. Also need to double backslashes.
1856 if (ctrl_x_mode_line_or_eval())
1857 {
1858 char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\");
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001859
1860 if (pat_esc == NULL)
1861 goto theend;
glepnir19e1dd62025-05-08 22:50:38 +02001862 size_t len = STRLEN(pat_esc) + 10;
Bram Moolenaar964b3742019-05-24 18:54:09 +02001863 ptr = alloc(len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001864 if (ptr == NULL)
1865 {
1866 vim_free(pat_esc);
1867 goto theend;
1868 }
1869 vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc);
1870 regmatch.regprog = vim_regcomp(ptr, RE_MAGIC);
1871 vim_free(pat_esc);
1872 vim_free(ptr);
1873 }
1874 else
1875 {
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001876 regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001877 if (regmatch.regprog == NULL)
1878 goto theend;
1879 }
1880
1881 // ignore case depends on 'ignorecase', 'smartcase' and "pat"
1882 regmatch.rm_ic = ignorecase(pat);
1883 while (*dict != NUL && !got_int && !compl_interrupted)
1884 {
1885 // copy one dictionary file name into buf
1886 if (flags == DICT_EXACT)
1887 {
1888 count = 1;
1889 files = &dict;
1890 }
1891 else
1892 {
1893 // Expand wildcards in the dictionary name, but do not allow
1894 // backticks (for security, the 'dict' option may have been set in
1895 // a modeline).
1896 copy_option_part(&dict, buf, LSIZE, ",");
1897# ifdef FEAT_SPELL
1898 if (!thesaurus && STRCMP(buf, "spell") == 0)
1899 count = -1;
1900 else
1901# endif
1902 if (vim_strchr(buf, '`') != NULL
1903 || expand_wildcards(1, &buf, &count, &files,
1904 EW_FILE|EW_SILENT) != OK)
1905 count = 0;
1906 }
1907
1908# ifdef FEAT_SPELL
1909 if (count == -1)
1910 {
1911 // Complete from active spelling. Skip "\<" in the pattern, we
1912 // don't use it as a RE.
1913 if (pat[0] == '\\' && pat[1] == '<')
1914 ptr = pat + 2;
1915 else
1916 ptr = pat;
1917 spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0);
1918 }
1919 else
1920# endif
1921 if (count > 0) // avoid warning for using "files" uninit
1922 {
1923 ins_compl_files(count, files, thesaurus, flags,
glepnirf31cfa22025-03-06 21:59:13 +01001924 (cfc_has_mode() ? NULL : &regmatch), buf, &dir);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001925 if (flags != DICT_EXACT)
1926 FreeWild(count, files);
1927 }
1928 if (flags != 0)
1929 break;
1930 }
1931
1932theend:
1933 p_scs = save_p_scs;
1934 vim_regfree(regmatch.regprog);
1935 vim_free(buf);
1936}
1937
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001938/*
1939 * Add all the words in the line "*buf_arg" from the thesaurus file "fname"
1940 * skipping the word at 'skip_word'. Returns OK on success.
1941 */
1942 static int
zeertzjq5fb3aab2022-08-24 16:48:23 +01001943thesaurus_add_words_in_line(
glepnir19e1dd62025-05-08 22:50:38 +02001944 char_u *fname,
1945 char_u **buf_arg,
1946 int dir,
1947 char_u *skip_word)
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001948{
1949 int status = OK;
1950 char_u *ptr;
1951 char_u *wstart;
1952
1953 // Add the other matches on the line
1954 ptr = *buf_arg;
1955 while (!got_int)
1956 {
1957 // Find start of the next word. Skip white
1958 // space and punctuation.
1959 ptr = find_word_start(ptr);
1960 if (*ptr == NUL || *ptr == NL)
1961 break;
1962 wstart = ptr;
1963
1964 // Find end of the word.
1965 if (has_mbyte)
1966 // Japanese words may have characters in
1967 // different classes, only separate words
1968 // with single-byte non-word characters.
1969 while (*ptr != NUL)
1970 {
1971 int l = (*mb_ptr2len)(ptr);
1972
1973 if (l < 2 && !vim_iswordc(*ptr))
1974 break;
1975 ptr += l;
1976 }
1977 else
1978 ptr = find_word_end(ptr);
1979
1980 // Add the word. Skip the regexp match.
1981 if (wstart != skip_word)
1982 {
1983 status = ins_compl_add_infercase(wstart, (int)(ptr - wstart), p_ic,
glepnirf31cfa22025-03-06 21:59:13 +01001984 fname, dir, FALSE, 0);
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001985 if (status == FAIL)
1986 break;
1987 }
1988 }
1989
1990 *buf_arg = ptr;
1991 return status;
1992}
1993
1994/*
1995 * Process "count" dictionary/thesaurus "files" and add the text matching
1996 * "regmatch".
1997 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001998 static void
1999ins_compl_files(
2000 int count,
2001 char_u **files,
2002 int thesaurus,
2003 int flags,
2004 regmatch_T *regmatch,
2005 char_u *buf,
2006 int *dir)
2007{
2008 char_u *ptr;
2009 int i;
2010 FILE *fp;
2011 int add_r;
glepnirf31cfa22025-03-06 21:59:13 +01002012 char_u *leader = NULL;
2013 int leader_len = 0;
glepnir58760162025-03-13 21:39:51 +01002014 int in_fuzzy_collect = cfc_has_mode();
glepnirf31cfa22025-03-06 21:59:13 +01002015 int score = 0;
2016 int len = 0;
2017 char_u *line_end = NULL;
2018
2019 if (in_fuzzy_collect)
2020 {
2021 leader = ins_compl_leader();
Yegappan Lakshmanan7b6add02025-04-01 20:38:37 +02002022 leader_len = (int)ins_compl_leader_len();
glepnirf31cfa22025-03-06 21:59:13 +01002023 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002024
2025 for (i = 0; i < count && !got_int && !compl_interrupted; i++)
2026 {
2027 fp = mch_fopen((char *)files[i], "r"); // open dictionary file
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01002028 if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002029 {
Bram Moolenaarcc233582020-12-12 13:32:07 +01002030 msg_hist_off = TRUE; // reset in msg_trunc_attr()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002031 vim_snprintf((char *)IObuff, IOSIZE,
2032 _("Scanning dictionary: %s"), (char *)files[i]);
2033 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
2034 }
2035
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002036 if (fp == NULL)
2037 continue;
2038
2039 // Read dictionary file line by line.
2040 // Check each line for a match.
2041 while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002042 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002043 ptr = buf;
glepnirf31cfa22025-03-06 21:59:13 +01002044 if (regmatch != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002045 {
glepnirf31cfa22025-03-06 21:59:13 +01002046 while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf)))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002047 {
glepnirf31cfa22025-03-06 21:59:13 +01002048 ptr = regmatch->startp[0];
2049 ptr = ctrl_x_mode_line_or_eval() ? find_line_end(ptr)
2050 : find_word_end(ptr);
2051 add_r = ins_compl_add_infercase(regmatch->startp[0],
2052 (int)(ptr - regmatch->startp[0]),
2053 p_ic, files[i], *dir, FALSE, 0);
2054 if (thesaurus)
2055 {
2056 // For a thesaurus, add all the words in the line
2057 ptr = buf;
2058 add_r = thesaurus_add_words_in_line(files[i], &ptr, *dir,
2059 regmatch->startp[0]);
2060 }
2061 if (add_r == OK)
2062 // if dir was BACKWARD then honor it just once
2063 *dir = FORWARD;
2064 else if (add_r == FAIL)
2065 break;
2066 // avoid expensive call to vim_regexec() when at end
2067 // of line
2068 if (*ptr == '\n' || got_int)
2069 break;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002070 }
glepnirf31cfa22025-03-06 21:59:13 +01002071 }
2072 else if (in_fuzzy_collect && leader_len > 0)
2073 {
2074 line_end = find_line_end(ptr);
2075 while (ptr < line_end)
2076 {
2077 if (fuzzy_match_str_in_line(&ptr, leader, &len, NULL, &score))
2078 {
2079 char_u *end_ptr = ctrl_x_mode_line_or_eval()
2080 ? find_line_end(ptr) : find_word_end(ptr);
2081 add_r = ins_compl_add_infercase(ptr, (int)(end_ptr - ptr),
2082 p_ic, files[i], *dir, FALSE, score);
2083 if (add_r == FAIL)
2084 break;
2085 ptr = end_ptr; // start from next word
2086 if (compl_get_longest && ctrl_x_mode_normal()
2087 && compl_first_match->cp_next
2088 && score == compl_first_match->cp_next->cp_score)
2089 compl_num_bests++;
2090 }
glepnirf31cfa22025-03-06 21:59:13 +01002091 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002092 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002093 line_breakcheck();
2094 ins_compl_check_keys(50, FALSE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002095 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002096 fclose(fp);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002097 }
2098}
2099
2100/*
2101 * Find the start of the next word.
2102 * Returns a pointer to the first char of the word. Also stops at a NUL.
2103 */
2104 char_u *
2105find_word_start(char_u *ptr)
2106{
2107 if (has_mbyte)
glepnir19e1dd62025-05-08 22:50:38 +02002108 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002109 while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1)
2110 ptr += (*mb_ptr2len)(ptr);
glepnir19e1dd62025-05-08 22:50:38 +02002111 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002112 else
glepnir19e1dd62025-05-08 22:50:38 +02002113 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002114 while (*ptr != NUL && *ptr != '\n' && !vim_iswordc(*ptr))
2115 ++ptr;
glepnir19e1dd62025-05-08 22:50:38 +02002116 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002117 return ptr;
2118}
2119
2120/*
2121 * Find the end of the word. Assumes it starts inside a word.
2122 * Returns a pointer to just after the word.
2123 */
2124 char_u *
2125find_word_end(char_u *ptr)
2126{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002127 if (has_mbyte)
2128 {
glepnir19e1dd62025-05-08 22:50:38 +02002129 int start_class = mb_get_class(ptr);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002130 if (start_class > 1)
2131 while (*ptr != NUL)
2132 {
2133 ptr += (*mb_ptr2len)(ptr);
2134 if (mb_get_class(ptr) != start_class)
2135 break;
2136 }
2137 }
2138 else
2139 while (vim_iswordc(*ptr))
2140 ++ptr;
2141 return ptr;
2142}
2143
2144/*
2145 * Find the end of the line, omitting CR and NL at the end.
2146 * Returns a pointer to just after the line.
2147 */
glepnirdd42b052025-03-08 16:52:55 +01002148 char_u *
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002149find_line_end(char_u *ptr)
2150{
glepnir19e1dd62025-05-08 22:50:38 +02002151 char_u *s = ptr + STRLEN(ptr);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002152 while (s > ptr && (s[-1] == CAR || s[-1] == NL))
2153 --s;
2154 return s;
2155}
2156
2157/*
Girish Palyacbe53192025-04-14 22:13:15 +02002158 * Free a completion item in the list
2159 */
2160 static void
2161ins_compl_item_free(compl_T *match)
2162{
Girish Palyacbe53192025-04-14 22:13:15 +02002163 VIM_CLEAR_STRING(match->cp_str);
2164 // several entries may use the same fname, free it just once.
2165 if (match->cp_flags & CP_FREE_FNAME)
2166 vim_free(match->cp_fname);
glepnir19e1dd62025-05-08 22:50:38 +02002167 for (int i = 0; i < CPT_COUNT; ++i)
Girish Palyacbe53192025-04-14 22:13:15 +02002168 vim_free(match->cp_text[i]);
2169#ifdef FEAT_EVAL
2170 clear_tv(&match->cp_user_data);
2171#endif
2172 vim_free(match);
2173}
2174
2175/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002176 * Free the list of completions
2177 */
2178 static void
2179ins_compl_free(void)
2180{
2181 compl_T *match;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002182
John Marriott5e6ea922024-11-23 14:01:57 +01002183 VIM_CLEAR_STRING(compl_pattern);
2184 VIM_CLEAR_STRING(compl_leader);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002185
2186 if (compl_first_match == NULL)
2187 return;
2188
2189 ins_compl_del_pum();
2190 pum_clear();
2191
2192 compl_curr_match = compl_first_match;
2193 do
2194 {
2195 match = compl_curr_match;
2196 compl_curr_match = compl_curr_match->cp_next;
Girish Palyacbe53192025-04-14 22:13:15 +02002197 ins_compl_item_free(match);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002198 } while (compl_curr_match != NULL && !is_first_match(compl_curr_match));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002199 compl_first_match = compl_curr_match = NULL;
2200 compl_shown_match = NULL;
2201 compl_old_match = NULL;
2202}
2203
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002204/*
2205 * Reset/clear the completion state.
2206 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002207 void
2208ins_compl_clear(void)
2209{
2210 compl_cont_status = 0;
2211 compl_started = FALSE;
glepnirf31cfa22025-03-06 21:59:13 +01002212 compl_cfc_longest_ins = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002213 compl_matches = 0;
glepnir07f0dbe2025-02-18 20:27:30 +01002214 compl_selected_item = -1;
glepnir6a38aff2024-12-16 21:56:16 +01002215 compl_ins_end_col = 0;
glepnircf7f0122025-04-15 19:02:00 +02002216 compl_curr_win = NULL;
2217 compl_curr_buf = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002218 VIM_CLEAR_STRING(compl_pattern);
2219 VIM_CLEAR_STRING(compl_leader);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002220 edit_submode_extra = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002221 VIM_CLEAR_STRING(compl_orig_text);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002222 compl_enter_selects = FALSE;
Girish Palya0ac1eb32025-04-16 20:18:33 +02002223 cpt_sources_clear();
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002224#ifdef FEAT_EVAL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002225 // clear v:completed_item
2226 set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002227#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002228}
2229
2230/*
2231 * Return TRUE when Insert completion is active.
2232 */
2233 int
2234ins_compl_active(void)
2235{
2236 return compl_started;
2237}
2238
2239/*
glepnir8d0bb6d2024-12-24 09:44:35 +01002240 * Return True when wp is the actual completion window
2241 */
2242 int
glepnircf7f0122025-04-15 19:02:00 +02002243ins_compl_win_active(win_T *wp)
glepnir8d0bb6d2024-12-24 09:44:35 +01002244{
glepnircf7f0122025-04-15 19:02:00 +02002245 return ins_compl_active() && wp == compl_curr_win
2246 && wp->w_buffer == compl_curr_buf;
glepnir8d0bb6d2024-12-24 09:44:35 +01002247}
2248
2249/*
Girish Palyacbe53192025-04-14 22:13:15 +02002250 * Selected one of the matches. When FALSE, the match was either edited or
2251 * using the longest common string.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002252 */
2253 int
2254ins_compl_used_match(void)
2255{
2256 return compl_used_match;
2257}
2258
2259/*
2260 * Initialize get longest common string.
2261 */
2262 void
2263ins_compl_init_get_longest(void)
2264{
2265 compl_get_longest = FALSE;
2266}
2267
2268/*
2269 * Returns TRUE when insert completion is interrupted.
2270 */
2271 int
2272ins_compl_interrupted(void)
2273{
2274 return compl_interrupted;
2275}
2276
2277/*
2278 * Returns TRUE if the <Enter> key selects a match in the completion popup
2279 * menu.
2280 */
2281 int
2282ins_compl_enter_selects(void)
2283{
2284 return compl_enter_selects;
2285}
2286
2287/*
2288 * Return the column where the text starts that is being completed
2289 */
2290 colnr_T
2291ins_compl_col(void)
2292{
2293 return compl_col;
2294}
2295
2296/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002297 * Return the length in bytes of the text being completed
2298 */
2299 int
2300ins_compl_len(void)
2301{
2302 return compl_length;
2303}
2304
2305/*
glepnir94a045e2025-03-01 16:12:23 +01002306 * Return TRUE when the 'completeopt' "preinsert" flag is in effect,
2307 * otherwise return FALSE.
glepniredd4ac32025-01-29 18:53:51 +01002308 */
2309 static int
2310ins_compl_has_preinsert(void)
2311{
glepnira2c55592025-02-28 17:43:42 +01002312 int cur_cot_flags = get_cot_flags();
glepnir94a045e2025-03-01 16:12:23 +01002313 return (cur_cot_flags & (COT_PREINSERT | COT_FUZZY | COT_MENUONE))
2314 == (COT_PREINSERT | COT_MENUONE);
glepniredd4ac32025-01-29 18:53:51 +01002315}
2316
2317/*
2318 * Returns TRUE if the pre-insert effect is valid and the cursor is within
2319 * the `compl_ins_end_col` range.
2320 */
2321 int
2322ins_compl_preinsert_effect(void)
2323{
2324 if (!ins_compl_has_preinsert())
2325 return FALSE;
2326
2327 return curwin->w_cursor.col < compl_ins_end_col;
2328}
2329
2330/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002331 * Delete one character before the cursor and show the subset of the matches
2332 * that match the word that is now before the cursor.
2333 * Returns the character to be used, NUL if the work is done and another char
2334 * to be got from the user.
2335 */
2336 int
2337ins_compl_bs(void)
2338{
2339 char_u *line;
2340 char_u *p;
2341
glepniredd4ac32025-01-29 18:53:51 +01002342 if (ins_compl_preinsert_effect())
2343 ins_compl_delete();
2344
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002345 line = ml_get_curline();
2346 p = line + curwin->w_cursor.col;
2347 MB_PTR_BACK(line, p);
2348
2349 // Stop completion when the whole word was deleted. For Omni completion
2350 // allow the word to be deleted, we won't match everything.
2351 // Respect the 'backspace' option.
2352 if ((int)(p - line) - (int)compl_col < 0
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002353 || ((int)(p - line) - (int)compl_col == 0 && !ctrl_x_mode_omni())
2354 || ctrl_x_mode_eval()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002355 || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col
2356 - compl_length < 0))
2357 return K_BS;
2358
2359 // Deleted more than what was used to find matches or didn't finish
2360 // finding all matches: need to look for matches all over again.
2361 if (curwin->w_cursor.col <= compl_col + compl_length
2362 || ins_compl_need_restart())
2363 ins_compl_restart();
2364
John Marriott5e6ea922024-11-23 14:01:57 +01002365 VIM_CLEAR_STRING(compl_leader);
2366 compl_leader.length = (size_t)((p - line) - compl_col);
2367 compl_leader.string = vim_strnsave(line + compl_col, compl_leader.length);
2368 if (compl_leader.string == NULL)
2369 {
2370 compl_leader.length = 0;
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00002371 return K_BS;
John Marriott5e6ea922024-11-23 14:01:57 +01002372 }
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00002373
2374 ins_compl_new_leader();
2375 if (compl_shown_match != NULL)
2376 // Make sure current match is not a hidden item.
2377 compl_curr_match = compl_shown_match;
2378 return NUL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002379}
2380
2381/*
2382 * Return TRUE when we need to find matches again, ins_compl_restart() is to
2383 * be called.
2384 */
2385 static int
2386ins_compl_need_restart(void)
2387{
2388 // Return TRUE if we didn't complete finding matches or when the
2389 // 'completefunc' returned "always" in the "refresh" dictionary item.
2390 return compl_was_interrupted
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002391 || ((ctrl_x_mode_function() || ctrl_x_mode_omni())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002392 && compl_opt_refresh_always);
2393}
2394
2395/*
2396 * Called after changing "compl_leader".
2397 * Show the popup menu with a different set of matches.
2398 * May also search for matches again if the previous search was interrupted.
2399 */
2400 static void
2401ins_compl_new_leader(void)
2402{
2403 ins_compl_del_pum();
2404 ins_compl_delete();
glepnir6a38aff2024-12-16 21:56:16 +01002405 ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002406 compl_used_match = FALSE;
2407
2408 if (compl_started)
Girish Palyacbe53192025-04-14 22:13:15 +02002409 {
John Marriott5e6ea922024-11-23 14:01:57 +01002410 ins_compl_set_original_text(compl_leader.string, compl_leader.length);
Girish Palyacbe53192025-04-14 22:13:15 +02002411 if (is_cpt_func_refresh_always())
2412 cpt_compl_refresh();
2413 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002414 else
2415 {
2416#ifdef FEAT_SPELL
2417 spell_bad_len = 0; // need to redetect bad word
2418#endif
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002419 // Matches were cleared, need to search for them now. Before drawing
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002420 // the popup menu display the changed text before the cursor. Set
2421 // "compl_restarting" to avoid that the first match is inserted.
2422 pum_call_update_screen();
2423#ifdef FEAT_GUI
2424 if (gui.in_use)
2425 {
2426 // Show the cursor after the match, not after the redrawn text.
2427 setcursor();
2428 out_flush_cursor(FALSE, FALSE);
2429 }
2430#endif
2431 compl_restarting = TRUE;
2432 if (ins_complete(Ctrl_N, TRUE) == FAIL)
2433 compl_cont_status = 0;
2434 compl_restarting = FALSE;
2435 }
2436
glepnir44180412025-02-20 22:09:48 +01002437 compl_enter_selects = !compl_used_match && compl_selected_item != -1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002438
2439 // Show the popup menu with a different set of matches.
2440 ins_compl_show_pum();
2441
2442 // Don't let Enter select the original text when there is no popup menu.
2443 if (compl_match_array == NULL)
2444 compl_enter_selects = FALSE;
glepniredd4ac32025-01-29 18:53:51 +01002445 else if (ins_compl_has_preinsert() && compl_leader.length > 0)
2446 ins_compl_insert(FALSE, TRUE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002447}
2448
2449/*
2450 * Return the length of the completion, from the completion start column to
2451 * the cursor column. Making sure it never goes below zero.
2452 */
2453 static int
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002454get_compl_len(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002455{
2456 int off = (int)curwin->w_cursor.col - (int)compl_col;
glepnir40891ba2025-02-10 22:18:00 +01002457 return MAX(0, off);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002458}
2459
2460/*
2461 * Append one character to the match leader. May reduce the number of
2462 * matches.
2463 */
2464 void
2465ins_compl_addleader(int c)
2466{
glepnir19e1dd62025-05-08 22:50:38 +02002467 int cc;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002468
glepniredd4ac32025-01-29 18:53:51 +01002469 if (ins_compl_preinsert_effect())
2470 ins_compl_delete();
2471
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002472 if (stop_arrow() == FAIL)
2473 return;
2474 if (has_mbyte && (cc = (*mb_char2len)(c)) > 1)
2475 {
2476 char_u buf[MB_MAXBYTES + 1];
2477
2478 (*mb_char2bytes)(c, buf);
2479 buf[cc] = NUL;
2480 ins_char_bytes(buf, cc);
2481 if (compl_opt_refresh_always)
2482 AppendToRedobuff(buf);
2483 }
2484 else
2485 {
2486 ins_char(c);
2487 if (compl_opt_refresh_always)
2488 AppendCharToRedobuff(c);
2489 }
2490
2491 // If we didn't complete finding matches we must search again.
2492 if (ins_compl_need_restart())
2493 ins_compl_restart();
2494
2495 // When 'always' is set, don't reset compl_leader. While completing,
2496 // cursor doesn't point original position, changing compl_leader would
2497 // break redo.
2498 if (!compl_opt_refresh_always)
2499 {
John Marriott5e6ea922024-11-23 14:01:57 +01002500 VIM_CLEAR_STRING(compl_leader);
2501 compl_leader.length = (size_t)(curwin->w_cursor.col - compl_col);
2502 compl_leader.string = vim_strnsave(ml_get_curline() + compl_col,
2503 compl_leader.length);
2504 if (compl_leader.string == NULL)
2505 {
2506 compl_leader.length = 0;
2507 return;
2508 }
2509
2510 ins_compl_new_leader();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002511 }
2512}
2513
2514/*
2515 * Setup for finding completions again without leaving CTRL-X mode. Used when
2516 * BS or a key was typed while still searching for matches.
2517 */
2518 static void
2519ins_compl_restart(void)
2520{
2521 ins_compl_free();
2522 compl_started = FALSE;
2523 compl_matches = 0;
2524 compl_cont_status = 0;
2525 compl_cont_mode = 0;
Girish Palya0ac1eb32025-04-16 20:18:33 +02002526 cpt_sources_clear();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002527}
2528
2529/*
2530 * Set the first match, the original text.
2531 */
2532 static void
John Marriott5e6ea922024-11-23 14:01:57 +01002533ins_compl_set_original_text(char_u *str, size_t len)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002534{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002535 // Replace the original text entry.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002536 // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
2537 // be at the last item for backward completion
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002538 if (match_at_original_text(compl_first_match)) // safety check
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002539 {
John Marriott5e6ea922024-11-23 14:01:57 +01002540 char_u *p = vim_strnsave(str, len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002541 if (p != NULL)
2542 {
John Marriott5e6ea922024-11-23 14:01:57 +01002543 VIM_CLEAR_STRING(compl_first_match->cp_str);
2544 compl_first_match->cp_str.string = p;
2545 compl_first_match->cp_str.length = len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002546 }
2547 }
2548 else if (compl_first_match->cp_prev != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002549 && match_at_original_text(compl_first_match->cp_prev))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002550 {
John Marriott5e6ea922024-11-23 14:01:57 +01002551 char_u *p = vim_strnsave(str, len);
2552 if (p != NULL)
2553 {
2554 VIM_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
2555 compl_first_match->cp_prev->cp_str.string = p;
2556 compl_first_match->cp_prev->cp_str.length = len;
2557 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002558 }
2559}
2560
2561/*
2562 * Append one character to the match leader. May reduce the number of
2563 * matches.
2564 */
2565 void
2566ins_compl_addfrommatch(void)
2567{
2568 char_u *p;
2569 int len = (int)curwin->w_cursor.col - (int)compl_col;
2570 int c;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002571
John Marriott5e6ea922024-11-23 14:01:57 +01002572 p = compl_shown_match->cp_str.string;
2573 if ((int)compl_shown_match->cp_str.length <= len) // the match is too short
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002574 {
John Marriott5e6ea922024-11-23 14:01:57 +01002575 size_t plen;
2576 compl_T *cp;
2577
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002578 // When still at the original match use the first entry that matches
2579 // the leader.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002580 if (!match_at_original_text(compl_shown_match))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002581 return;
2582
2583 p = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002584 plen = 0;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002585 for (cp = compl_shown_match->cp_next; cp != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002586 && !is_first_match(cp); cp = cp->cp_next)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002587 {
John Marriott5e6ea922024-11-23 14:01:57 +01002588 if (compl_leader.string == NULL
2589 || ins_compl_equal(cp, compl_leader.string,
2590 (int)compl_leader.length))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002591 {
John Marriott5e6ea922024-11-23 14:01:57 +01002592 p = cp->cp_str.string;
2593 plen = cp->cp_str.length;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002594 break;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002595 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002596 }
John Marriott5e6ea922024-11-23 14:01:57 +01002597 if (p == NULL || (int)plen <= len)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002598 return;
2599 }
2600 p += len;
2601 c = PTR2CHAR(p);
2602 ins_compl_addleader(c);
2603}
2604
2605/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002606 * Set the CTRL-X completion mode based on the key "c" typed after a CTRL-X.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002607 * Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre,
2608 * compl_cont_mode and compl_cont_status.
2609 * Returns TRUE when the character is not to be inserted.
2610 */
2611 static int
2612set_ctrl_x_mode(int c)
2613{
glepnir05460682025-05-26 18:23:27 +02002614 int retval = FALSE;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002615
2616 switch (c)
2617 {
2618 case Ctrl_E:
2619 case Ctrl_Y:
2620 // scroll the window one line up or down
2621 ctrl_x_mode = CTRL_X_SCROLL;
2622 if (!(State & REPLACE_FLAG))
2623 edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
2624 else
2625 edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
2626 edit_submode_pre = NULL;
2627 showmode();
2628 break;
2629 case Ctrl_L:
2630 // complete whole line
2631 ctrl_x_mode = CTRL_X_WHOLE_LINE;
2632 break;
2633 case Ctrl_F:
2634 // complete filenames
2635 ctrl_x_mode = CTRL_X_FILES;
2636 break;
2637 case Ctrl_K:
zeertzjq5fb3aab2022-08-24 16:48:23 +01002638 // complete words from a dictionary
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002639 ctrl_x_mode = CTRL_X_DICTIONARY;
2640 break;
2641 case Ctrl_R:
glepnir05460682025-05-26 18:23:27 +02002642 // When CTRL-R is followed by '=', don't trigger register completion
2643 // This allows expressions like <C-R>=func()<CR> to work normally
2644 if (vpeekc() == '=')
2645 break;
2646 ctrl_x_mode = CTRL_X_REGISTER;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002647 break;
2648 case Ctrl_T:
2649 // complete words from a thesaurus
2650 ctrl_x_mode = CTRL_X_THESAURUS;
2651 break;
2652#ifdef FEAT_COMPL_FUNC
2653 case Ctrl_U:
2654 // user defined completion
2655 ctrl_x_mode = CTRL_X_FUNCTION;
2656 break;
2657 case Ctrl_O:
2658 // omni completion
2659 ctrl_x_mode = CTRL_X_OMNI;
2660 break;
2661#endif
2662 case 's':
2663 case Ctrl_S:
2664 // complete spelling suggestions
2665 ctrl_x_mode = CTRL_X_SPELL;
2666#ifdef FEAT_SPELL
2667 ++emsg_off; // Avoid getting the E756 error twice.
2668 spell_back_to_badword();
2669 --emsg_off;
2670#endif
2671 break;
2672 case Ctrl_RSB:
2673 // complete tag names
2674 ctrl_x_mode = CTRL_X_TAGS;
2675 break;
2676#ifdef FEAT_FIND_ID
2677 case Ctrl_I:
2678 case K_S_TAB:
2679 // complete keywords from included files
2680 ctrl_x_mode = CTRL_X_PATH_PATTERNS;
2681 break;
2682 case Ctrl_D:
2683 // complete definitions from included files
2684 ctrl_x_mode = CTRL_X_PATH_DEFINES;
2685 break;
2686#endif
2687 case Ctrl_V:
2688 case Ctrl_Q:
2689 // complete vim commands
2690 ctrl_x_mode = CTRL_X_CMDLINE;
2691 break;
2692 case Ctrl_Z:
2693 // stop completion
2694 ctrl_x_mode = CTRL_X_NORMAL;
2695 edit_submode = NULL;
2696 showmode();
2697 retval = TRUE;
2698 break;
2699 case Ctrl_P:
2700 case Ctrl_N:
2701 // ^X^P means LOCAL expansion if nothing interrupted (eg we
2702 // just started ^X mode, or there were enough ^X's to cancel
2703 // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
2704 // do normal expansion when interrupting a different mode (say
2705 // ^X^F^X^P or ^P^X^X^P, see below)
2706 // nothing changes if interrupting mode 0, (eg, the flag
2707 // doesn't change when going to ADDING mode -- Acevedo
2708 if (!(compl_cont_status & CONT_INTRPT))
2709 compl_cont_status |= CONT_LOCAL;
2710 else if (compl_cont_mode != 0)
2711 compl_cont_status &= ~CONT_LOCAL;
2712 // FALLTHROUGH
2713 default:
2714 // If we have typed at least 2 ^X's... for modes != 0, we set
2715 // compl_cont_status = 0 (eg, as if we had just started ^X
2716 // mode).
2717 // For mode 0, we set "compl_cont_mode" to an impossible
2718 // value, in both cases ^X^X can be used to restart the same
2719 // mode (avoiding ADDING mode).
2720 // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
2721 // 'complete' and local ^P expansions respectively.
2722 // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
2723 // mode -- Acevedo
2724 if (c == Ctrl_X)
2725 {
2726 if (compl_cont_mode != 0)
2727 compl_cont_status = 0;
2728 else
2729 compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
2730 }
2731 ctrl_x_mode = CTRL_X_NORMAL;
2732 edit_submode = NULL;
2733 showmode();
2734 break;
2735 }
2736
2737 return retval;
2738}
2739
2740/*
glepnir1c5a1202024-12-04 20:27:34 +01002741 * Trigger CompleteDone event and adds relevant information to v:event
2742 */
2743 static void
2744trigger_complete_done_event(int mode UNUSED, char_u *word UNUSED)
2745{
2746#if defined(FEAT_EVAL)
2747 save_v_event_T save_v_event;
2748 dict_T *v_event = get_v_event(&save_v_event);
2749 char_u *mode_str = NULL;
2750
2751 mode = mode & ~CTRL_X_WANT_IDENT;
2752 if (ctrl_x_mode_names[mode])
2753 mode_str = (char_u *)ctrl_x_mode_names[mode];
2754
2755 (void)dict_add_string(v_event, "complete_word",
2756 word == NULL ? (char_u *)"" : word);
2757 (void)dict_add_string(v_event, "complete_type",
2758 mode_str != NULL ? mode_str : (char_u *)"");
2759
2760 dict_set_items_ro(v_event);
2761#endif
2762 ins_apply_autocmds(EVENT_COMPLETEDONE);
2763
2764#if defined(FEAT_EVAL)
2765 restore_v_event(v_event, &save_v_event);
2766#endif
2767}
2768
2769/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002770 * Stop insert completion mode
2771 */
2772 static int
2773ins_compl_stop(int c, int prev_mode, int retval)
2774{
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002775 int want_cindent;
glepnir1c5a1202024-12-04 20:27:34 +01002776 char_u *word = NULL;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002777
glepnir84a75032025-03-15 09:59:22 +01002778 // Remove pre-inserted text when present.
zeertzjq13436812025-04-23 20:46:35 +02002779 if (ins_compl_preinsert_effect() && ins_compl_win_active(curwin))
glepnir84a75032025-03-15 09:59:22 +01002780 ins_compl_delete();
2781
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002782 // Get here when we have finished typing a sequence of ^N and
2783 // ^P or other completion characters in CTRL-X mode. Free up
2784 // memory that was used, and make sure we can redo the insert.
John Marriott5e6ea922024-11-23 14:01:57 +01002785 if (compl_curr_match != NULL || compl_leader.string != NULL || c == Ctrl_E)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002786 {
glepnir40891ba2025-02-10 22:18:00 +01002787 char_u *ptr = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002788
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002789 // If any of the original typed text has been changed, eg when
2790 // ignorecase is set, we must add back-spaces to the redo
2791 // buffer. We add as few as necessary to delete just the part
2792 // of the original text that has changed.
2793 // When using the longest match, edited the match or used
2794 // CTRL-E then don't use the current match.
2795 if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E)
John Marriott5e6ea922024-11-23 14:01:57 +01002796 ptr = compl_curr_match->cp_str.string;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002797 ins_compl_fixRedoBufForLeader(ptr);
2798 }
2799
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002800 want_cindent = (get_can_cindent() && cindent_on());
Bram Moolenaar8e145b82022-05-21 20:17:31 +01002801
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002802 // When completing whole lines: fix indent for 'cindent'.
2803 // Otherwise, break line if it's too long.
2804 if (compl_cont_mode == CTRL_X_WHOLE_LINE)
2805 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002806 // re-indent the current line
2807 if (want_cindent)
2808 {
2809 do_c_expr_indent();
2810 want_cindent = FALSE; // don't do it again
2811 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002812 }
2813 else
2814 {
2815 int prev_col = curwin->w_cursor.col;
2816
2817 // put the cursor on the last char, for 'tw' formatting
2818 if (prev_col > 0)
2819 dec_cursor();
2820 // only format when something was inserted
2821 if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E)
2822 insertchar(NUL, 0, -1);
2823 if (prev_col > 0
2824 && ml_get_curline()[curwin->w_cursor.col] != NUL)
2825 inc_cursor();
2826 }
2827
2828 // If the popup menu is displayed pressing CTRL-Y means accepting
2829 // the selection without inserting anything. When
2830 // compl_enter_selects is set the Enter key does the same.
2831 if ((c == Ctrl_Y || (compl_enter_selects
2832 && (c == CAR || c == K_KENTER || c == NL)))
2833 && pum_visible())
glepnir1c5a1202024-12-04 20:27:34 +01002834 {
2835 word = vim_strsave(compl_shown_match->cp_str.string);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002836 retval = TRUE;
glepnir1c5a1202024-12-04 20:27:34 +01002837 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002838
2839 // CTRL-E means completion is Ended, go back to the typed text.
2840 // but only do this, if the Popup is still visible
2841 if (c == Ctrl_E)
2842 {
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002843 char_u *p = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002844 size_t plen = 0;
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002845
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002846 ins_compl_delete();
John Marriott5e6ea922024-11-23 14:01:57 +01002847 if (compl_leader.string != NULL)
2848 {
2849 p = compl_leader.string;
2850 plen = compl_leader.length;
2851 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002852 else if (compl_first_match != NULL)
John Marriott5e6ea922024-11-23 14:01:57 +01002853 {
2854 p = compl_orig_text.string;
2855 plen = compl_orig_text.length;
2856 }
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002857 if (p != NULL)
2858 {
2859 int compl_len = get_compl_len();
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002860
John Marriott5e6ea922024-11-23 14:01:57 +01002861 if ((int)plen > compl_len)
zeertzjqf25d8f92024-12-18 21:12:25 +01002862 ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len);
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002863 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002864 retval = TRUE;
2865 }
2866
2867 auto_format(FALSE, TRUE);
2868
2869 // Trigger the CompleteDonePre event to give scripts a chance to
2870 // act upon the completion before clearing the info, and restore
2871 // ctrl_x_mode, so that complete_info() can be used.
2872 ctrl_x_mode = prev_mode;
2873 ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
2874
2875 ins_compl_free();
2876 compl_started = FALSE;
2877 compl_matches = 0;
2878 if (!shortmess(SHM_COMPLETIONMENU))
2879 msg_clr_cmdline(); // necessary for "noshowmode"
2880 ctrl_x_mode = CTRL_X_NORMAL;
2881 compl_enter_selects = FALSE;
2882 if (edit_submode != NULL)
2883 {
2884 edit_submode = NULL;
2885 showmode();
2886 }
2887
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002888 if (c == Ctrl_C && cmdwin_type != 0)
2889 // Avoid the popup menu remains displayed when leaving the
2890 // command line window.
2891 update_screen(0);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002892 // Indent now if a key was typed that is in 'cinkeys'.
2893 if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0)))
2894 do_c_expr_indent();
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002895 // Trigger the CompleteDone event to give scripts a chance to act
2896 // upon the end of completion.
glepnir1c5a1202024-12-04 20:27:34 +01002897 trigger_complete_done_event(prev_mode, word);
2898 vim_free(word);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002899
2900 return retval;
2901}
2902
2903/*
glepnircf7f0122025-04-15 19:02:00 +02002904 * Cancel completion.
2905 */
2906 int
2907ins_compl_cancel(void)
2908{
2909 return ins_compl_stop(' ', ctrl_x_mode, TRUE);
2910}
2911
2912/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002913 * Prepare for Insert mode completion, or stop it.
2914 * Called just after typing a character in Insert mode.
2915 * Returns TRUE when the character is not to be inserted;
2916 */
2917 int
2918ins_compl_prep(int c)
2919{
glepnir19e1dd62025-05-08 22:50:38 +02002920 int retval = FALSE;
2921 int prev_mode = ctrl_x_mode;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002922
2923 // Forget any previous 'special' messages if this is actually
2924 // a ^X mode key - bar ^R, in which case we wait to see what it gives us.
2925 if (c != Ctrl_R && vim_is_ctrl_x_key(c))
2926 edit_submode_extra = NULL;
2927
zeertzjq440d4cb2023-03-02 17:51:32 +00002928 // Ignore end of Select mode mapping and mouse scroll/movement.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002929 if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
zeertzjq440d4cb2023-03-02 17:51:32 +00002930 || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_MOUSEMOVE
Bram Moolenaare32c3c42022-01-15 18:26:04 +00002931 || c == K_COMMAND || c == K_SCRIPT_COMMAND)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002932 return retval;
2933
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002934#ifdef FEAT_PROP_POPUP
Bram Moolenaarf0bc15c2019-08-18 19:23:45 +02002935 // Ignore mouse events in a popup window
2936 if (is_mouse_key(c))
2937 {
2938 // Ignore drag and release events, the position does not need to be in
2939 // the popup and it may have just closed.
2940 if (c == K_LEFTRELEASE
2941 || c == K_LEFTRELEASE_NM
2942 || c == K_MIDDLERELEASE
2943 || c == K_RIGHTRELEASE
2944 || c == K_X1RELEASE
2945 || c == K_X2RELEASE
2946 || c == K_LEFTDRAG
2947 || c == K_MIDDLEDRAG
2948 || c == K_RIGHTDRAG
2949 || c == K_X1DRAG
2950 || c == K_X2DRAG)
2951 return retval;
2952 if (popup_visible)
2953 {
2954 int row = mouse_row;
2955 int col = mouse_col;
2956 win_T *wp = mouse_find_win(&row, &col, FIND_POPUP);
2957
2958 if (wp != NULL && WIN_IS_POPUP(wp))
2959 return retval;
2960 }
2961 }
2962#endif
2963
zeertzjqdca29d92021-08-31 19:12:51 +02002964 if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X)
2965 {
2966 if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c)
2967 || !vim_is_ctrl_x_key(c))
2968 {
2969 // Not starting another completion mode.
2970 ctrl_x_mode = CTRL_X_CMDLINE;
2971
2972 // CTRL-X CTRL-Z should stop completion without inserting anything
2973 if (c == Ctrl_Z)
2974 retval = TRUE;
2975 }
2976 else
2977 {
2978 ctrl_x_mode = CTRL_X_CMDLINE;
2979
2980 // Other CTRL-X keys first stop completion, then start another
2981 // completion mode.
2982 ins_compl_prep(' ');
2983 ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
2984 }
2985 }
2986
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002987 // Set "compl_get_longest" when finding the first matches.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002988 if (ctrl_x_mode_not_defined_yet()
2989 || (ctrl_x_mode_normal() && !compl_started))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002990 {
zeertzjq529b9ad2024-06-05 20:27:06 +02002991 compl_get_longest = (get_cot_flags() & COT_LONGEST) != 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002992 compl_used_match = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002993 }
2994
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002995 if (ctrl_x_mode_not_defined_yet())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002996 // We have just typed CTRL-X and aren't quite sure which CTRL-X mode
2997 // it will be yet. Now we decide.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002998 retval = set_ctrl_x_mode(c);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002999 else if (ctrl_x_mode_not_default())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003000 {
3001 // We're already in CTRL-X mode, do we stay in it?
3002 if (!vim_is_ctrl_x_key(c))
3003 {
glepnir40891ba2025-02-10 22:18:00 +01003004 ctrl_x_mode = ctrl_x_mode_scroll() ? CTRL_X_NORMAL : CTRL_X_FINISHED;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003005 edit_submode = NULL;
3006 }
3007 showmode();
3008 }
3009
3010 if (compl_started || ctrl_x_mode == CTRL_X_FINISHED)
3011 {
3012 // Show error message from attempted keyword completion (probably
3013 // 'Pattern not found') until another key is hit, then go back to
3014 // showing what mode we are in.
3015 showmode();
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003016 if ((ctrl_x_mode_normal() && c != Ctrl_N && c != Ctrl_P
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003017 && c != Ctrl_R && !ins_compl_pum_key(c))
3018 || ctrl_x_mode == CTRL_X_FINISHED)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00003019 retval = ins_compl_stop(c, prev_mode, retval);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003020 }
3021 else if (ctrl_x_mode == CTRL_X_LOCAL_MSG)
3022 // Trigger the CompleteDone event to give scripts a chance to act
3023 // upon the (possibly failed) completion.
glepnir1c5a1202024-12-04 20:27:34 +01003024 trigger_complete_done_event(ctrl_x_mode, NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003025
LemonBoy2bf52dd2022-04-09 18:17:34 +01003026 may_trigger_modechanged();
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01003027
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003028 // reset continue_* if we left expansion-mode, if we stay they'll be
3029 // (re)set properly in ins_complete()
3030 if (!vim_is_ctrl_x_key(c))
3031 {
3032 compl_cont_status = 0;
3033 compl_cont_mode = 0;
3034 }
3035
3036 return retval;
3037}
3038
3039/*
3040 * Fix the redo buffer for the completion leader replacing some of the typed
3041 * text. This inserts backspaces and appends the changed text.
3042 * "ptr" is the known leader text or NUL.
3043 */
3044 static void
3045ins_compl_fixRedoBufForLeader(char_u *ptr_arg)
3046{
John Marriott5e6ea922024-11-23 14:01:57 +01003047 int len = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003048 char_u *p;
3049 char_u *ptr = ptr_arg;
3050
3051 if (ptr == NULL)
3052 {
John Marriott5e6ea922024-11-23 14:01:57 +01003053 if (compl_leader.string != NULL)
3054 ptr = compl_leader.string;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003055 else
3056 return; // nothing to do
3057 }
John Marriott5e6ea922024-11-23 14:01:57 +01003058 if (compl_orig_text.string != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003059 {
John Marriott5e6ea922024-11-23 14:01:57 +01003060 p = compl_orig_text.string;
glepnir40891ba2025-02-10 22:18:00 +01003061 // Find length of common prefix between original text and new completion
3062 while (p[len] != NUL && p[len] == ptr[len])
3063 len++;
3064 // Adjust length to not break inside a multi-byte character
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003065 if (len > 0)
3066 len -= (*mb_head_off)(p, p + len);
glepnir40891ba2025-02-10 22:18:00 +01003067 // Add backspace characters for each remaining character in
3068 // original text
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003069 for (p += len; *p != NUL; MB_PTR_ADV(p))
3070 AppendCharToRedobuff(K_BS);
3071 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003072 if (ptr != NULL)
3073 AppendToRedobuffLit(ptr + len, -1);
3074}
3075
3076/*
3077 * Loops through the list of windows, loaded-buffers or non-loaded-buffers
3078 * (depending on flag) starting from buf and looking for a non-scanned
3079 * buffer (other than curbuf). curbuf is special, if it is called with
3080 * buf=curbuf then it has to be the first call for a given flag/expansion.
3081 *
3082 * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo
3083 */
3084 static buf_T *
3085ins_compl_next_buf(buf_T *buf, int flag)
3086{
glepnir19e1dd62025-05-08 22:50:38 +02003087 static win_T *wp = NULL;
3088 int skip_buffer;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003089
3090 if (flag == 'w') // just windows
3091 {
Bram Moolenaar0ff01832022-09-24 19:20:30 +01003092 if (buf == curbuf || !win_valid(wp))
3093 // first call for this flag/expansion or window was closed
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003094 wp = curwin;
glepnir40891ba2025-02-10 22:18:00 +01003095
3096 while (TRUE)
3097 {
Hirohito Higashi355db992025-04-28 18:07:02 +02003098 // Move to next window (wrap to first window if at the end)
3099 wp = (wp->w_next != NULL) ? wp->w_next : firstwin;
3100 // Break if we're back at start or found an unscanned buffer
3101 if (wp == curwin || !wp->w_buffer->b_scanned)
3102 break;
glepnir40891ba2025-02-10 22:18:00 +01003103 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003104 buf = wp->w_buffer;
3105 }
3106 else
glepnir40891ba2025-02-10 22:18:00 +01003107 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003108 // 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U'
3109 // (unlisted buffers)
3110 // When completing whole lines skip unloaded buffers.
glepnir40891ba2025-02-10 22:18:00 +01003111 while (TRUE)
3112 {
3113 // Move to next buffer (wrap to first buffer if at the end)
3114 buf = (buf->b_next != NULL) ? buf->b_next : firstbuf;
3115 // Break if we're back at start buffer
3116 if (buf == curbuf)
3117 break;
3118
3119 // Check buffer conditions based on flag
3120 if (flag == 'U')
3121 skip_buffer = buf->b_p_bl;
3122 else
3123 skip_buffer = !buf->b_p_bl ||
3124 (buf->b_ml.ml_mfp == NULL) != (flag == 'u');
3125
3126 // Break if we found a buffer that matches our criteria
3127 if (!skip_buffer && !buf->b_scanned)
3128 break;
3129 }
3130 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003131 return buf;
3132}
3133
3134#ifdef FEAT_COMPL_FUNC
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003135
3136# ifdef FEAT_EVAL
3137static callback_T cfu_cb; // 'completefunc' callback function
3138static callback_T ofu_cb; // 'omnifunc' callback function
3139static callback_T tsrfu_cb; // 'thesaurusfunc' callback function
3140# endif
3141
3142/*
3143 * Copy a global callback function to a buffer local callback.
3144 */
3145 static void
3146copy_global_to_buflocal_cb(callback_T *globcb, callback_T *bufcb)
3147{
3148 free_callback(bufcb);
3149 if (globcb->cb_name != NULL && *globcb->cb_name != NUL)
3150 copy_callback(bufcb, globcb);
3151}
3152
3153/*
3154 * Parse the 'completefunc' option value and set the callback function.
3155 * Invoked when the 'completefunc' option is set. The option value can be a
3156 * name of a function (string), or function(<name>) or funcref(<name>) or a
3157 * lambda expression.
3158 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003159 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003160did_set_completefunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003161{
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003162 if (option_set_callback_func(curbuf->b_p_cfu, &cfu_cb) == FAIL)
3163 return e_invalid_argument;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003164
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003165 set_buflocal_cfu_callback(curbuf);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003166
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003167 return NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003168}
3169
3170/*
3171 * Copy the global 'completefunc' callback function to the buffer-local
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003172 * 'completefunc' callback for "buf".
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003173 */
3174 void
3175set_buflocal_cfu_callback(buf_T *buf UNUSED)
3176{
3177# ifdef FEAT_EVAL
3178 copy_global_to_buflocal_cb(&cfu_cb, &buf->b_cfu_cb);
3179# endif
3180}
3181
3182/*
3183 * Parse the 'omnifunc' option value and set the callback function.
3184 * Invoked when the 'omnifunc' option is set. The option value can be a
3185 * name of a function (string), or function(<name>) or funcref(<name>) or a
3186 * lambda expression.
3187 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003188 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003189did_set_omnifunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003190{
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003191 if (option_set_callback_func(curbuf->b_p_ofu, &ofu_cb) == FAIL)
3192 return e_invalid_argument;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003193
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003194 set_buflocal_ofu_callback(curbuf);
3195 return NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003196}
3197
3198/*
3199 * Copy the global 'omnifunc' callback function to the buffer-local 'omnifunc'
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003200 * callback for "buf".
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003201 */
3202 void
3203set_buflocal_ofu_callback(buf_T *buf UNUSED)
3204{
3205# ifdef FEAT_EVAL
3206 copy_global_to_buflocal_cb(&ofu_cb, &buf->b_ofu_cb);
3207# endif
3208}
3209
3210/*
3211 * Parse the 'thesaurusfunc' option value and set the callback function.
3212 * Invoked when the 'thesaurusfunc' option is set. The option value can be a
3213 * name of a function (string), or function(<name>) or funcref(<name>) or a
3214 * lambda expression.
3215 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003216 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003217did_set_thesaurusfunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003218{
3219 int retval;
3220
zeertzjq6eda2692024-11-03 09:23:33 +01003221 if (args->os_flags & OPT_LOCAL)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003222 // buffer-local option set
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003223 retval = option_set_callback_func(curbuf->b_p_tsrfu,
3224 &curbuf->b_tsrfu_cb);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003225 else
3226 {
3227 // global option set
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003228 retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
zeertzjq6eda2692024-11-03 09:23:33 +01003229 // when using :set, free the local callback
3230 if (!(args->os_flags & OPT_GLOBAL))
3231 free_callback(&curbuf->b_tsrfu_cb);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003232 }
3233
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003234 return retval == FAIL ? e_invalid_argument : NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003235}
3236
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00003237/*
3238 * Mark the global 'completefunc' 'omnifunc' and 'thesaurusfunc' callbacks with
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003239 * "copyID" so that they are not garbage collected.
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00003240 */
3241 int
3242set_ref_in_insexpand_funcs(int copyID)
3243{
glepnir19e1dd62025-05-08 22:50:38 +02003244 int abort = set_ref_in_callback(&cfu_cb, copyID);
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00003245 abort = abort || set_ref_in_callback(&ofu_cb, copyID);
3246 abort = abort || set_ref_in_callback(&tsrfu_cb, copyID);
3247
3248 return abort;
3249}
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003250
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003251/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003252 * Get the user-defined completion function name for completion "type"
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01003253 */
3254 static char_u *
3255get_complete_funcname(int type)
3256{
3257 switch (type)
3258 {
3259 case CTRL_X_FUNCTION:
3260 return curbuf->b_p_cfu;
3261 case CTRL_X_OMNI:
3262 return curbuf->b_p_ofu;
3263 case CTRL_X_THESAURUS:
Bram Moolenaarf4d8b762021-10-17 14:13:09 +01003264 return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu;
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01003265 default:
3266 return (char_u *)"";
3267 }
3268}
3269
3270/*
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003271 * Get the callback to use for insert mode completion.
3272 */
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003273 static callback_T *
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003274get_insert_callback(int type)
3275{
3276 if (type == CTRL_X_FUNCTION)
3277 return &curbuf->b_cfu_cb;
3278 if (type == CTRL_X_OMNI)
3279 return &curbuf->b_ofu_cb;
3280 // CTRL_X_THESAURUS
3281 return (*curbuf->b_p_tsrfu != NUL) ? &curbuf->b_tsrfu_cb : &tsrfu_cb;
3282}
3283
3284/*
Yegappan Lakshmanan05e59e32021-12-01 10:30:07 +00003285 * Execute user defined complete function 'completefunc', 'omnifunc' or
3286 * 'thesaurusfunc', and get matches in "matches".
Girish Palyacbe53192025-04-14 22:13:15 +02003287 * "type" can be one of CTRL_X_OMNI, CTRL_X_FUNCTION, or CTRL_X_THESAURUS.
3288 * Callback function "cb" is set if triggered by a function in the 'cpt'
3289 * option; otherwise, it is NULL.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003290 */
3291 static void
Girish Palyacbe53192025-04-14 22:13:15 +02003292expand_by_function(int type, char_u *base, callback_T *cb)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003293{
3294 list_T *matchlist = NULL;
3295 dict_T *matchdict = NULL;
3296 typval_T args[3];
3297 char_u *funcname;
3298 pos_T pos;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003299 typval_T rettv;
3300 int save_State = State;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003301 int retval;
Girish Palyacbe53192025-04-14 22:13:15 +02003302 int is_cpt_function = (cb != NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003303
Girish Palyacbe53192025-04-14 22:13:15 +02003304 if (!is_cpt_function)
3305 {
3306 funcname = get_complete_funcname(type);
3307 if (*funcname == NUL)
3308 return;
3309 cb = get_insert_callback(type);
3310 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003311
3312 // Call 'completefunc' to obtain the list of matches.
3313 args[0].v_type = VAR_NUMBER;
3314 args[0].vval.v_number = 0;
3315 args[1].v_type = VAR_STRING;
3316 args[1].vval.v_string = base != NULL ? base : (char_u *)"";
3317 args[2].v_type = VAR_UNKNOWN;
3318
3319 pos = curwin->w_cursor;
Bram Moolenaar28976e22021-01-29 21:07:07 +01003320 // Lock the text to avoid weird things from happening. Also disallow
3321 // switching to another window, it should not be needed and may end up in
3322 // Insert mode in another buffer.
zeertzjqcfe45652022-05-27 17:26:55 +01003323 ++textlock;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003324
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003325 retval = call_callback(cb, 0, &rettv, 2, args);
3326
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003327 // Call a function, which returns a list or dict.
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003328 if (retval == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003329 {
3330 switch (rettv.v_type)
3331 {
3332 case VAR_LIST:
3333 matchlist = rettv.vval.v_list;
3334 break;
3335 case VAR_DICT:
3336 matchdict = rettv.vval.v_dict;
3337 break;
3338 case VAR_SPECIAL:
3339 if (rettv.vval.v_number == VVAL_NONE)
3340 compl_opt_suppress_empty = TRUE;
3341 // FALLTHROUGH
3342 default:
3343 // TODO: Give error message?
3344 clear_tv(&rettv);
3345 break;
3346 }
3347 }
zeertzjqcfe45652022-05-27 17:26:55 +01003348 --textlock;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003349
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003350 curwin->w_cursor = pos; // restore the cursor position
zeertzjq0a419e02024-04-02 19:01:14 +02003351 check_cursor(); // make sure cursor position is valid, just in case
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003352 validate_cursor();
3353 if (!EQUAL_POS(curwin->w_cursor, pos))
3354 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003355 emsg(_(e_complete_function_deleted_text));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003356 goto theend;
3357 }
3358
3359 if (matchlist != NULL)
3360 ins_compl_add_list(matchlist);
3361 else if (matchdict != NULL)
3362 ins_compl_add_dict(matchdict);
3363
3364theend:
3365 // Restore State, it might have been changed.
3366 State = save_State;
3367
3368 if (matchdict != NULL)
3369 dict_unref(matchdict);
3370 if (matchlist != NULL)
3371 list_unref(matchlist);
3372}
3373#endif // FEAT_COMPL_FUNC
3374
3375#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) || defined(PROTO)
glepnir38f99a12024-08-23 18:31:06 +02003376
3377 static inline int
3378get_user_highlight_attr(char_u *hlname)
3379{
3380 if (hlname != NULL && *hlname != NUL)
Hirohito Higashi355db992025-04-28 18:07:02 +02003381 return syn_name2attr(hlname);
glepnir38f99a12024-08-23 18:31:06 +02003382 return -1;
3383}
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003384/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003385 * Add a match to the list of matches from a typeval_T.
3386 * If the given string is already in the list of completions, then return
3387 * NOTDONE, otherwise add it to the list and return OK. If there is an error,
3388 * maybe because alloc() returns NULL, then FAIL is returned.
Bram Moolenaar440cf092021-04-03 20:13:30 +02003389 * When "fast" is TRUE use fast_breakcheck() instead of ui_breakcheck().
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003390 */
3391 static int
Bram Moolenaar440cf092021-04-03 20:13:30 +02003392ins_compl_add_tv(typval_T *tv, int dir, int fast)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003393{
3394 char_u *word;
3395 int dup = FALSE;
3396 int empty = FALSE;
Bram Moolenaar440cf092021-04-03 20:13:30 +02003397 int flags = fast ? CP_FAST : 0;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003398 char_u *(cptext[CPT_COUNT]);
Bram Moolenaar08928322020-01-04 14:32:48 +01003399 typval_T user_data;
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003400 int status;
glepnir0fe17f82024-10-08 22:26:44 +02003401 char_u *user_abbr_hlname;
glepnir38f99a12024-08-23 18:31:06 +02003402 char_u *user_kind_hlname;
glepnir80b66202024-11-27 21:53:53 +01003403 int user_hl[2] = { -1, -1 };
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003404
Bram Moolenaar08928322020-01-04 14:32:48 +01003405 user_data.v_type = VAR_UNKNOWN;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003406 if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL)
3407 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003408 word = dict_get_string(tv->vval.v_dict, "word", FALSE);
3409 cptext[CPT_ABBR] = dict_get_string(tv->vval.v_dict, "abbr", FALSE);
3410 cptext[CPT_MENU] = dict_get_string(tv->vval.v_dict, "menu", FALSE);
3411 cptext[CPT_KIND] = dict_get_string(tv->vval.v_dict, "kind", FALSE);
3412 cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict, "info", FALSE);
glepnir38f99a12024-08-23 18:31:06 +02003413
glepnir0fe17f82024-10-08 22:26:44 +02003414 user_abbr_hlname = dict_get_string(tv->vval.v_dict, "abbr_hlgroup", FALSE);
glepnir80b66202024-11-27 21:53:53 +01003415 user_hl[0] = get_user_highlight_attr(user_abbr_hlname);
glepnir38f99a12024-08-23 18:31:06 +02003416
3417 user_kind_hlname = dict_get_string(tv->vval.v_dict, "kind_hlgroup", FALSE);
glepnir80b66202024-11-27 21:53:53 +01003418 user_hl[1] = get_user_highlight_attr(user_kind_hlname);
glepnir508e7852024-07-25 21:39:08 +02003419
Bram Moolenaard61efa52022-07-23 09:52:04 +01003420 dict_get_tv(tv->vval.v_dict, "user_data", &user_data);
3421 if (dict_get_string(tv->vval.v_dict, "icase", FALSE) != NULL
3422 && dict_get_number(tv->vval.v_dict, "icase"))
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003423 flags |= CP_ICASE;
Bram Moolenaard61efa52022-07-23 09:52:04 +01003424 if (dict_get_string(tv->vval.v_dict, "dup", FALSE) != NULL)
3425 dup = dict_get_number(tv->vval.v_dict, "dup");
3426 if (dict_get_string(tv->vval.v_dict, "empty", FALSE) != NULL)
3427 empty = dict_get_number(tv->vval.v_dict, "empty");
3428 if (dict_get_string(tv->vval.v_dict, "equal", FALSE) != NULL
3429 && dict_get_number(tv->vval.v_dict, "equal"))
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003430 flags |= CP_EQUAL;
3431 }
3432 else
3433 {
3434 word = tv_get_string_chk(tv);
Bram Moolenaara80faa82020-04-12 19:37:17 +02003435 CLEAR_FIELD(cptext);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003436 }
3437 if (word == NULL || (!empty && *word == NUL))
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003438 {
3439 clear_tv(&user_data);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003440 return FAIL;
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003441 }
glepnir508e7852024-07-25 21:39:08 +02003442 status = ins_compl_add(word, -1, NULL, cptext,
glepnirf31cfa22025-03-06 21:59:13 +01003443 &user_data, dir, flags, dup, user_hl, 0);
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003444 if (status != OK)
3445 clear_tv(&user_data);
3446 return status;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003447}
3448
3449/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003450 * Add completions from a list.
3451 */
3452 static void
3453ins_compl_add_list(list_T *list)
3454{
3455 listitem_T *li;
3456 int dir = compl_direction;
3457
3458 // Go through the List with matches and add each of them.
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02003459 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02003460 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003461 {
Bram Moolenaar440cf092021-04-03 20:13:30 +02003462 if (ins_compl_add_tv(&li->li_tv, dir, TRUE) == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003463 // if dir was BACKWARD then honor it just once
3464 dir = FORWARD;
3465 else if (did_emsg)
3466 break;
3467 }
3468}
3469
3470/*
3471 * Add completions from a dict.
3472 */
3473 static void
3474ins_compl_add_dict(dict_T *dict)
3475{
3476 dictitem_T *di_refresh;
3477 dictitem_T *di_words;
3478
3479 // Check for optional "refresh" item.
3480 compl_opt_refresh_always = FALSE;
3481 di_refresh = dict_find(dict, (char_u *)"refresh", 7);
3482 if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING)
3483 {
3484 char_u *v = di_refresh->di_tv.vval.v_string;
3485
3486 if (v != NULL && STRCMP(v, (char_u *)"always") == 0)
3487 compl_opt_refresh_always = TRUE;
3488 }
3489
3490 // Add completions from a "words" list.
3491 di_words = dict_find(dict, (char_u *)"words", 5);
3492 if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST)
3493 ins_compl_add_list(di_words->di_tv.vval.v_list);
3494}
3495
3496/*
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003497 * Start completion for the complete() function.
3498 * "startcol" is where the matched text starts (1 is first column).
3499 * "list" is the list of matches.
3500 */
3501 static void
3502set_completion(colnr_T startcol, list_T *list)
3503{
3504 int save_w_wrow = curwin->w_wrow;
3505 int save_w_leftcol = curwin->w_leftcol;
3506 int flags = CP_ORIGINAL_TEXT;
zeertzjqaa925ee2024-06-09 18:24:05 +02003507 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02003508 int compl_longest = (cur_cot_flags & COT_LONGEST) != 0;
3509 int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
3510 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003511
3512 // If already doing completions stop it.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003513 if (ctrl_x_mode_not_default())
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003514 ins_compl_prep(' ');
3515 ins_compl_clear();
3516 ins_compl_free();
bfredl87af60c2022-09-24 11:17:51 +01003517 compl_get_longest = compl_longest;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003518
3519 compl_direction = FORWARD;
3520 if (startcol > curwin->w_cursor.col)
3521 startcol = curwin->w_cursor.col;
3522 compl_col = startcol;
glepnir76bdb822025-02-08 19:04:51 +01003523 compl_lnum = curwin->w_cursor.lnum;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003524 compl_length = (int)curwin->w_cursor.col - (int)startcol;
3525 // compl_pattern doesn't need to be set
glepniredd4ac32025-01-29 18:53:51 +01003526 compl_orig_text.string = vim_strnsave(ml_get_curline() + compl_col,
3527 (size_t)compl_length);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003528 if (p_ic)
3529 flags |= CP_ICASE;
John Marriott5e6ea922024-11-23 14:01:57 +01003530 if (compl_orig_text.string == NULL)
3531 {
3532 compl_orig_text.length = 0;
3533 return;
3534 }
3535 compl_orig_text.length = (size_t)compl_length;
3536 if (ins_compl_add(compl_orig_text.string,
Hirohito Higashi355db992025-04-28 18:07:02 +02003537 (int)compl_orig_text.length, NULL, NULL, NULL, 0,
3538 flags | CP_FAST, FALSE, NULL, 0) != OK)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003539 return;
3540
3541 ctrl_x_mode = CTRL_X_EVAL;
3542
3543 ins_compl_add_list(list);
3544 compl_matches = ins_compl_make_cyclic();
3545 compl_started = TRUE;
3546 compl_used_match = TRUE;
3547 compl_cont_status = 0;
3548
3549 compl_curr_match = compl_first_match;
bfredl87af60c2022-09-24 11:17:51 +01003550 int no_select = compl_no_select || compl_longest;
3551 if (compl_no_insert || no_select)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003552 {
3553 ins_complete(K_DOWN, FALSE);
bfredl87af60c2022-09-24 11:17:51 +01003554 if (no_select)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003555 // Down/Up has no real effect.
3556 ins_complete(K_UP, FALSE);
3557 }
3558 else
3559 ins_complete(Ctrl_N, FALSE);
3560 compl_enter_selects = compl_no_insert;
3561
3562 // Lazily show the popup menu, unless we got interrupted.
3563 if (!compl_interrupted)
3564 show_pum(save_w_wrow, save_w_leftcol);
LemonBoy2bf52dd2022-04-09 18:17:34 +01003565 may_trigger_modechanged();
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003566 out_flush();
3567}
3568
3569/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003570 * "complete()" function
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003571 */
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003572 void
3573f_complete(typval_T *argvars, typval_T *rettv UNUSED)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003574{
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003575 if (in_vim9script()
3576 && (check_for_number_arg(argvars, 0) == FAIL
3577 || check_for_list_arg(argvars, 1) == FAIL))
3578 return;
3579
Bram Moolenaar24959102022-05-07 20:01:16 +01003580 if ((State & MODE_INSERT) == 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003581 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00003582 emsg(_(e_complete_can_only_be_used_in_insert_mode));
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003583 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003584 }
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003585
3586 // Check for undo allowed here, because if something was already inserted
3587 // the line was already saved for undo and this check isn't done.
3588 if (!undo_allowed())
3589 return;
3590
Bram Moolenaard83392a2022-09-01 12:22:46 +01003591 if (check_for_nonnull_list_arg(argvars, 1) != FAIL)
Bram Moolenaarff06f282020-04-21 22:01:14 +02003592 {
glepnir19e1dd62025-05-08 22:50:38 +02003593 int startcol = (int)tv_get_number_chk(&argvars[0], NULL);
Bram Moolenaarff06f282020-04-21 22:01:14 +02003594 if (startcol > 0)
3595 set_completion(startcol - 1, argvars[1].vval.v_list);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003596 }
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003597}
3598
3599/*
3600 * "complete_add()" function
3601 */
3602 void
3603f_complete_add(typval_T *argvars, typval_T *rettv)
3604{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003605 if (in_vim9script() && check_for_string_or_dict_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003606 return;
3607
Bram Moolenaar440cf092021-04-03 20:13:30 +02003608 rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, FALSE);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003609}
3610
3611/*
3612 * "complete_check()" function
3613 */
3614 void
3615f_complete_check(typval_T *argvars UNUSED, typval_T *rettv)
3616{
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003617 int save_RedrawingDisabled = RedrawingDisabled;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003618 RedrawingDisabled = 0;
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003619
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003620 ins_compl_check_keys(0, TRUE);
3621 rettv->vval.v_number = ins_compl_interrupted();
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003622
3623 RedrawingDisabled = save_RedrawingDisabled;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003624}
3625
3626/*
glepnirbcd59952025-04-24 21:48:35 +02003627 * Add match item to the return list.
3628 * Returns FAIL if out of memory, OK otherwise.
3629 */
3630 static int
3631add_match_to_list(
3632 typval_T *rettv,
3633 char_u *str,
3634 int len,
3635 int pos)
3636{
3637 list_T *match;
3638 int ret;
3639
3640 match = list_alloc();
3641 if (match == NULL)
Hirohito Higashi355db992025-04-28 18:07:02 +02003642 return FAIL;
glepnirbcd59952025-04-24 21:48:35 +02003643
3644 if ((ret = list_append_number(match, pos + 1)) == FAIL
3645 || (ret = list_append_string(match, str, len)) == FAIL
3646 || (ret = list_append_list(rettv->vval.v_list, match)) == FAIL)
3647 {
Hirohito Higashi355db992025-04-28 18:07:02 +02003648 vim_free(match);
3649 return FAIL;
glepnirbcd59952025-04-24 21:48:35 +02003650 }
3651
3652 return OK;
3653}
3654
3655/*
3656 * "complete_match()" function
3657 */
3658 void
3659f_complete_match(typval_T *argvars, typval_T *rettv)
3660{
3661 linenr_T lnum;
3662 colnr_T col;
3663 char_u *line = NULL;
3664 char_u *ise = NULL;
3665 regmatch_T regmatch;
3666 char_u *before_cursor = NULL;
3667 char_u *cur_end = NULL;
glepnirbcd59952025-04-24 21:48:35 +02003668 int bytepos = 0;
3669 char_u part[MAXPATHL];
3670 int ret;
3671
3672 if (rettv_list_alloc(rettv) == FAIL)
3673 return;
3674
3675 ise = curbuf->b_p_ise[0] != NUL ? curbuf->b_p_ise : p_ise;
3676
3677 if (argvars[0].v_type == VAR_UNKNOWN)
3678 {
3679 lnum = curwin->w_cursor.lnum;
3680 col = curwin->w_cursor.col;
3681 }
3682 else if (argvars[1].v_type == VAR_UNKNOWN)
3683 {
3684 emsg(_(e_invalid_argument));
3685 return;
3686 }
3687 else
3688 {
3689 lnum = (linenr_T)tv_get_number(&argvars[0]);
3690 col = (colnr_T)tv_get_number(&argvars[1]);
3691 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3692 {
3693 semsg(_(e_invalid_line_number_nr), lnum);
3694 return;
3695 }
3696 if (col < 1 || col > ml_get_buf_len(curbuf, lnum))
3697 {
3698 semsg(_(e_invalid_column_number_nr), col + 1);
3699 return;
3700 }
3701 }
3702
3703 line = ml_get_buf(curbuf, lnum, FALSE);
3704 if (line == NULL)
3705 return;
3706
3707 before_cursor = vim_strnsave(line, col);
3708 if (before_cursor == NULL)
3709 return;
3710
3711 if (ise == NULL || *ise == NUL)
3712 {
3713 regmatch.regprog = vim_regcomp((char_u *)"\\k\\+$", RE_MAGIC);
3714 if (regmatch.regprog != NULL)
3715 {
3716 if (vim_regexec_nl(&regmatch, before_cursor, (colnr_T)0))
3717 {
Christian Brabandt3accf042025-04-25 19:01:06 +02003718 char_u *trig = vim_strnsave(regmatch.startp[0],
glepnirbcd59952025-04-24 21:48:35 +02003719 regmatch.endp[0] - regmatch.startp[0]);
3720 if (trig == NULL)
3721 {
3722 vim_free(before_cursor);
Christian Brabandt3accf042025-04-25 19:01:06 +02003723 vim_regfree(regmatch.regprog);
glepnirbcd59952025-04-24 21:48:35 +02003724 return;
3725 }
3726
Christian Brabandt3accf042025-04-25 19:01:06 +02003727 bytepos = (int)(regmatch.startp[0] - before_cursor);
glepnirbcd59952025-04-24 21:48:35 +02003728 ret = add_match_to_list(rettv, trig, -1, bytepos);
3729 vim_free(trig);
3730 if (ret == FAIL)
3731 {
Christian Brabandt3accf042025-04-25 19:01:06 +02003732 vim_free(before_cursor);
glepnirbcd59952025-04-24 21:48:35 +02003733 vim_regfree(regmatch.regprog);
3734 return;
3735 }
3736 }
3737 vim_regfree(regmatch.regprog);
3738 }
3739 }
3740 else
3741 {
glepnir08db2f42025-05-14 20:26:19 +02003742 char_u *p = ise;
3743 char_u *p_space = NULL;
3744
glepnirbcd59952025-04-24 21:48:35 +02003745 cur_end = before_cursor + (int)STRLEN(before_cursor);
3746
3747 while (*p != NUL)
3748 {
glepnir8d0e42b2025-05-12 20:28:28 +02003749 int len = 0;
glepnir08db2f42025-05-14 20:26:19 +02003750 if (p_space)
glepnir8d0e42b2025-05-12 20:28:28 +02003751 {
glepnir08db2f42025-05-14 20:26:19 +02003752 len = p - p_space - 1;
3753 memcpy(part, p_space + 1, len);
3754 p_space = NULL;
glepnir8d0e42b2025-05-12 20:28:28 +02003755 }
3756 else
glepnir08db2f42025-05-14 20:26:19 +02003757 {
3758 char_u *next_comma = vim_strchr((*p == ',') ? p + 1 : p, ',');
3759 if (next_comma && *(next_comma + 1) == ' ')
3760 p_space = next_comma;
3761
glepnir8d0e42b2025-05-12 20:28:28 +02003762 len = copy_option_part(&p, part, MAXPATHL, ",");
glepnir08db2f42025-05-14 20:26:19 +02003763 }
glepnirbcd59952025-04-24 21:48:35 +02003764
3765 if (len > 0 && len <= col)
3766 {
3767 if (STRNCMP(cur_end - len, part, len) == 0)
3768 {
3769 bytepos = col - len;
3770 if (add_match_to_list(rettv, part, len, bytepos) == FAIL)
3771 {
3772 vim_free(before_cursor);
3773 return;
3774 }
3775 }
3776 }
3777 }
3778 }
3779
3780 vim_free(before_cursor);
3781}
3782
3783/*
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003784 * Return Insert completion mode name string
3785 */
3786 static char_u *
3787ins_compl_mode(void)
3788{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003789 if (ctrl_x_mode_not_defined_yet() || ctrl_x_mode_scroll() || compl_started)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003790 return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
3791
3792 return (char_u *)"";
3793}
3794
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00003795/*
3796 * Assign the sequence number to all the completion matches which don't have
3797 * one assigned yet.
3798 */
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003799 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00003800ins_compl_update_sequence_numbers(void)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003801{
3802 int number = 0;
3803 compl_T *match;
3804
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003805 if (compl_dir_forward())
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003806 {
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003807 // Search backwards for the first valid (!= -1) number.
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003808 // This should normally succeed already at the first loop
3809 // cycle, so it's fast!
3810 for (match = compl_curr_match->cp_prev; match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003811 && !is_first_match(match); match = match->cp_prev)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003812 if (match->cp_number != -1)
3813 {
3814 number = match->cp_number;
3815 break;
3816 }
3817 if (match != NULL)
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003818 // go up and assign all numbers which are not assigned yet
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003819 for (match = match->cp_next;
3820 match != NULL && match->cp_number == -1;
3821 match = match->cp_next)
3822 match->cp_number = ++number;
3823 }
3824 else // BACKWARD
3825 {
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003826 // Search forwards (upwards) for the first valid (!= -1)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003827 // number. This should normally succeed already at the
3828 // first loop cycle, so it's fast!
3829 for (match = compl_curr_match->cp_next; match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003830 && !is_first_match(match); match = match->cp_next)
glepnir40891ba2025-02-10 22:18:00 +01003831 {
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003832 if (match->cp_number != -1)
3833 {
3834 number = match->cp_number;
3835 break;
3836 }
glepnir40891ba2025-02-10 22:18:00 +01003837 }
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003838 if (match != NULL)
glepnir40891ba2025-02-10 22:18:00 +01003839 {
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003840 // go down and assign all numbers which are not assigned yet
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003841 for (match = match->cp_prev; match
3842 && match->cp_number == -1;
3843 match = match->cp_prev)
3844 match->cp_number = ++number;
glepnir40891ba2025-02-10 22:18:00 +01003845 }
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003846 }
3847}
3848
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003849/*
glepnir037b0282025-01-16 14:37:44 +01003850 * Fill the dict of complete_info
3851 */
3852 static void
3853fill_complete_info_dict(dict_T *di, compl_T *match, int add_match)
3854{
3855 dict_add_string(di, "word", match->cp_str.string);
3856 dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]);
3857 dict_add_string(di, "menu", match->cp_text[CPT_MENU]);
3858 dict_add_string(di, "kind", match->cp_text[CPT_KIND]);
3859 dict_add_string(di, "info", match->cp_text[CPT_INFO]);
3860 if (add_match)
Hirohito Higashi355db992025-04-28 18:07:02 +02003861 dict_add_bool(di, "match", match->cp_in_match_array);
glepnir037b0282025-01-16 14:37:44 +01003862 if (match->cp_user_data.v_type == VAR_UNKNOWN)
Hirohito Higashi355db992025-04-28 18:07:02 +02003863 // Add an empty string for backwards compatibility
3864 dict_add_string(di, "user_data", (char_u *)"");
glepnir037b0282025-01-16 14:37:44 +01003865 else
Hirohito Higashi355db992025-04-28 18:07:02 +02003866 dict_add_tv(di, "user_data", &match->cp_user_data);
glepnir037b0282025-01-16 14:37:44 +01003867}
3868
3869/*
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003870 * Get complete information
3871 */
3872 static void
3873get_complete_info(list_T *what_list, dict_T *retdict)
3874{
3875 int ret = OK;
3876 listitem_T *item;
3877#define CI_WHAT_MODE 0x01
3878#define CI_WHAT_PUM_VISIBLE 0x02
3879#define CI_WHAT_ITEMS 0x04
3880#define CI_WHAT_SELECTED 0x08
glepnir037b0282025-01-16 14:37:44 +01003881#define CI_WHAT_COMPLETED 0x10
glepnird4088ed2024-12-31 10:55:22 +01003882#define CI_WHAT_MATCHES 0x20
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003883#define CI_WHAT_ALL 0xff
3884 int what_flag;
Girish Palya0ac1eb32025-04-16 20:18:33 +02003885 int compl_fuzzy_match = (get_cot_flags() & COT_FUZZY) != 0;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003886
3887 if (what_list == NULL)
glepnir037b0282025-01-16 14:37:44 +01003888 what_flag = CI_WHAT_ALL & ~(CI_WHAT_MATCHES | CI_WHAT_COMPLETED);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003889 else
3890 {
3891 what_flag = 0;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02003892 CHECK_LIST_MATERIALIZE(what_list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02003893 FOR_ALL_LIST_ITEMS(what_list, item)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003894 {
3895 char_u *what = tv_get_string(&item->li_tv);
3896
3897 if (STRCMP(what, "mode") == 0)
3898 what_flag |= CI_WHAT_MODE;
3899 else if (STRCMP(what, "pum_visible") == 0)
3900 what_flag |= CI_WHAT_PUM_VISIBLE;
3901 else if (STRCMP(what, "items") == 0)
3902 what_flag |= CI_WHAT_ITEMS;
3903 else if (STRCMP(what, "selected") == 0)
3904 what_flag |= CI_WHAT_SELECTED;
glepnir037b0282025-01-16 14:37:44 +01003905 else if (STRCMP(what, "completed") == 0)
3906 what_flag |= CI_WHAT_COMPLETED;
glepnird4088ed2024-12-31 10:55:22 +01003907 else if (STRCMP(what, "matches") == 0)
3908 what_flag |= CI_WHAT_MATCHES;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003909 }
3910 }
3911
3912 if (ret == OK && (what_flag & CI_WHAT_MODE))
3913 ret = dict_add_string(retdict, "mode", ins_compl_mode());
3914
3915 if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE))
3916 ret = dict_add_number(retdict, "pum_visible", pum_visible());
3917
glepnir037b0282025-01-16 14:37:44 +01003918 if (ret == OK && (what_flag & (CI_WHAT_ITEMS | CI_WHAT_SELECTED
3919 | CI_WHAT_MATCHES | CI_WHAT_COMPLETED)))
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003920 {
Christian Brabandt9eb236f2024-03-21 20:12:59 +01003921 list_T *li = NULL;
Girish Palya8950bf72024-03-20 20:07:29 +01003922 dict_T *di;
3923 compl_T *match;
3924 int selected_idx = -1;
glepnird4088ed2024-12-31 10:55:22 +01003925 int has_items = what_flag & CI_WHAT_ITEMS;
3926 int has_matches = what_flag & CI_WHAT_MATCHES;
glepnir037b0282025-01-16 14:37:44 +01003927 int has_completed = what_flag & CI_WHAT_COMPLETED;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003928
glepnird4088ed2024-12-31 10:55:22 +01003929 if (has_items || has_matches)
Girish Palya8950bf72024-03-20 20:07:29 +01003930 {
3931 li = list_alloc();
3932 if (li == NULL)
3933 return;
glepnird4088ed2024-12-31 10:55:22 +01003934 ret = dict_add_list(retdict, (has_matches && !has_items)
3935 ? "matches" : "items", li);
Girish Palya8950bf72024-03-20 20:07:29 +01003936 }
3937 if (ret == OK && what_flag & CI_WHAT_SELECTED)
3938 if (compl_curr_match != NULL && compl_curr_match->cp_number == -1)
3939 ins_compl_update_sequence_numbers();
3940 if (ret == OK && compl_first_match != NULL)
3941 {
3942 int list_idx = 0;
3943 match = compl_first_match;
3944 do
3945 {
3946 if (!match_at_original_text(match))
3947 {
glepnird4088ed2024-12-31 10:55:22 +01003948 if (has_items
3949 || (has_matches && match->cp_in_match_array))
Girish Palya8950bf72024-03-20 20:07:29 +01003950 {
3951 di = dict_alloc();
3952 if (di == NULL)
3953 return;
3954 ret = list_append_dict(li, di);
3955 if (ret != OK)
3956 return;
glepnir037b0282025-01-16 14:37:44 +01003957 fill_complete_info_dict(di, match, has_matches && has_items);
Girish Palya8950bf72024-03-20 20:07:29 +01003958 }
glepnird4088ed2024-12-31 10:55:22 +01003959 if (compl_curr_match != NULL
3960 && compl_curr_match->cp_number == match->cp_number)
Girish Palya8950bf72024-03-20 20:07:29 +01003961 selected_idx = list_idx;
Girish Palya0ac1eb32025-04-16 20:18:33 +02003962 if (compl_fuzzy_match || match->cp_in_match_array)
3963 list_idx += 1;
Girish Palya8950bf72024-03-20 20:07:29 +01003964 }
3965 match = match->cp_next;
3966 }
3967 while (match != NULL && !is_first_match(match));
3968 }
3969 if (ret == OK && (what_flag & CI_WHAT_SELECTED))
3970 ret = dict_add_number(retdict, "selected", selected_idx);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003971
glepnir037b0282025-01-16 14:37:44 +01003972 if (ret == OK && selected_idx != -1 && has_completed)
3973 {
3974 di = dict_alloc();
3975 if (di == NULL)
3976 return;
3977 fill_complete_info_dict(di, compl_curr_match, FALSE);
3978 ret = dict_add_dict(retdict, "completed", di);
3979 }
Yegappan Lakshmanan6b085b92022-09-04 12:47:21 +01003980 }
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003981}
3982
3983/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003984 * "complete_info()" function
3985 */
3986 void
3987f_complete_info(typval_T *argvars, typval_T *rettv)
3988{
3989 list_T *what_list = NULL;
3990
Bram Moolenaar93a10962022-06-16 11:42:09 +01003991 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003992 return;
3993
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003994 if (in_vim9script() && check_for_opt_list_arg(argvars, 0) == FAIL)
3995 return;
3996
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003997 if (argvars[0].v_type != VAR_UNKNOWN)
3998 {
Bram Moolenaard83392a2022-09-01 12:22:46 +01003999 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02004000 return;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02004001 what_list = argvars[0].vval.v_list;
4002 }
4003 get_complete_info(what_list, rettv->vval.v_dict);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004004}
4005#endif
4006
4007/*
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01004008 * Returns TRUE when using a user-defined function for thesaurus completion.
4009 */
4010 static int
4011thesaurus_func_complete(int type UNUSED)
4012{
4013#ifdef FEAT_COMPL_FUNC
Bram Moolenaarf4d8b762021-10-17 14:13:09 +01004014 return type == CTRL_X_THESAURUS
4015 && (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL);
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01004016#else
4017 return FALSE;
4018#endif
4019}
4020
4021/*
Girish Palya7c621052025-05-26 19:41:59 +02004022 * Check if 'cpt' list index can be advanced to the next completion source.
4023 */
4024 static int
4025may_advance_cpt_index(char_u *cpt)
4026{
4027 char_u *p = cpt;
4028
4029 while (*p == ',' || *p == ' ') // Skip delimiters
4030 p++;
4031 return (*p != NUL);
4032}
4033
4034/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004035 * Return value of process_next_cpt_value()
4036 */
4037enum
4038{
4039 INS_COMPL_CPT_OK = 1,
4040 INS_COMPL_CPT_CONT,
4041 INS_COMPL_CPT_END
4042};
4043
4044/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004045 * state information used for getting the next set of insert completion
4046 * matches.
4047 */
4048typedef struct
4049{
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004050 char_u *e_cpt_copy; // copy of 'complete'
4051 char_u *e_cpt; // current entry in "e_cpt_copy"
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004052 buf_T *ins_buf; // buffer being scanned
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004053 pos_T *cur_match_pos; // current match position
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004054 pos_T prev_match_pos; // previous match position
4055 int set_match_pos; // save first_match_pos/last_match_pos
4056 pos_T first_match_pos; // first match position
4057 pos_T last_match_pos; // last match position
4058 int found_all; // found all matches of a certain type.
4059 char_u *dict; // dictionary file to search
4060 int dict_f; // "dict" is an exact file name or not
Girish Palyacbe53192025-04-14 22:13:15 +02004061 callback_T *func_cb; // callback of function in 'cpt' option
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004062} ins_compl_next_state_T;
4063
4064/*
4065 * Process the next 'complete' option value in st->e_cpt.
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004066 *
4067 * If successful, the arguments are set as below:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004068 * st->cpt - pointer to the next option value in "st->cpt"
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004069 * compl_type_arg - type of insert mode completion to use
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004070 * st->found_all - all matches of this type are found
4071 * st->ins_buf - search for completions in this buffer
4072 * st->first_match_pos - position of the first completion match
4073 * st->last_match_pos - position of the last completion match
4074 * st->set_match_pos - TRUE if the first match position should be saved to
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01004075 * avoid loops after the search wraps around.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004076 * st->dict - name of the dictionary or thesaurus file to search
4077 * st->dict_f - flag specifying whether "dict" is an exact file name or not
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004078 *
4079 * Returns INS_COMPL_CPT_OK if the next value is processed successfully.
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00004080 * Returns INS_COMPL_CPT_CONT to skip the current completion source matching
4081 * the "st->e_cpt" option value and process the next matching source.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004082 * Returns INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004083 */
4084 static int
4085process_next_cpt_value(
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004086 ins_compl_next_state_T *st,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004087 int *compl_type_arg,
glepnir8159fb12024-07-17 20:32:54 +02004088 pos_T *start_match_pos,
Girish Palya7c621052025-05-26 19:41:59 +02004089 int fuzzy_collect,
4090 int *advance_cpt_idx)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004091{
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004092 int compl_type = -1;
4093 int status = INS_COMPL_CPT_OK;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004094
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004095 st->found_all = FALSE;
Girish Palya7c621052025-05-26 19:41:59 +02004096 *advance_cpt_idx = FALSE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004097
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004098 while (*st->e_cpt == ',' || *st->e_cpt == ' ')
4099 st->e_cpt++;
4100
4101 if (*st->e_cpt == '.' && !curbuf->b_scanned)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004102 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004103 st->ins_buf = curbuf;
4104 st->first_match_pos = *start_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004105 // Move the cursor back one character so that ^N can match the
4106 // word immediately after the cursor.
glepnir53b14572025-03-12 21:28:39 +01004107 if (ctrl_x_mode_normal() && (!fuzzy_collect && dec(&st->first_match_pos) < 0))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004108 {
4109 // Move the cursor to after the last character in the
4110 // buffer, so that word at start of buffer is found
4111 // correctly.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004112 st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count;
zeertzjq94b7c322024-03-12 21:50:32 +01004113 st->first_match_pos.col = ml_get_len(st->first_match_pos.lnum);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004114 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004115 st->last_match_pos = st->first_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004116 compl_type = 0;
4117
4118 // Remember the first match so that the loop stops when we
4119 // wrap and come back there a second time.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004120 st->set_match_pos = TRUE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004121 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004122 else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004123 && (st->ins_buf = ins_compl_next_buf(
4124 st->ins_buf, *st->e_cpt)) != curbuf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004125 {
4126 // Scan a buffer, but not the current one.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004127 if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004128 {
4129 compl_started = TRUE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004130 st->first_match_pos.col = st->last_match_pos.col = 0;
4131 st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1;
4132 st->last_match_pos.lnum = 0;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004133 compl_type = 0;
4134 }
4135 else // unloaded buffer, scan like dictionary
4136 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004137 st->found_all = TRUE;
4138 if (st->ins_buf->b_fname == NULL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004139 {
4140 status = INS_COMPL_CPT_CONT;
4141 goto done;
4142 }
4143 compl_type = CTRL_X_DICTIONARY;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004144 st->dict = st->ins_buf->b_fname;
4145 st->dict_f = DICT_EXACT;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004146 }
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01004147 if (!shortmess(SHM_COMPLETIONSCAN))
4148 {
4149 msg_hist_off = TRUE; // reset in msg_trunc_attr()
4150 vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
4151 st->ins_buf->b_fname == NULL
4152 ? buf_spname(st->ins_buf)
4153 : st->ins_buf->b_sfname == NULL
4154 ? st->ins_buf->b_fname
4155 : st->ins_buf->b_sfname);
4156 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
4157 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004158 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004159 else if (*st->e_cpt == NUL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004160 status = INS_COMPL_CPT_END;
4161 else
4162 {
4163 if (ctrl_x_mode_line_or_eval())
4164 compl_type = -1;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004165 else if (*st->e_cpt == 'k' || *st->e_cpt == 's')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004166 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004167 if (*st->e_cpt == 'k')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004168 compl_type = CTRL_X_DICTIONARY;
4169 else
4170 compl_type = CTRL_X_THESAURUS;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004171 if (*++st->e_cpt != ',' && *st->e_cpt != NUL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004172 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004173 st->dict = st->e_cpt;
4174 st->dict_f = DICT_FIRST;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004175 }
4176 }
Girish Palyacbe53192025-04-14 22:13:15 +02004177#ifdef FEAT_COMPL_FUNC
Girish Palya14f6da52025-05-26 19:04:25 +02004178 else if (*st->e_cpt == 'F' || *st->e_cpt == 'o')
Girish Palyacbe53192025-04-14 22:13:15 +02004179 {
4180 compl_type = CTRL_X_FUNCTION;
4181 if (*st->e_cpt == 'o')
4182 st->func_cb = &curbuf->b_ofu_cb;
4183 else
4184 st->func_cb = (*++st->e_cpt != ',' && *st->e_cpt != NUL)
4185 ? get_cpt_func_callback(st->e_cpt) : &curbuf->b_cfu_cb;
4186 if (!st->func_cb)
4187 compl_type = -1;
4188 }
4189#endif
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004190#ifdef FEAT_FIND_ID
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004191 else if (*st->e_cpt == 'i')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004192 compl_type = CTRL_X_PATH_PATTERNS;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004193 else if (*st->e_cpt == 'd')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004194 compl_type = CTRL_X_PATH_DEFINES;
4195#endif
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004196 else if (*st->e_cpt == ']' || *st->e_cpt == 't')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004197 {
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004198 compl_type = CTRL_X_TAGS;
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01004199 if (!shortmess(SHM_COMPLETIONSCAN))
4200 {
4201 msg_hist_off = TRUE; // reset in msg_trunc_attr()
4202 vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags."));
4203 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
4204 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004205 }
4206 else
4207 compl_type = -1;
4208
4209 // in any case e_cpt is advanced to the next entry
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004210 (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ",");
Girish Palya7c621052025-05-26 19:41:59 +02004211 *advance_cpt_idx = may_advance_cpt_index(st->e_cpt);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004212
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004213 st->found_all = TRUE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004214 if (compl_type == -1)
4215 status = INS_COMPL_CPT_CONT;
4216 }
4217
4218done:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004219 *compl_type_arg = compl_type;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004220 return status;
4221}
4222
4223#ifdef FEAT_FIND_ID
4224/*
4225 * Get the next set of identifiers or defines matching "compl_pattern" in
4226 * included files.
4227 */
4228 static void
4229get_next_include_file_completion(int compl_type)
4230{
John Marriott5e6ea922024-11-23 14:01:57 +01004231 find_pattern_in_path(compl_pattern.string, compl_direction,
4232 (int)compl_pattern.length, FALSE, FALSE,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004233 (compl_type == CTRL_X_PATH_DEFINES
4234 && !(compl_cont_status & CONT_SOL))
4235 ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND,
Colin Kennedy21570352024-03-03 16:16:47 +01004236 (linenr_T)1, (linenr_T)MAXLNUM, FALSE);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004237}
4238#endif
4239
4240/*
4241 * Get the next set of words matching "compl_pattern" in dictionary or
4242 * thesaurus files.
4243 */
4244 static void
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004245get_next_dict_tsr_completion(int compl_type, char_u *dict, int dict_f)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004246{
4247#ifdef FEAT_COMPL_FUNC
4248 if (thesaurus_func_complete(compl_type))
Girish Palyacbe53192025-04-14 22:13:15 +02004249 expand_by_function(compl_type, compl_pattern.string, NULL);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004250 else
4251#endif
4252 ins_compl_dictionaries(
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004253 dict != NULL ? dict
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004254 : (compl_type == CTRL_X_THESAURUS
4255 ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
4256 : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
John Marriott5e6ea922024-11-23 14:01:57 +01004257 compl_pattern.string,
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004258 dict != NULL ? dict_f : 0,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004259 compl_type == CTRL_X_THESAURUS);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004260}
4261
4262/*
4263 * Get the next set of tag names matching "compl_pattern".
4264 */
4265 static void
4266get_next_tag_completion(void)
4267{
4268 int save_p_ic;
4269 char_u **matches;
4270 int num_matches;
4271
4272 // set p_ic according to p_ic, p_scs and pat for find_tags().
4273 save_p_ic = p_ic;
John Marriott5e6ea922024-11-23 14:01:57 +01004274 p_ic = ignorecase(compl_pattern.string);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004275
4276 // Find up to TAG_MANY matches. Avoids that an enormous number
4277 // of matches is found when compl_pattern is empty
4278 g_tag_at_cursor = TRUE;
John Marriott5e6ea922024-11-23 14:01:57 +01004279 if (find_tags(compl_pattern.string, &num_matches, &matches,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004280 TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004281 | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004282 TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0)
4283 ins_compl_add_matches(num_matches, matches, p_ic);
4284 g_tag_at_cursor = FALSE;
4285 p_ic = save_p_ic;
4286}
4287
4288/*
glepnir8159fb12024-07-17 20:32:54 +02004289 * Compare function for qsort
4290 */
glepnirf31cfa22025-03-06 21:59:13 +01004291 static int
4292compare_scores(const void *a, const void *b)
glepnir8159fb12024-07-17 20:32:54 +02004293{
4294 int idx_a = *(const int *)a;
4295 int idx_b = *(const int *)b;
4296 int score_a = compl_fuzzy_scores[idx_a];
4297 int score_b = compl_fuzzy_scores[idx_b];
zeertzjq58d70522024-08-31 17:05:39 +02004298 return score_a == score_b ? (idx_a == idx_b ? 0 : (idx_a < idx_b ? -1 : 1))
4299 : (score_a > score_b ? -1 : 1);
glepnir8159fb12024-07-17 20:32:54 +02004300}
4301
4302/*
glepnirf31cfa22025-03-06 21:59:13 +01004303 * insert prefix with redraw
4304 */
4305 static void
4306ins_compl_longest_insert(char_u *prefix)
4307{
4308 ins_compl_delete();
4309 ins_compl_insert_bytes(prefix + get_compl_len(), -1);
4310 ins_redraw(FALSE);
4311}
4312
4313/*
4314 * Calculate the longest common prefix among the best fuzzy matches
4315 * stored in compl_best_matches, and insert it as the longest.
4316 */
4317 static void
4318fuzzy_longest_match(void)
4319{
4320 char_u *prefix = NULL;
4321 int prefix_len = 0;
4322 int i = 0;
4323 int j = 0;
4324 char_u *match_str = NULL;
4325 char_u *prefix_ptr = NULL;
4326 char_u *match_ptr = NULL;
4327 char_u *leader = NULL;
4328 size_t leader_len = 0;
4329 compl_T *compl = NULL;
4330 int more_candidates = FALSE;
4331 compl_T *nn_compl = NULL;
4332
4333 if (compl_num_bests == 0)
Hirohito Higashi355db992025-04-28 18:07:02 +02004334 return;
glepnirf31cfa22025-03-06 21:59:13 +01004335
4336 nn_compl = compl_first_match->cp_next->cp_next;
4337 if (nn_compl && nn_compl != compl_first_match)
4338 more_candidates = TRUE;
4339
4340 compl = ctrl_x_mode_whole_line() ? compl_first_match
4341 : compl_first_match->cp_next;
4342 if (compl_num_bests == 1)
4343 {
4344 // no more candidates insert the match str
4345 if (!more_candidates)
4346 {
4347 ins_compl_longest_insert(compl->cp_str.string);
4348 compl_num_bests = 0;
4349 }
4350 compl_num_bests = 0;
4351 return;
4352 }
4353
4354 compl_best_matches = (compl_T **)alloc(compl_num_bests * sizeof(compl_T *));
4355 if (compl_best_matches == NULL)
Hirohito Higashi355db992025-04-28 18:07:02 +02004356 return;
glepnirf31cfa22025-03-06 21:59:13 +01004357 while (compl != NULL && i < compl_num_bests)
4358 {
Hirohito Higashi355db992025-04-28 18:07:02 +02004359 compl_best_matches[i] = compl;
4360 compl = compl->cp_next;
4361 i++;
glepnirf31cfa22025-03-06 21:59:13 +01004362 }
4363
4364 prefix = compl_best_matches[0]->cp_str.string;
zeertzjq4422de62025-03-07 19:06:02 +01004365 prefix_len = (int)compl_best_matches[0]->cp_str.length;
glepnirf31cfa22025-03-06 21:59:13 +01004366
4367 for (i = 1; i < compl_num_bests; i++)
4368 {
4369 match_str = compl_best_matches[i]->cp_str.string;
Hirohito Higashi355db992025-04-28 18:07:02 +02004370 prefix_ptr = prefix;
4371 match_ptr = match_str;
4372 j = 0;
glepnirf31cfa22025-03-06 21:59:13 +01004373
4374 while (j < prefix_len && *match_ptr != NUL && *prefix_ptr != NUL)
4375 {
4376 if (STRNCMP(prefix_ptr, match_ptr, mb_ptr2len(prefix_ptr)) != 0)
4377 break;
4378
4379 MB_PTR_ADV(prefix_ptr);
4380 MB_PTR_ADV(match_ptr);
4381 j++;
4382 }
4383
4384 if (j > 0)
4385 prefix_len = j;
4386 }
4387
4388 leader = ins_compl_leader();
zeertzjq4422de62025-03-07 19:06:02 +01004389 leader_len = ins_compl_leader_len();
glepnirf31cfa22025-03-06 21:59:13 +01004390
4391 // skip non-consecutive prefixes
zeertzjq4422de62025-03-07 19:06:02 +01004392 if (leader_len > 0 && STRNCMP(prefix, leader, leader_len) != 0)
glepnirf31cfa22025-03-06 21:59:13 +01004393 goto end;
4394
zeertzjq4422de62025-03-07 19:06:02 +01004395 prefix = vim_strnsave(prefix, prefix_len);
glepnirf31cfa22025-03-06 21:59:13 +01004396 if (prefix != NULL)
4397 {
4398 ins_compl_longest_insert(prefix);
4399 compl_cfc_longest_ins = TRUE;
4400 vim_free(prefix);
4401 }
4402
4403end:
4404 vim_free(compl_best_matches);
4405 compl_best_matches = NULL;
4406 compl_num_bests = 0;
4407}
4408
4409/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004410 * Get the next set of filename matching "compl_pattern".
4411 */
4412 static void
4413get_next_filename_completion(void)
4414{
4415 char_u **matches;
4416 int num_matches;
glepnir8159fb12024-07-17 20:32:54 +02004417 char_u *ptr;
4418 garray_T fuzzy_indices;
4419 int i;
4420 int score;
4421 char_u *leader = ins_compl_leader();
John Marriott5e6ea922024-11-23 14:01:57 +01004422 size_t leader_len = ins_compl_leader_len();;
glepnirf31cfa22025-03-06 21:59:13 +01004423 int in_fuzzy_collect = (cfc_has_mode() && leader_len > 0);
glepnir8159fb12024-07-17 20:32:54 +02004424 int *fuzzy_indices_data;
glepnir0be03e12024-07-19 16:45:05 +02004425 char_u *last_sep = NULL;
glepnirf31cfa22025-03-06 21:59:13 +01004426 int need_collect_bests = in_fuzzy_collect && compl_get_longest;
4427 int max_score = 0;
4428 int current_score = 0;
4429 int dir = compl_direction;
glepnir8159fb12024-07-17 20:32:54 +02004430
glepnirb9de1a02024-08-02 19:14:38 +02004431#ifdef BACKSLASH_IN_FILENAME
4432 char pathsep = (curbuf->b_p_csl[0] == 's') ?
4433 '/' : (curbuf->b_p_csl[0] == 'b') ? '\\' : PATHSEP;
4434#else
4435 char pathsep = PATHSEP;
4436#endif
4437
glepnirf31cfa22025-03-06 21:59:13 +01004438 if (in_fuzzy_collect)
glepnir8159fb12024-07-17 20:32:54 +02004439 {
glepnirb9de1a02024-08-02 19:14:38 +02004440#ifdef BACKSLASH_IN_FILENAME
4441 if (curbuf->b_p_csl[0] == 's')
4442 {
John Marriott5e6ea922024-11-23 14:01:57 +01004443 for (i = 0; i < (int)leader_len; i++)
glepnirb9de1a02024-08-02 19:14:38 +02004444 {
4445 if (leader[i] == '\\')
4446 leader[i] = '/';
4447 }
4448 }
4449 else if (curbuf->b_p_csl[0] == 'b')
4450 {
John Marriott5e6ea922024-11-23 14:01:57 +01004451 for (i = 0; i < (int)leader_len; i++)
glepnirb9de1a02024-08-02 19:14:38 +02004452 {
4453 if (leader[i] == '/')
4454 leader[i] = '\\';
4455 }
4456 }
4457#endif
4458 last_sep = vim_strrchr(leader, pathsep);
glepnir0be03e12024-07-19 16:45:05 +02004459 if (last_sep == NULL)
4460 {
4461 // No path separator or separator is the last character,
4462 // fuzzy match the whole leader
John Marriott5e6ea922024-11-23 14:01:57 +01004463 VIM_CLEAR_STRING(compl_pattern);
4464 compl_pattern.string = vim_strnsave((char_u *)"*", 1);
4465 if (compl_pattern.string == NULL)
4466 return;
4467 compl_pattern.length = 1;
glepnir0be03e12024-07-19 16:45:05 +02004468 }
4469 else if (*(last_sep + 1) == '\0')
glepnirf31cfa22025-03-06 21:59:13 +01004470 in_fuzzy_collect = FALSE;
glepnir0be03e12024-07-19 16:45:05 +02004471 else
4472 {
4473 // Split leader into path and file parts
4474 int path_len = last_sep - leader + 1;
John Marriott5e6ea922024-11-23 14:01:57 +01004475 char_u *path_with_wildcard;
4476
4477 path_with_wildcard = alloc(path_len + 2);
glepnir0be03e12024-07-19 16:45:05 +02004478 if (path_with_wildcard != NULL)
4479 {
John Marriott5e6ea922024-11-23 14:01:57 +01004480 vim_snprintf((char *)path_with_wildcard, path_len + 2, "%*.*s*", path_len, path_len, leader);
4481 VIM_CLEAR_STRING(compl_pattern);
4482 compl_pattern.string = path_with_wildcard;
4483 compl_pattern.length = path_len + 1;
glepnir0be03e12024-07-19 16:45:05 +02004484
4485 // Move leader to the file part
4486 leader = last_sep + 1;
John Marriott5e6ea922024-11-23 14:01:57 +01004487 leader_len -= path_len;
glepnir0be03e12024-07-19 16:45:05 +02004488 }
4489 }
glepnir8159fb12024-07-17 20:32:54 +02004490 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004491
John Marriott5e6ea922024-11-23 14:01:57 +01004492 if (expand_wildcards(1, &compl_pattern.string, &num_matches, &matches,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004493 EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK)
4494 return;
4495
4496 // May change home directory back to "~".
John Marriott5e6ea922024-11-23 14:01:57 +01004497 tilde_replace(compl_pattern.string, num_matches, matches);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004498#ifdef BACKSLASH_IN_FILENAME
4499 if (curbuf->b_p_csl[0] != NUL)
4500 {
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004501 for (i = 0; i < num_matches; ++i)
4502 {
glepnir8159fb12024-07-17 20:32:54 +02004503 ptr = matches[i];
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004504 while (*ptr != NUL)
4505 {
4506 if (curbuf->b_p_csl[0] == 's' && *ptr == '\\')
4507 *ptr = '/';
4508 else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/')
4509 *ptr = '\\';
4510 ptr += (*mb_ptr2len)(ptr);
4511 }
4512 }
4513 }
4514#endif
glepnir8159fb12024-07-17 20:32:54 +02004515
glepnirf31cfa22025-03-06 21:59:13 +01004516 if (in_fuzzy_collect)
glepnir8159fb12024-07-17 20:32:54 +02004517 {
4518 ga_init2(&fuzzy_indices, sizeof(int), 10);
4519 compl_fuzzy_scores = (int *)alloc(sizeof(int) * num_matches);
4520
4521 for (i = 0; i < num_matches; i++)
4522 {
4523 ptr = matches[i];
4524 score = fuzzy_match_str(ptr, leader);
4525 if (score > 0)
4526 {
4527 if (ga_grow(&fuzzy_indices, 1) == OK)
4528 {
4529 ((int *)fuzzy_indices.ga_data)[fuzzy_indices.ga_len] = i;
4530 compl_fuzzy_scores[i] = score;
4531 fuzzy_indices.ga_len++;
4532 }
4533 }
4534 }
4535
Christian Brabandt220474d2024-07-20 13:26:44 +02004536 // prevent qsort from deref NULL pointer
4537 if (fuzzy_indices.ga_len > 0)
4538 {
glepnirf31cfa22025-03-06 21:59:13 +01004539 char_u *match = NULL;
Christian Brabandt220474d2024-07-20 13:26:44 +02004540 fuzzy_indices_data = (int *)fuzzy_indices.ga_data;
4541 qsort(fuzzy_indices_data, fuzzy_indices.ga_len, sizeof(int), compare_scores);
glepnir8159fb12024-07-17 20:32:54 +02004542
Christian Brabandt220474d2024-07-20 13:26:44 +02004543 for (i = 0; i < fuzzy_indices.ga_len; ++i)
glepnirf31cfa22025-03-06 21:59:13 +01004544 {
Hirohito Higashi355db992025-04-28 18:07:02 +02004545 match = matches[fuzzy_indices_data[i]];
glepnirf31cfa22025-03-06 21:59:13 +01004546 current_score = compl_fuzzy_scores[fuzzy_indices_data[i]];
4547 if (ins_compl_add(match, -1, NULL, NULL, NULL, dir,
Hirohito Higashi355db992025-04-28 18:07:02 +02004548 CP_FAST | ((p_fic || p_wic) ? CP_ICASE : 0),
4549 FALSE, NULL, current_score) == OK)
glepnirf31cfa22025-03-06 21:59:13 +01004550 dir = FORWARD;
4551
4552 if (need_collect_bests)
4553 {
4554 if (i == 0 || current_score == max_score)
4555 {
4556 compl_num_bests++;
4557 max_score = current_score;
4558 }
4559 }
4560 }
glepnir8159fb12024-07-17 20:32:54 +02004561
Christian Brabandt220474d2024-07-20 13:26:44 +02004562 FreeWild(num_matches, matches);
Christian Brabandt220474d2024-07-20 13:26:44 +02004563 }
glepnir6b6280c2024-07-27 16:25:45 +02004564 else if (leader_len > 0)
4565 {
4566 FreeWild(num_matches, matches);
4567 num_matches = 0;
4568 }
Christian Brabandt220474d2024-07-20 13:26:44 +02004569
glepnir8159fb12024-07-17 20:32:54 +02004570 vim_free(compl_fuzzy_scores);
4571 ga_clear(&fuzzy_indices);
glepnirf31cfa22025-03-06 21:59:13 +01004572
4573 if (compl_num_bests > 0 && compl_get_longest)
4574 fuzzy_longest_match();
4575 return;
glepnir8159fb12024-07-17 20:32:54 +02004576 }
4577
glepnir6b6280c2024-07-27 16:25:45 +02004578 if (num_matches > 0)
4579 ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004580}
4581
4582/*
4583 * Get the next set of command-line completions matching "compl_pattern".
4584 */
4585 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00004586get_next_cmdline_completion(void)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004587{
4588 char_u **matches;
4589 int num_matches;
4590
John Marriott5e6ea922024-11-23 14:01:57 +01004591 if (expand_cmdline(&compl_xp, compl_pattern.string,
4592 (int)compl_pattern.length, &num_matches, &matches) == EXPAND_OK)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004593 ins_compl_add_matches(num_matches, matches, FALSE);
4594}
4595
4596/*
4597 * Get the next set of spell suggestions matching "compl_pattern".
4598 */
4599 static void
4600get_next_spell_completion(linenr_T lnum UNUSED)
4601{
4602#ifdef FEAT_SPELL
4603 char_u **matches;
4604 int num_matches;
4605
John Marriott5e6ea922024-11-23 14:01:57 +01004606 num_matches = expand_spelling(lnum, compl_pattern.string, &matches);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004607 if (num_matches > 0)
4608 ins_compl_add_matches(num_matches, matches, p_ic);
Bram Moolenaar8e7cc6b2021-12-30 10:32:25 +00004609 else
4610 vim_free(matches);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004611#endif
4612}
4613
4614/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004615 * Return the next word or line from buffer "ins_buf" at position
4616 * "cur_match_pos" for completion. The length of the match is set in "len".
4617 */
4618 static char_u *
zeertzjq440d4cb2023-03-02 17:51:32 +00004619ins_compl_get_next_word_or_line(
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004620 buf_T *ins_buf, // buffer being scanned
4621 pos_T *cur_match_pos, // current match position
4622 int *match_len,
4623 int *cont_s_ipos) // next ^X<> will set initial_pos
4624{
4625 char_u *ptr;
4626 int len;
4627
4628 *match_len = 0;
4629 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) +
4630 cur_match_pos->col;
John Marriott5e6ea922024-11-23 14:01:57 +01004631 len = (int)ml_get_buf_len(ins_buf, cur_match_pos->lnum) - cur_match_pos->col;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004632 if (ctrl_x_mode_line_or_eval())
4633 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004634 if (compl_status_adding())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004635 {
4636 if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count)
4637 return NULL;
4638 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
John Marriott5e6ea922024-11-23 14:01:57 +01004639 len = ml_get_buf_len(ins_buf, cur_match_pos->lnum + 1);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004640 if (!p_paste)
John Marriott5e6ea922024-11-23 14:01:57 +01004641 {
4642 char_u *tmp_ptr = ptr;
4643
4644 ptr = skipwhite(tmp_ptr);
4645 len -= (int)(ptr - tmp_ptr);
4646 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004647 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004648 }
4649 else
4650 {
4651 char_u *tmp_ptr = ptr;
4652
John Marriott5e6ea922024-11-23 14:01:57 +01004653 if (compl_status_adding() && compl_length <= len)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004654 {
4655 tmp_ptr += compl_length;
4656 // Skip if already inside a word.
4657 if (vim_iswordp(tmp_ptr))
4658 return NULL;
4659 // Find start of next word.
4660 tmp_ptr = find_word_start(tmp_ptr);
4661 }
4662 // Find end of this word.
4663 tmp_ptr = find_word_end(tmp_ptr);
4664 len = (int)(tmp_ptr - ptr);
4665
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004666 if (compl_status_adding() && len == compl_length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004667 {
4668 if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count)
4669 {
4670 // Try next line, if any. the new word will be
4671 // "join" as if the normal command "J" was used.
4672 // IOSIZE is always greater than
4673 // compl_length, so the next STRNCPY always
4674 // works -- Acevedo
4675 STRNCPY(IObuff, ptr, len);
4676 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
4677 tmp_ptr = ptr = skipwhite(ptr);
4678 // Find start of next word.
4679 tmp_ptr = find_word_start(tmp_ptr);
4680 // Find end of next word.
4681 tmp_ptr = find_word_end(tmp_ptr);
4682 if (tmp_ptr > ptr)
4683 {
4684 if (*ptr != ')' && IObuff[len - 1] != TAB)
4685 {
4686 if (IObuff[len - 1] != ' ')
4687 IObuff[len++] = ' ';
4688 // IObuf =~ "\k.* ", thus len >= 2
4689 if (p_js
4690 && (IObuff[len - 2] == '.'
4691 || (vim_strchr(p_cpo, CPO_JOINSP)
4692 == NULL
4693 && (IObuff[len - 2] == '?'
4694 || IObuff[len - 2] == '!'))))
4695 IObuff[len++] = ' ';
4696 }
4697 // copy as much as possible of the new word
4698 if (tmp_ptr - ptr >= IOSIZE - len)
4699 tmp_ptr = ptr + IOSIZE - len - 1;
4700 STRNCPY(IObuff + len, ptr, tmp_ptr - ptr);
4701 len += (int)(tmp_ptr - ptr);
4702 *cont_s_ipos = TRUE;
4703 }
4704 IObuff[len] = NUL;
4705 ptr = IObuff;
4706 }
4707 if (len == compl_length)
4708 return NULL;
4709 }
4710 }
4711
4712 *match_len = len;
4713 return ptr;
4714}
4715
4716/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004717 * Get the next set of words matching "compl_pattern" for default completion(s)
4718 * (normal ^P/^N and ^X^L).
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004719 * Search for "compl_pattern" in the buffer "st->ins_buf" starting from the
4720 * position "st->start_pos" in the "compl_direction" direction. If
4721 * "st->set_match_pos" is TRUE, then set the "st->first_match_pos" and
4722 * "st->last_match_pos".
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004723 * Returns OK if a new next match is found, otherwise returns FAIL.
4724 */
4725 static int
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004726get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004727{
4728 int found_new_match = FAIL;
4729 int save_p_scs;
4730 int save_p_ws;
4731 int looped_around = FALSE;
glepnir8159fb12024-07-17 20:32:54 +02004732 char_u *ptr = NULL;
4733 int len = 0;
glepnirf31cfa22025-03-06 21:59:13 +01004734 int in_collect = (cfc_has_mode() && compl_length > 0);
glepnir8159fb12024-07-17 20:32:54 +02004735 char_u *leader = ins_compl_leader();
glepnirf31cfa22025-03-06 21:59:13 +01004736 int score = 0;
Girish Palyab1565882025-04-15 20:16:00 +02004737 int in_curbuf = st->ins_buf == curbuf;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004738
4739 // If 'infercase' is set, don't use 'smartcase' here
4740 save_p_scs = p_scs;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004741 if (st->ins_buf->b_p_inf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004742 p_scs = FALSE;
4743
4744 // Buffers other than curbuf are scanned from the beginning or the
4745 // end but never from the middle, thus setting nowrapscan in this
4746 // buffer is a good idea, on the other hand, we always set
4747 // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
4748 save_p_ws = p_ws;
Girish Palyab1565882025-04-15 20:16:00 +02004749 if (!in_curbuf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004750 p_ws = FALSE;
glepnirf31cfa22025-03-06 21:59:13 +01004751 else if (*st->e_cpt == '.')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004752 p_ws = TRUE;
4753 looped_around = FALSE;
4754 for (;;)
4755 {
4756 int cont_s_ipos = FALSE;
4757
4758 ++msg_silent; // Don't want messages for wrapscan.
4759
glepnirf31cfa22025-03-06 21:59:13 +01004760 if (in_collect)
4761 {
Hirohito Higashi355db992025-04-28 18:07:02 +02004762 found_new_match = search_for_fuzzy_match(st->ins_buf,
glepnir8159fb12024-07-17 20:32:54 +02004763 st->cur_match_pos, leader, compl_direction,
glepnirf31cfa22025-03-06 21:59:13 +01004764 start_pos, &len, &ptr, &score);
4765 }
4766 // ctrl_x_mode_line_or_eval() || word-wise search that
4767 // has added a word that was at the beginning of the line
4768 else if (ctrl_x_mode_whole_line() || ctrl_x_mode_eval() || (compl_cont_status & CONT_SOL))
4769 found_new_match = search_for_exact_line(st->ins_buf,
4770 st->cur_match_pos, compl_direction, compl_pattern.string);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004771 else
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004772 found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
John Marriott5e6ea922024-11-23 14:01:57 +01004773 NULL, compl_direction, compl_pattern.string, (int)compl_pattern.length,
John Marriott8c85a2a2024-05-20 19:18:26 +02004774 1L, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004775 --msg_silent;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004776 if (!compl_started || st->set_match_pos)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004777 {
4778 // set "compl_started" even on fail
4779 compl_started = TRUE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004780 st->first_match_pos = *st->cur_match_pos;
4781 st->last_match_pos = *st->cur_match_pos;
4782 st->set_match_pos = FALSE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004783 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004784 else if (st->first_match_pos.lnum == st->last_match_pos.lnum
4785 && st->first_match_pos.col == st->last_match_pos.col)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004786 {
4787 found_new_match = FAIL;
4788 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004789 else if (compl_dir_forward()
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004790 && (st->prev_match_pos.lnum > st->cur_match_pos->lnum
4791 || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
4792 && st->prev_match_pos.col >= st->cur_match_pos->col)))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004793 {
4794 if (looped_around)
4795 found_new_match = FAIL;
4796 else
4797 looped_around = TRUE;
4798 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004799 else if (!compl_dir_forward()
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004800 && (st->prev_match_pos.lnum < st->cur_match_pos->lnum
4801 || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
4802 && st->prev_match_pos.col <= st->cur_match_pos->col)))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004803 {
4804 if (looped_around)
4805 found_new_match = FAIL;
4806 else
4807 looped_around = TRUE;
4808 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004809 st->prev_match_pos = *st->cur_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004810 if (found_new_match == FAIL)
4811 break;
4812
4813 // when ADDING, the text before the cursor matches, skip it
Girish Palyab1565882025-04-15 20:16:00 +02004814 if (compl_status_adding() && in_curbuf
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004815 && start_pos->lnum == st->cur_match_pos->lnum
4816 && start_pos->col == st->cur_match_pos->col)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004817 continue;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004818
glepnirf31cfa22025-03-06 21:59:13 +01004819 if (!in_collect)
glepnir8159fb12024-07-17 20:32:54 +02004820 ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
4821 &len, &cont_s_ipos);
glepnirbfb4eea2025-01-31 15:28:29 +01004822 if (ptr == NULL || (ins_compl_has_preinsert() && STRCMP(ptr, compl_pattern.string) == 0))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004823 continue;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004824
Girish Palyab1565882025-04-15 20:16:00 +02004825 if (is_nearest_active() && in_curbuf)
4826 {
4827 score = st->cur_match_pos->lnum - curwin->w_cursor.lnum;
4828 if (score < 0)
4829 score = -score;
4830 score++;
4831 }
4832
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004833 if (ins_compl_add_infercase(ptr, len, p_ic,
Girish Palyab1565882025-04-15 20:16:00 +02004834 in_curbuf ? NULL : st->ins_buf->b_sfname,
glepnirf31cfa22025-03-06 21:59:13 +01004835 0, cont_s_ipos, score) != NOTDONE)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004836 {
glepnirf31cfa22025-03-06 21:59:13 +01004837 if (in_collect && score == compl_first_match->cp_next->cp_score)
4838 compl_num_bests++;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004839 found_new_match = OK;
4840 break;
4841 }
4842 }
4843 p_scs = save_p_scs;
4844 p_ws = save_p_ws;
4845
4846 return found_new_match;
4847}
4848
4849/*
Girish Palyacbe53192025-04-14 22:13:15 +02004850 * Return the callback function associated with "funcname".
4851 */
4852#ifdef FEAT_COMPL_FUNC
4853 static callback_T *
4854get_cpt_func_callback(char_u *funcname)
4855{
4856 static callback_T cb;
4857 char_u buf[LSIZE];
4858 int slen;
4859
4860 slen = copy_option_part(&funcname, buf, LSIZE, ",");
4861 if (slen > 0 && option_set_callback_func(buf, &cb))
4862 return &cb;
4863 return NULL;
4864}
4865
4866/*
4867 * Retrieve new completion matches by invoking callback "cb".
4868 */
4869 static void
4870expand_cpt_function(callback_T *cb)
4871{
4872 // Re-insert the text removed by ins_compl_delete().
4873 ins_compl_insert_bytes(compl_orig_text.string + get_compl_len(), -1);
4874 // Get matches
4875 get_cpt_func_completion_matches(cb);
4876 // Undo insertion
4877 ins_compl_delete();
4878}
4879#endif
4880
4881/*
glepnir05460682025-05-26 18:23:27 +02004882 * Get completion matches from register contents.
4883 * Extracts words from all available registers and adds them to the completion list.
4884 */
4885 static void
4886get_register_completion(void)
4887{
4888 int dir = compl_direction;
4889 yankreg_T *reg = NULL;
4890 void *reg_ptr = NULL;
4891
4892 for (int i = 0; i < NUM_REGISTERS; i++)
4893 {
4894 int regname = 0;
4895
4896 if (i == 0)
4897 regname = '"'; // unnamed register
4898 else if (i < 10)
4899 regname = '0' + i;
4900 else if (i == DELETION_REGISTER)
4901 regname = '-';
4902#ifdef FEAT_CLIPBOARD
4903 else if (i == STAR_REGISTER)
4904 regname = '*';
4905 else if (i == PLUS_REGISTER)
4906 regname = '+';
4907#endif
4908 else
4909 regname = 'a' + i - 10;
4910
4911 // Skip invalid or black hole register
4912 if (!valid_yank_reg(regname, FALSE) || regname == '_')
4913 continue;
4914
4915 reg_ptr = get_register(regname, FALSE);
4916 if (reg_ptr == NULL)
4917 continue;
4918
4919 reg = (yankreg_T *)reg_ptr;
4920
4921 for (int j = 0; j < reg->y_size; j++)
4922 {
4923 char_u *str = reg->y_array[j].string;
4924 if (str == NULL)
4925 continue;
4926
4927 char_u *p = str;
4928 while (*p != NUL)
4929 {
4930 p = find_word_start(p);
4931 if (*p == NUL)
4932 break;
4933
4934 char_u *word_end = find_word_end(p);
4935
4936 // Add the word to the completion list
4937 int len = (int)(word_end - p);
4938 if (len > 0 && (!compl_orig_text.string
4939 || (p_ic ? STRNICMP(p, compl_orig_text.string,
4940 compl_orig_text.length) == 0
4941 : STRNCMP(p, compl_orig_text.string,
4942 compl_orig_text.length) == 0)))
4943 {
4944 if (ins_compl_add_infercase(p, len, p_ic, NULL,
4945 dir, FALSE, 0) == OK)
4946 dir = FORWARD;
4947 }
4948
4949 p = word_end;
4950 }
4951 }
4952
4953 // Free the register copy
4954 put_register(regname, reg_ptr);
4955 }
4956}
4957
4958/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004959 * get the next set of completion matches for "type".
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004960 * Returns TRUE if a new match is found. Otherwise returns FALSE.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004961 */
4962 static int
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004963get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004964{
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004965 int found_new_match = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004966
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004967 switch (type)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004968 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004969 case -1:
4970 break;
4971#ifdef FEAT_FIND_ID
4972 case CTRL_X_PATH_PATTERNS:
4973 case CTRL_X_PATH_DEFINES:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004974 get_next_include_file_completion(type);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004975 break;
4976#endif
4977
4978 case CTRL_X_DICTIONARY:
4979 case CTRL_X_THESAURUS:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004980 get_next_dict_tsr_completion(type, st->dict, st->dict_f);
4981 st->dict = NULL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004982 break;
4983
4984 case CTRL_X_TAGS:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004985 get_next_tag_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004986 break;
4987
4988 case CTRL_X_FILES:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004989 get_next_filename_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004990 break;
4991
4992 case CTRL_X_CMDLINE:
zeertzjqdca29d92021-08-31 19:12:51 +02004993 case CTRL_X_CMDLINE_CTRL_X:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004994 get_next_cmdline_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004995 break;
4996
4997#ifdef FEAT_COMPL_FUNC
4998 case CTRL_X_FUNCTION:
Girish Palyacbe53192025-04-14 22:13:15 +02004999 if (ctrl_x_mode_normal()) // Invoked by a func in 'cpt' option
5000 expand_cpt_function(st->func_cb);
5001 else
5002 expand_by_function(type, compl_pattern.string, NULL);
5003 break;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005004 case CTRL_X_OMNI:
Girish Palyacbe53192025-04-14 22:13:15 +02005005 expand_by_function(type, compl_pattern.string, NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005006 break;
5007#endif
5008
5009 case CTRL_X_SPELL:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005010 get_next_spell_completion(st->first_match_pos.lnum);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005011 break;
5012
glepnir05460682025-05-26 18:23:27 +02005013 case CTRL_X_REGISTER:
5014 get_register_completion();
5015 break;
5016
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005017 default: // normal ^P/^N and ^X^L
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005018 found_new_match = get_next_default_completion(st, ini);
5019 if (found_new_match == FAIL && st->ins_buf == curbuf)
5020 st->found_all = TRUE;
5021 }
5022
5023 // check if compl_curr_match has changed, (e.g. other type of
5024 // expansion added something)
5025 if (type != 0 && compl_curr_match != compl_old_match)
5026 found_new_match = OK;
5027
5028 return found_new_match;
5029}
5030
5031/*
Girish Palya0ac1eb32025-04-16 20:18:33 +02005032 * Strips carets followed by numbers. This suffix typically represents the
5033 * max_matches setting.
5034 */
5035 static void
5036strip_caret_numbers_in_place(char_u *str)
5037{
5038 char_u *read = str, *write = str, *p;
5039
5040 if (str == NULL)
5041 return;
5042
5043 while (*read)
5044 {
5045 if (*read == '^')
5046 {
5047 p = read + 1;
5048 while (vim_isdigit(*p))
5049 p++;
5050 if ((*p == ',' || *p == '\0') && p != read + 1)
5051 {
5052 read = p;
5053 continue;
5054 }
5055 else
5056 *write++ = *read++;
5057 }
5058 else
5059 *write++ = *read++;
5060 }
5061 *write = '\0';
5062}
5063
5064/*
Girish Palya7c621052025-05-26 19:41:59 +02005065 * Safely advance the cpt_sources_index by one.
5066 */
5067 static int
5068advance_cpt_sources_index_safe(void)
5069{
5070 if (cpt_sources_index < cpt_sources_count - 1)
5071 {
5072 cpt_sources_index++;
5073 return OK;
5074 }
5075#ifdef FEAT_EVAL
5076 semsg(_(e_list_index_out_of_range_nr), cpt_sources_index + 1);
5077#endif
5078 return FAIL;
5079}
5080
5081/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005082 * Get the next expansion(s), using "compl_pattern".
5083 * The search starts at position "ini" in curbuf and in the direction
5084 * compl_direction.
5085 * When "compl_started" is FALSE start at that position, otherwise continue
5086 * where we stopped searching before.
5087 * This may return before finding all the matches.
5088 * Return the total number of matches or -1 if still unknown -- Acevedo
5089 */
5090 static int
5091ins_compl_get_exp(pos_T *ini)
5092{
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005093 static ins_compl_next_state_T st;
5094 static int st_cleared = FALSE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005095 int i;
5096 int found_new_match;
5097 int type = ctrl_x_mode;
Girish Palya7c621052025-05-26 19:41:59 +02005098 int may_advance_cpt_idx = FALSE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005099
5100 if (!compl_started)
5101 {
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005102 buf_T *buf;
5103
5104 FOR_ALL_BUFFERS(buf)
5105 buf->b_scanned = 0;
5106 if (!st_cleared)
5107 {
5108 CLEAR_FIELD(st);
5109 st_cleared = TRUE;
5110 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005111 st.found_all = FALSE;
5112 st.ins_buf = curbuf;
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005113 vim_free(st.e_cpt_copy);
Christian Brabandtee17b6f2023-09-09 11:23:50 +02005114 // Make a copy of 'complete', in case the buffer is wiped out.
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005115 st.e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL)
5116 ? (char_u *)"." : curbuf->b_p_cpt);
Girish Palya0ac1eb32025-04-16 20:18:33 +02005117 strip_caret_numbers_in_place(st.e_cpt_copy);
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005118 st.e_cpt = st.e_cpt_copy == NULL ? (char_u *)"" : st.e_cpt_copy;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005119 st.last_match_pos = st.first_match_pos = *ini;
Girish Palyacbe53192025-04-14 22:13:15 +02005120
5121 if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
Girish Palya0ac1eb32025-04-16 20:18:33 +02005122 && !cpt_sources_init())
Girish Palyacbe53192025-04-14 22:13:15 +02005123 return FAIL;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005124 }
5125 else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
5126 st.ins_buf = curbuf; // In case the buffer was wiped out.
5127
5128 compl_old_match = compl_curr_match; // remember the last current match
Christian Brabandt13032a42024-07-28 21:16:48 +02005129 st.cur_match_pos = (compl_dir_forward())
glepnir8159fb12024-07-17 20:32:54 +02005130 ? &st.last_match_pos : &st.first_match_pos;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005131
5132 // For ^N/^P loop over all the flags/windows/buffers in 'complete'.
Girish Palya7c621052025-05-26 19:41:59 +02005133 cpt_sources_index = 0;
5134 for (;;)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005135 {
5136 found_new_match = FAIL;
5137 st.set_match_pos = FALSE;
5138
5139 // For ^N/^P pick a new entry from e_cpt if compl_started is off,
5140 // or if found_all says this entry is done. For ^X^L only use the
5141 // entries from 'complete' that look in loaded buffers.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005142 if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005143 && (!compl_started || st.found_all))
5144 {
Girish Palya7c621052025-05-26 19:41:59 +02005145 int status = process_next_cpt_value(&st, &type, ini,
5146 cfc_has_mode(), &may_advance_cpt_idx);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005147
5148 if (status == INS_COMPL_CPT_END)
5149 break;
5150 if (status == INS_COMPL_CPT_CONT)
Girish Palyacbe53192025-04-14 22:13:15 +02005151 {
Girish Palya7c621052025-05-26 19:41:59 +02005152 if (may_advance_cpt_idx && !advance_cpt_sources_index_safe())
5153 break;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005154 continue;
Girish Palyacbe53192025-04-14 22:13:15 +02005155 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005156 }
5157
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005158 // If complete() was called then compl_pattern has been reset. The
5159 // following won't work then, bail out.
John Marriott5e6ea922024-11-23 14:01:57 +01005160 if (compl_pattern.string == NULL)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005161 break;
5162
5163 // get the next set of completion matches
5164 found_new_match = get_next_completion_match(type, &st, ini);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005165
Girish Palya7c621052025-05-26 19:41:59 +02005166 if (may_advance_cpt_idx && !advance_cpt_sources_index_safe())
5167 break;
Girish Palyacbe53192025-04-14 22:13:15 +02005168
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005169 // break the loop for specialized modes (use 'complete' just for the
5170 // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new
5171 // match
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005172 if ((ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())
5173 || found_new_match != FAIL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005174 {
5175 if (got_int)
5176 break;
5177 // Fill the popup menu as soon as possible.
5178 if (type != -1)
5179 ins_compl_check_keys(0, FALSE);
5180
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005181 if ((ctrl_x_mode_not_default()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005182 && !ctrl_x_mode_line_or_eval()) || compl_interrupted)
5183 break;
5184 compl_started = TRUE;
5185 }
5186 else
5187 {
5188 // Mark a buffer scanned when it has been scanned completely
Christian Brabandtee9166e2023-09-03 21:24:33 +02005189 if (buf_valid(st.ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005190 st.ins_buf->b_scanned = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005191
5192 compl_started = FALSE;
5193 }
5194 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02005195 cpt_sources_index = -1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005196 compl_started = TRUE;
5197
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005198 if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00005199 && *st.e_cpt == NUL) // Got to end of 'complete'
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005200 found_new_match = FAIL;
5201
5202 i = -1; // total of matches, unknown
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005203 if (found_new_match == FAIL || (ctrl_x_mode_not_default()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005204 && !ctrl_x_mode_line_or_eval()))
5205 i = ins_compl_make_cyclic();
5206
glepnirf31cfa22025-03-06 21:59:13 +01005207 if (cfc_has_mode() && compl_get_longest && compl_num_bests > 0)
5208 fuzzy_longest_match();
5209
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005210 if (compl_old_match != NULL)
5211 {
5212 // If several matches were added (FORWARD) or the search failed and has
5213 // just been made cyclic then we have to move compl_curr_match to the
5214 // next or previous entry (if any) -- Acevedo
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005215 compl_curr_match = compl_dir_forward() ? compl_old_match->cp_next
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005216 : compl_old_match->cp_prev;
5217 if (compl_curr_match == NULL)
5218 compl_curr_match = compl_old_match;
5219 }
LemonBoy2bf52dd2022-04-09 18:17:34 +01005220 may_trigger_modechanged();
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01005221
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005222 return i;
5223}
5224
5225/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005226 * Update "compl_shown_match" to the actually shown match, it may differ when
5227 * "compl_leader" is used to omit some of the matches.
5228 */
5229 static void
5230ins_compl_update_shown_match(void)
5231{
5232 while (!ins_compl_equal(compl_shown_match,
John Marriott5e6ea922024-11-23 14:01:57 +01005233 compl_leader.string, (int)compl_leader.length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005234 && compl_shown_match->cp_next != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005235 && !is_first_match(compl_shown_match->cp_next))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005236 compl_shown_match = compl_shown_match->cp_next;
5237
5238 // If we didn't find it searching forward, and compl_shows_dir is
5239 // backward, find the last match.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005240 if (compl_shows_dir_backward()
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005241 && !ins_compl_equal(compl_shown_match,
John Marriott5e6ea922024-11-23 14:01:57 +01005242 compl_leader.string, (int)compl_leader.length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005243 && (compl_shown_match->cp_next == NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005244 || is_first_match(compl_shown_match->cp_next)))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005245 {
5246 while (!ins_compl_equal(compl_shown_match,
John Marriott5e6ea922024-11-23 14:01:57 +01005247 compl_leader.string, (int)compl_leader.length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005248 && compl_shown_match->cp_prev != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005249 && !is_first_match(compl_shown_match->cp_prev))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005250 compl_shown_match = compl_shown_match->cp_prev;
5251 }
5252}
5253
5254/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005255 * Delete the old text being completed.
5256 */
5257 void
5258ins_compl_delete(void)
5259{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005260 // In insert mode: Delete the typed part.
5261 // In replace mode: Put the old characters back, if any.
glepniredd4ac32025-01-29 18:53:51 +01005262 int col = compl_col + (compl_status_adding() ? compl_length : 0);
John Marriottf4b36412025-02-23 09:09:59 +01005263 string_T remaining = {NULL, 0};
glepnir76bdb822025-02-08 19:04:51 +01005264 int orig_col;
glepniredd4ac32025-01-29 18:53:51 +01005265 int has_preinsert = ins_compl_preinsert_effect();
5266 if (has_preinsert)
5267 {
Yegappan Lakshmanan7b6add02025-04-01 20:38:37 +02005268 col += (int)ins_compl_leader_len();
glepniredd4ac32025-01-29 18:53:51 +01005269 curwin->w_cursor.col = compl_ins_end_col;
5270 }
5271
glepnir76bdb822025-02-08 19:04:51 +01005272 if (curwin->w_cursor.lnum > compl_lnum)
5273 {
5274 if (curwin->w_cursor.col < ml_get_curline_len())
5275 {
John Marriottf4b36412025-02-23 09:09:59 +01005276 char_u *line = ml_get_cursor();
5277 remaining.length = ml_get_cursor_len();
5278 remaining.string = vim_strnsave(line, remaining.length);
5279 if (remaining.string == NULL)
glepnir76bdb822025-02-08 19:04:51 +01005280 return;
5281 }
5282 while (curwin->w_cursor.lnum > compl_lnum)
5283 {
5284 if (ml_delete(curwin->w_cursor.lnum) == FAIL)
5285 {
John Marriottf4b36412025-02-23 09:09:59 +01005286 if (remaining.string)
5287 vim_free(remaining.string);
glepnir76bdb822025-02-08 19:04:51 +01005288 return;
5289 }
zeertzjq060e6552025-02-21 20:06:26 +01005290 deleted_lines_mark(curwin->w_cursor.lnum, 1L);
glepnir76bdb822025-02-08 19:04:51 +01005291 curwin->w_cursor.lnum--;
5292 }
5293 // move cursor to end of line
5294 curwin->w_cursor.col = ml_get_curline_len();
5295 }
5296
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005297 if ((int)curwin->w_cursor.col > col)
5298 {
5299 if (stop_arrow() == FAIL)
glepnire3647c82025-02-10 21:16:32 +01005300 {
John Marriottf4b36412025-02-23 09:09:59 +01005301 if (remaining.string)
5302 vim_free(remaining.string);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005303 return;
glepnire3647c82025-02-10 21:16:32 +01005304 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005305 backspace_until_column(col);
zeertzjqf25d8f92024-12-18 21:12:25 +01005306 compl_ins_end_col = curwin->w_cursor.col;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005307 }
5308
John Marriottf4b36412025-02-23 09:09:59 +01005309 if (remaining.string != NULL)
glepnir76bdb822025-02-08 19:04:51 +01005310 {
5311 orig_col = curwin->w_cursor.col;
John Marriottf4b36412025-02-23 09:09:59 +01005312 ins_str(remaining.string, remaining.length);
glepnir76bdb822025-02-08 19:04:51 +01005313 curwin->w_cursor.col = orig_col;
John Marriottf4b36412025-02-23 09:09:59 +01005314 vim_free(remaining.string);
glepnir76bdb822025-02-08 19:04:51 +01005315 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005316 // TODO: is this sufficient for redrawing? Redrawing everything causes
5317 // flicker, thus we can't do that.
5318 changed_cline_bef_curs();
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02005319#ifdef FEAT_EVAL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005320 // clear v:completed_item
5321 set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02005322#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005323}
5324
5325/*
glepnir76bdb822025-02-08 19:04:51 +01005326 * Insert a completion string that contains newlines.
5327 * The string is split and inserted line by line.
5328 */
5329 static void
5330ins_compl_expand_multiple(char_u *str)
5331{
5332 char_u *start = str;
5333 char_u *curr = str;
glepnir5090a1f2025-02-24 19:10:37 +01005334 int base_indent = get_indent();
glepnir76bdb822025-02-08 19:04:51 +01005335
5336 while (*curr != NUL)
5337 {
5338 if (*curr == '\n')
5339 {
5340 // Insert the text chunk before newline
5341 if (curr > start)
5342 ins_char_bytes(start, (int)(curr - start));
5343
5344 // Handle newline
glepnir5090a1f2025-02-24 19:10:37 +01005345 open_line(FORWARD, OPENLINE_KEEPTRAIL | OPENLINE_FORCE_INDENT, base_indent, NULL);
glepnir76bdb822025-02-08 19:04:51 +01005346 start = curr + 1;
5347 }
5348 curr++;
5349 }
5350
5351 // Handle remaining text after last newline (if any)
5352 if (curr > start)
5353 ins_char_bytes(start, (int)(curr - start));
5354
5355 compl_ins_end_col = curwin->w_cursor.col;
5356}
5357
5358/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005359 * Insert the new text being completed.
5360 * "in_compl_func" is TRUE when called from complete_check().
glepniredd4ac32025-01-29 18:53:51 +01005361 * "move_cursor" is used when 'completeopt' includes "preinsert" and when TRUE
5362 * cursor needs to move back from the inserted text to the compl_leader.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005363 */
5364 void
glepniredd4ac32025-01-29 18:53:51 +01005365ins_compl_insert(int in_compl_func, int move_cursor)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005366{
glepnir76bdb822025-02-08 19:04:51 +01005367 int compl_len = get_compl_len();
5368 int preinsert = ins_compl_has_preinsert();
5369 char_u *cp_str = compl_shown_match->cp_str.string;
5370 size_t cp_str_len = compl_shown_match->cp_str.length;
5371 size_t leader_len = ins_compl_leader_len();
5372 char_u *has_multiple = vim_strchr(cp_str, '\n');
Bram Moolenaar4b28ba32021-12-27 19:28:37 +00005373
5374 // Make sure we don't go over the end of the string, this can happen with
5375 // illegal bytes.
zeertzjq8297e2c2025-01-30 11:04:47 +01005376 if (compl_len < (int)cp_str_len)
glepniredd4ac32025-01-29 18:53:51 +01005377 {
glepnir76bdb822025-02-08 19:04:51 +01005378 if (has_multiple)
5379 ins_compl_expand_multiple(cp_str + compl_len);
5380 else
5381 {
5382 ins_compl_insert_bytes(cp_str + compl_len, -1);
5383 if (preinsert && move_cursor)
5384 curwin->w_cursor.col -= (colnr_T)(cp_str_len - leader_len);
5385 }
glepniredd4ac32025-01-29 18:53:51 +01005386 }
5387 if (match_at_original_text(compl_shown_match) || preinsert)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005388 compl_used_match = FALSE;
5389 else
5390 compl_used_match = TRUE;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02005391#ifdef FEAT_EVAL
5392 {
5393 dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
5394
5395 set_vim_var_dict(VV_COMPLETED_ITEM, dict);
5396 }
5397#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005398 if (!in_compl_func)
5399 compl_curr_match = compl_shown_match;
5400}
5401
5402/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005403 * show the file name for the completion match (if any). Truncate the file
5404 * name to avoid a wait for return.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005405 */
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005406 static void
5407ins_compl_show_filename(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005408{
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005409 char *lead = _("match in file");
5410 int space = sc_col - vim_strsize((char_u *)lead) - 2;
5411 char_u *s;
5412 char_u *e;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005413
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005414 if (space <= 0)
5415 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005416
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005417 // We need the tail that fits. With double-byte encoding going
5418 // back from the end is very slow, thus go from the start and keep
5419 // the text that fits in "space" between "s" and "e".
5420 for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005421 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005422 space -= ptr2cells(e);
5423 while (space < 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005424 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005425 space += ptr2cells(s);
5426 MB_PTR_ADV(s);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005427 }
5428 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005429 msg_hist_off = TRUE;
5430 vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
5431 s > compl_shown_match->cp_fname ? "<" : "", s);
5432 msg((char *)IObuff);
5433 msg_hist_off = FALSE;
5434 redraw_cmdline = FALSE; // don't overwrite!
5435}
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005436
glepnirdca57fb2024-06-04 22:01:21 +02005437/*
zeertzjq551d8c32024-06-05 19:53:32 +02005438 * Find a completion item when 'completeopt' contains "fuzzy".
glepnirdca57fb2024-06-04 22:01:21 +02005439 */
glepnira218cc62024-06-03 19:32:39 +02005440 static compl_T *
5441find_comp_when_fuzzy(void)
5442{
5443 int score;
5444 char_u* str;
5445 int target_idx = -1;
5446 int is_forward = compl_shows_dir_forward();
5447 int is_backward = compl_shows_dir_backward();
5448 compl_T *comp = NULL;
5449
glepnir43eef882024-06-19 20:20:48 +02005450 if ((is_forward && compl_selected_item == compl_match_arraysize - 1)
glepnira218cc62024-06-03 19:32:39 +02005451 || (is_backward && compl_selected_item == 0))
glepnir5a18ccf2025-05-11 13:48:33 +02005452 return match_at_original_text(compl_first_match)
5453 ? compl_first_match : compl_first_match->cp_prev;
glepnira218cc62024-06-03 19:32:39 +02005454
5455 if (is_forward)
5456 target_idx = compl_selected_item + 1;
5457 else if (is_backward)
Hirohito Higashi355db992025-04-28 18:07:02 +02005458 target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1
glepnirdca57fb2024-06-04 22:01:21 +02005459 : compl_selected_item - 1;
glepnira218cc62024-06-03 19:32:39 +02005460
5461 score = compl_match_array[target_idx].pum_score;
5462 str = compl_match_array[target_idx].pum_text;
5463
5464 comp = compl_first_match;
Hirohito Higashia4a00a72025-05-08 22:58:31 +02005465 do
5466 {
Hirohito Higashi355db992025-04-28 18:07:02 +02005467 if (comp->cp_score == score && (str == comp->cp_str.string || str == comp->cp_text[CPT_ABBR]))
5468 return comp;
5469 comp = comp->cp_next;
glepnira218cc62024-06-03 19:32:39 +02005470 } while (comp != NULL && !is_first_match(comp));
5471
5472 return NULL;
5473}
5474
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005475/*
Girish Palya0ac1eb32025-04-16 20:18:33 +02005476 * Find the appropriate completion item when 'complete' ('cpt') includes
5477 * a 'max_matches' postfix. In this case, we search for a match where
5478 * 'cp_in_match_array' is set, indicating that the match is also present
5479 * in 'compl_match_array'.
5480 */
5481 static compl_T *
5482find_comp_when_cpt_sources(void)
5483{
5484 int is_forward = compl_shows_dir_forward();
5485 compl_T *match = compl_shown_match;
5486
5487 do
5488 match = is_forward ? match->cp_next : match->cp_prev;
5489 while (match->cp_next && !match->cp_in_match_array
5490 && !match_at_original_text(match));
5491 return match;
5492}
5493
5494/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005495 * Find the next set of matches for completion. Repeat the completion "todo"
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005496 * times. The number of matches found is returned in 'num_matches'.
5497 *
5498 * If "allow_get_expansion" is TRUE, then ins_compl_get_exp() may be called to
5499 * get more completions. If it is FALSE, then do nothing when there are no more
5500 * completions in the given direction.
5501 *
5502 * If "advance" is TRUE, then completion will move to the first match.
5503 * Otherwise, the original text will be shown.
5504 *
5505 * Returns OK on success and -1 if the number of matches are unknown.
5506 */
5507 static int
5508find_next_completion_match(
5509 int allow_get_expansion,
5510 int todo, // repeat completion this many times
5511 int advance,
5512 int *num_matches)
5513{
5514 int found_end = FALSE;
5515 compl_T *found_compl = NULL;
zeertzjqaa925ee2024-06-09 18:24:05 +02005516 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02005517 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
5518 int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
Girish Palya0ac1eb32025-04-16 20:18:33 +02005519 int cpt_sources_active = compl_match_array && cpt_sources_array;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005520
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005521 while (--todo >= 0)
5522 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005523 if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005524 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02005525 if (compl_match_array != NULL && compl_fuzzy_match)
5526 compl_shown_match = find_comp_when_fuzzy();
5527 else if (cpt_sources_active)
5528 compl_shown_match = find_comp_when_cpt_sources();
5529 else
5530 compl_shown_match = compl_shown_match->cp_next;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005531 found_end = (compl_first_match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005532 && (is_first_match(compl_shown_match->cp_next)
5533 || is_first_match(compl_shown_match)));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005534 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005535 else if (compl_shows_dir_backward()
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005536 && compl_shown_match->cp_prev != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005537 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005538 found_end = is_first_match(compl_shown_match);
Girish Palya0ac1eb32025-04-16 20:18:33 +02005539 if (compl_match_array != NULL && compl_fuzzy_match)
5540 compl_shown_match = find_comp_when_fuzzy();
5541 else if (cpt_sources_active)
5542 compl_shown_match = find_comp_when_cpt_sources();
5543 else
5544 compl_shown_match = compl_shown_match->cp_prev;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005545 found_end |= is_first_match(compl_shown_match);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005546 }
5547 else
5548 {
5549 if (!allow_get_expansion)
5550 {
5551 if (advance)
5552 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005553 if (compl_shows_dir_backward())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005554 compl_pending -= todo + 1;
5555 else
5556 compl_pending += todo + 1;
5557 }
5558 return -1;
5559 }
5560
5561 if (!compl_no_select && advance)
5562 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005563 if (compl_shows_dir_backward())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005564 --compl_pending;
5565 else
5566 ++compl_pending;
5567 }
5568
5569 // Find matches.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005570 *num_matches = ins_compl_get_exp(&compl_startpos);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005571
5572 // handle any pending completions
5573 while (compl_pending != 0 && compl_direction == compl_shows_dir
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005574 && advance)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005575 {
5576 if (compl_pending > 0 && compl_shown_match->cp_next != NULL)
5577 {
5578 compl_shown_match = compl_shown_match->cp_next;
5579 --compl_pending;
5580 }
5581 if (compl_pending < 0 && compl_shown_match->cp_prev != NULL)
5582 {
5583 compl_shown_match = compl_shown_match->cp_prev;
5584 ++compl_pending;
5585 }
5586 else
5587 break;
5588 }
5589 found_end = FALSE;
5590 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005591 if (!match_at_original_text(compl_shown_match)
John Marriott5e6ea922024-11-23 14:01:57 +01005592 && compl_leader.string != NULL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005593 && !ins_compl_equal(compl_shown_match,
John Marriott5e6ea922024-11-23 14:01:57 +01005594 compl_leader.string, (int)compl_leader.length)
glepnira218cc62024-06-03 19:32:39 +02005595 && !(compl_fuzzy_match && compl_shown_match->cp_score > 0))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005596 ++todo;
5597 else
5598 // Remember a matching item.
5599 found_compl = compl_shown_match;
5600
5601 // Stop at the end of the list when we found a usable match.
5602 if (found_end)
5603 {
5604 if (found_compl != NULL)
5605 {
5606 compl_shown_match = found_compl;
5607 break;
5608 }
5609 todo = 1; // use first usable match after wrapping around
5610 }
5611 }
5612
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005613 return OK;
5614}
5615
5616/*
5617 * Fill in the next completion in the current direction.
5618 * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to
5619 * get more completions. If it is FALSE, then we just do nothing when there
5620 * are no more completions in a given direction. The latter case is used when
5621 * we are still in the middle of finding completions, to allow browsing
5622 * through the ones found so far.
5623 * Return the total number of matches, or -1 if still unknown -- webb.
5624 *
5625 * compl_curr_match is currently being used by ins_compl_get_exp(), so we use
5626 * compl_shown_match here.
5627 *
5628 * Note that this function may be called recursively once only. First with
5629 * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn
5630 * calls this function with "allow_get_expansion" FALSE.
5631 */
5632 static int
5633ins_compl_next(
5634 int allow_get_expansion,
5635 int count, // repeat completion this many times; should
5636 // be at least 1
5637 int insert_match, // Insert the newly selected match
5638 int in_compl_func) // called from complete_check()
5639{
5640 int num_matches = -1;
5641 int todo = count;
5642 int advance;
5643 int started = compl_started;
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005644 buf_T *orig_curbuf = curbuf;
zeertzjqaa925ee2024-06-09 18:24:05 +02005645 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02005646 int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
5647 int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
glepniredd4ac32025-01-29 18:53:51 +01005648 int compl_preinsert = ins_compl_has_preinsert();
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005649
5650 // When user complete function return -1 for findstart which is next
5651 // time of 'always', compl_shown_match become NULL.
5652 if (compl_shown_match == NULL)
5653 return -1;
5654
John Marriott5e6ea922024-11-23 14:01:57 +01005655 if (compl_leader.string != NULL
glepnira218cc62024-06-03 19:32:39 +02005656 && !match_at_original_text(compl_shown_match)
5657 && !compl_fuzzy_match)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005658 // Update "compl_shown_match" to the actually shown match
5659 ins_compl_update_shown_match();
5660
5661 if (allow_get_expansion && insert_match
nwounkn2e3cd522023-10-17 11:05:38 +02005662 && (!compl_get_longest || compl_used_match))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005663 // Delete old text to be replaced
5664 ins_compl_delete();
5665
5666 // When finding the longest common text we stick at the original text,
5667 // don't let CTRL-N or CTRL-P move to the first match.
5668 advance = count != 1 || !allow_get_expansion || !compl_get_longest;
5669
5670 // When restarting the search don't insert the first match either.
5671 if (compl_restarting)
5672 {
5673 advance = FALSE;
5674 compl_restarting = FALSE;
5675 }
5676
5677 // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
5678 // around.
5679 if (find_next_completion_match(allow_get_expansion, todo, advance,
5680 &num_matches) == -1)
5681 return -1;
5682
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005683 if (curbuf != orig_curbuf)
5684 {
5685 // In case some completion function switched buffer, don't want to
5686 // insert the completion elsewhere.
5687 return -1;
5688 }
5689
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005690 // Insert the text of the new completion, or the compl_leader.
glepniredd4ac32025-01-29 18:53:51 +01005691 if (compl_no_insert && !started && !compl_preinsert)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005692 {
glepnir6a38aff2024-12-16 21:56:16 +01005693 ins_compl_insert_bytes(compl_orig_text.string + get_compl_len(), -1);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005694 compl_used_match = FALSE;
5695 }
5696 else if (insert_match)
5697 {
5698 if (!compl_get_longest || compl_used_match)
glepniredd4ac32025-01-29 18:53:51 +01005699 ins_compl_insert(in_compl_func, TRUE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005700 else
glepnir6a38aff2024-12-16 21:56:16 +01005701 ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005702 }
5703 else
5704 compl_used_match = FALSE;
5705
5706 if (!allow_get_expansion)
5707 {
5708 // may undisplay the popup menu first
5709 ins_compl_upd_pum();
5710
5711 if (pum_enough_matches())
5712 // Will display the popup menu, don't redraw yet to avoid flicker.
5713 pum_call_update_screen();
5714 else
5715 // Not showing the popup menu yet, redraw to show the user what was
5716 // inserted.
5717 update_screen(0);
5718
5719 // display the updated popup menu
5720 ins_compl_show_pum();
5721#ifdef FEAT_GUI
5722 if (gui.in_use)
5723 {
5724 // Show the cursor after the match, not after the redrawn text.
5725 setcursor();
5726 out_flush_cursor(FALSE, FALSE);
5727 }
5728#endif
5729
5730 // Delete old text to be replaced, since we're still searching and
5731 // don't want to match ourselves!
5732 ins_compl_delete();
5733 }
5734
5735 // Enter will select a match when the match wasn't inserted and the popup
5736 // menu is visible.
5737 if (compl_no_insert && !started)
5738 compl_enter_selects = TRUE;
5739 else
5740 compl_enter_selects = !insert_match && compl_match_array != NULL;
5741
5742 // Show the file name for the match (if any)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005743 if (compl_shown_match->cp_fname != NULL)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005744 ins_compl_show_filename();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005745
5746 return num_matches;
5747}
5748
5749/*
5750 * Call this while finding completions, to check whether the user has hit a key
5751 * that should change the currently displayed completion, or exit completion
5752 * mode. Also, when compl_pending is not zero, show a completion as soon as
5753 * possible. -- webb
5754 * "frequency" specifies out of how many calls we actually check.
5755 * "in_compl_func" is TRUE when called from complete_check(), don't set
5756 * compl_curr_match.
5757 */
5758 void
5759ins_compl_check_keys(int frequency, int in_compl_func)
5760{
5761 static int count = 0;
5762 int c;
5763
5764 // Don't check when reading keys from a script, :normal or feedkeys().
5765 // That would break the test scripts. But do check for keys when called
5766 // from complete_check().
5767 if (!in_compl_func && (using_script() || ex_normal_busy))
5768 return;
5769
5770 // Only do this at regular intervals
5771 if (++count < frequency)
5772 return;
5773 count = 0;
5774
5775 // Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key()
5776 // can't do its work correctly.
5777 c = vpeekc_any();
5778 if (c != NUL)
5779 {
5780 if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R)
5781 {
5782 c = safe_vgetc(); // Eat the character
5783 compl_shows_dir = ins_compl_key2dir(c);
5784 (void)ins_compl_next(FALSE, ins_compl_key2count(c),
5785 c != K_UP && c != K_DOWN, in_compl_func);
5786 }
5787 else
5788 {
5789 // Need to get the character to have KeyTyped set. We'll put it
5790 // back with vungetc() below. But skip K_IGNORE.
5791 c = safe_vgetc();
5792 if (c != K_IGNORE)
5793 {
5794 // Don't interrupt completion when the character wasn't typed,
5795 // e.g., when doing @q to replay keys.
5796 if (c != Ctrl_R && KeyTyped)
5797 compl_interrupted = TRUE;
5798
5799 vungetc(c);
5800 }
5801 }
5802 }
zeertzjq529b9ad2024-06-05 20:27:06 +02005803 if (compl_pending != 0 && !got_int && !(cot_flags & COT_NOINSERT))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005804 {
5805 int todo = compl_pending > 0 ? compl_pending : -compl_pending;
5806
5807 compl_pending = 0;
5808 (void)ins_compl_next(FALSE, todo, TRUE, in_compl_func);
5809 }
5810}
5811
5812/*
5813 * Decide the direction of Insert mode complete from the key typed.
5814 * Returns BACKWARD or FORWARD.
5815 */
5816 static int
5817ins_compl_key2dir(int c)
5818{
5819 if (c == Ctrl_P || c == Ctrl_L
5820 || c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP || c == K_UP)
5821 return BACKWARD;
5822 return FORWARD;
5823}
5824
5825/*
5826 * Return TRUE for keys that are used for completion only when the popup menu
5827 * is visible.
5828 */
5829 static int
5830ins_compl_pum_key(int c)
5831{
5832 return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP
5833 || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == K_S_DOWN
5834 || c == K_UP || c == K_DOWN);
5835}
5836
5837/*
5838 * Decide the number of completions to move forward.
5839 * Returns 1 for most keys, height of the popup menu for page-up/down keys.
5840 */
5841 static int
5842ins_compl_key2count(int c)
5843{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005844 if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN)
5845 {
glepnir19e1dd62025-05-08 22:50:38 +02005846 int h = pum_get_height();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005847 if (h > 3)
5848 h -= 2; // keep some context
5849 return h;
5850 }
5851 return 1;
5852}
5853
5854/*
5855 * Return TRUE if completion with "c" should insert the match, FALSE if only
5856 * to change the currently selected completion.
5857 */
5858 static int
5859ins_compl_use_match(int c)
5860{
5861 switch (c)
5862 {
5863 case K_UP:
5864 case K_DOWN:
5865 case K_PAGEDOWN:
5866 case K_KPAGEDOWN:
5867 case K_S_DOWN:
5868 case K_PAGEUP:
5869 case K_KPAGEUP:
5870 case K_S_UP:
5871 return FALSE;
5872 }
5873 return TRUE;
5874}
5875
5876/*
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005877 * Get the pattern, column and length for normal completion (CTRL-N CTRL-P
5878 * completion)
John Marriott5e6ea922024-11-23 14:01:57 +01005879 * Sets the global variables: compl_col, compl_length and compl_pattern.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005880 * Uses the global variables: compl_cont_status and ctrl_x_mode
5881 */
5882 static int
5883get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
5884{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005885 if ((compl_cont_status & CONT_SOL) || ctrl_x_mode_path_defines())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005886 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005887 if (!compl_status_adding())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005888 {
5889 while (--startcol >= 0 && vim_isIDc(line[startcol]))
5890 ;
5891 compl_col += ++startcol;
5892 compl_length = curs_col - startcol;
5893 }
5894 if (p_ic)
John Marriott8c85a2a2024-05-20 19:18:26 +02005895 {
John Marriott5e6ea922024-11-23 14:01:57 +01005896 compl_pattern.string = str_foldcase(line + compl_col,
5897 compl_length, NULL, 0);
5898 if (compl_pattern.string == NULL)
5899 {
5900 compl_pattern.length = 0;
5901 return FAIL;
5902 }
5903 compl_pattern.length = STRLEN(compl_pattern.string);
5904 }
5905 else
5906 {
5907 compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
5908 if (compl_pattern.string == NULL)
5909 {
5910 compl_pattern.length = 0;
5911 return FAIL;
5912 }
5913 compl_pattern.length = (size_t)compl_length;
John Marriott8c85a2a2024-05-20 19:18:26 +02005914 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005915 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005916 else if (compl_status_adding())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005917 {
5918 char_u *prefix = (char_u *)"\\<";
John Marriott8c85a2a2024-05-20 19:18:26 +02005919 size_t prefixlen = STRLEN_LITERAL("\\<");
John Marriott5e6ea922024-11-23 14:01:57 +01005920 size_t n;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005921
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005922 if (!vim_iswordp(line + compl_col)
5923 || (compl_col > 0
5924 && (vim_iswordp(mb_prevptr(line, line + compl_col)))))
John Marriott8c85a2a2024-05-20 19:18:26 +02005925 {
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005926 prefix = (char_u *)"";
John Marriott8c85a2a2024-05-20 19:18:26 +02005927 prefixlen = 0;
5928 }
John Marriott5e6ea922024-11-23 14:01:57 +01005929
5930 // we need up to 2 extra chars for the prefix
5931 n = quote_meta(NULL, line + compl_col, compl_length) + prefixlen;
5932 compl_pattern.string = alloc(n);
5933 if (compl_pattern.string == NULL)
5934 {
5935 compl_pattern.length = 0;
5936 return FAIL;
5937 }
5938 STRCPY((char *)compl_pattern.string, prefix);
5939 (void)quote_meta(compl_pattern.string + prefixlen,
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005940 line + compl_col, compl_length);
John Marriott5e6ea922024-11-23 14:01:57 +01005941 compl_pattern.length = n - 1;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005942 }
5943 else if (--startcol < 0
5944 || !vim_iswordp(mb_prevptr(line, line + startcol + 1)))
5945 {
John Marriott5e6ea922024-11-23 14:01:57 +01005946 size_t len = STRLEN_LITERAL("\\<\\k\\k");
5947
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005948 // Match any word of at least two chars
John Marriott5e6ea922024-11-23 14:01:57 +01005949 compl_pattern.string = vim_strnsave((char_u *)"\\<\\k\\k", len);
5950 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02005951 {
John Marriott5e6ea922024-11-23 14:01:57 +01005952 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005953 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02005954 }
John Marriott5e6ea922024-11-23 14:01:57 +01005955 compl_pattern.length = len;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005956 compl_col += curs_col;
5957 compl_length = 0;
5958 }
5959 else
5960 {
5961 // Search the point of change class of multibyte character
5962 // or not a word single byte character backward.
5963 if (has_mbyte)
5964 {
5965 int base_class;
5966 int head_off;
5967
5968 startcol -= (*mb_head_off)(line, line + startcol);
5969 base_class = mb_get_class(line + startcol);
5970 while (--startcol >= 0)
5971 {
5972 head_off = (*mb_head_off)(line, line + startcol);
5973 if (base_class != mb_get_class(line + startcol
5974 - head_off))
5975 break;
5976 startcol -= head_off;
5977 }
5978 }
5979 else
glepnir19e1dd62025-05-08 22:50:38 +02005980 {
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005981 while (--startcol >= 0 && vim_iswordc(line[startcol]))
5982 ;
glepnir19e1dd62025-05-08 22:50:38 +02005983 }
5984
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005985 compl_col += ++startcol;
5986 compl_length = (int)curs_col - startcol;
5987 if (compl_length == 1)
5988 {
5989 // Only match word with at least two chars -- webb
5990 // there's no need to call quote_meta,
5991 // alloc(7) is enough -- Acevedo
John Marriott5e6ea922024-11-23 14:01:57 +01005992 compl_pattern.string = alloc(7);
5993 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02005994 {
John Marriott5e6ea922024-11-23 14:01:57 +01005995 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005996 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02005997 }
John Marriott5e6ea922024-11-23 14:01:57 +01005998 STRCPY((char *)compl_pattern.string, "\\<");
5999 (void)quote_meta(compl_pattern.string + 2, line + compl_col, 1);
6000 STRCAT((char *)compl_pattern.string, "\\k");
6001 compl_pattern.length = STRLEN(compl_pattern.string);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006002 }
6003 else
6004 {
John Marriott5e6ea922024-11-23 14:01:57 +01006005 size_t n = quote_meta(NULL, line + compl_col, compl_length) + 2;
6006
6007 compl_pattern.string = alloc(n);
6008 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02006009 {
John Marriott5e6ea922024-11-23 14:01:57 +01006010 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006011 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02006012 }
John Marriott5e6ea922024-11-23 14:01:57 +01006013 STRCPY((char *)compl_pattern.string, "\\<");
6014 (void)quote_meta(compl_pattern.string + 2, line + compl_col,
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006015 compl_length);
John Marriott5e6ea922024-11-23 14:01:57 +01006016 compl_pattern.length = n - 1;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006017 }
6018 }
6019
6020 return OK;
6021}
6022
6023/*
6024 * Get the pattern, column and length for whole line completion or for the
6025 * complete() function.
6026 * Sets the global variables: compl_col, compl_length and compl_pattern.
6027 */
6028 static int
6029get_wholeline_compl_info(char_u *line, colnr_T curs_col)
6030{
6031 compl_col = (colnr_T)getwhitecols(line);
6032 compl_length = (int)curs_col - (int)compl_col;
6033 if (compl_length < 0) // cursor in indent: empty pattern
6034 compl_length = 0;
6035 if (p_ic)
John Marriott8c85a2a2024-05-20 19:18:26 +02006036 {
John Marriott5e6ea922024-11-23 14:01:57 +01006037 compl_pattern.string = str_foldcase(line + compl_col, compl_length,
6038 NULL, 0);
6039 if (compl_pattern.string == NULL)
6040 {
6041 compl_pattern.length = 0;
6042 return FAIL;
6043 }
6044 compl_pattern.length = STRLEN(compl_pattern.string);
John Marriott8c85a2a2024-05-20 19:18:26 +02006045 }
John Marriott5e6ea922024-11-23 14:01:57 +01006046 else
6047 {
6048 compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
6049 if (compl_pattern.string == NULL)
6050 {
6051 compl_pattern.length = 0;
6052 return FAIL;
6053 }
6054 compl_pattern.length = (size_t)compl_length;
6055 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006056
6057 return OK;
6058}
6059
6060/*
6061 * Get the pattern, column and length for filename completion.
6062 * Sets the global variables: compl_col, compl_length and compl_pattern.
6063 */
6064 static int
6065get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col)
6066{
6067 // Go back to just before the first filename character.
6068 if (startcol > 0)
6069 {
6070 char_u *p = line + startcol;
6071
6072 MB_PTR_BACK(line, p);
6073 while (p > line && vim_isfilec(PTR2CHAR(p)))
6074 MB_PTR_BACK(line, p);
6075 if (p == line && vim_isfilec(PTR2CHAR(p)))
6076 startcol = 0;
6077 else
6078 startcol = (int)(p - line) + 1;
6079 }
6080
6081 compl_col += startcol;
6082 compl_length = (int)curs_col - startcol;
John Marriott5e6ea922024-11-23 14:01:57 +01006083 compl_pattern.string = addstar(line + compl_col, compl_length, EXPAND_FILES);
6084 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02006085 {
John Marriott5e6ea922024-11-23 14:01:57 +01006086 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006087 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02006088 }
6089
John Marriott5e6ea922024-11-23 14:01:57 +01006090 compl_pattern.length = STRLEN(compl_pattern.string);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006091
6092 return OK;
6093}
6094
6095/*
6096 * Get the pattern, column and length for command-line completion.
6097 * Sets the global variables: compl_col, compl_length and compl_pattern.
6098 */
6099 static int
6100get_cmdline_compl_info(char_u *line, colnr_T curs_col)
6101{
John Marriott5e6ea922024-11-23 14:01:57 +01006102 compl_pattern.string = vim_strnsave(line, curs_col);
6103 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02006104 {
John Marriott5e6ea922024-11-23 14:01:57 +01006105 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006106 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02006107 }
John Marriott5e6ea922024-11-23 14:01:57 +01006108 compl_pattern.length = curs_col;
6109 set_cmd_context(&compl_xp, compl_pattern.string,
6110 (int)compl_pattern.length, curs_col, FALSE);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006111 if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
6112 || compl_xp.xp_context == EXPAND_NOTHING)
6113 // No completion possible, use an empty pattern to get a
6114 // "pattern not found" message.
6115 compl_col = curs_col;
6116 else
John Marriott5e6ea922024-11-23 14:01:57 +01006117 compl_col = (int)(compl_xp.xp_pattern - compl_pattern.string);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006118 compl_length = curs_col - compl_col;
6119
6120 return OK;
6121}
6122
6123/*
6124 * Get the pattern, column and length for user defined completion ('omnifunc',
6125 * 'completefunc' and 'thesaurusfunc')
6126 * Sets the global variables: compl_col, compl_length and compl_pattern.
6127 * Uses the global variable: spell_bad_len
Girish Palyacbe53192025-04-14 22:13:15 +02006128 * Callback function "cb" is set if triggered by a function in the 'cpt'
6129 * option; otherwise, it is NULL.
6130 * "startcol", when not NULL, contains the column returned by function.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006131 */
6132 static int
glepnir19e1dd62025-05-08 22:50:38 +02006133get_userdefined_compl_info(
6134 colnr_T curs_col UNUSED,
6135 callback_T *cb UNUSED,
6136 int *startcol UNUSED)
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006137{
6138 int ret = FAIL;
6139
6140#ifdef FEAT_COMPL_FUNC
6141 // Call user defined function 'completefunc' with "a:findstart"
6142 // set to 1 to obtain the length of text to use for completion.
glepnir19e1dd62025-05-08 22:50:38 +02006143 char_u *line = NULL;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006144 typval_T args[3];
6145 int col;
glepnir19e1dd62025-05-08 22:50:38 +02006146 char_u *funcname = NULL;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006147 pos_T pos;
6148 int save_State = State;
Girish Palyacbe53192025-04-14 22:13:15 +02006149 int len;
glepnir19e1dd62025-05-08 22:50:38 +02006150 string_T *compl_pat = NULL;
Girish Palyacbe53192025-04-14 22:13:15 +02006151 int is_cpt_function = (cb != NULL);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006152
Girish Palyacbe53192025-04-14 22:13:15 +02006153 if (!is_cpt_function)
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006154 {
Girish Palyacbe53192025-04-14 22:13:15 +02006155 // Call 'completefunc' or 'omnifunc' or 'thesaurusfunc' and get pattern
6156 // length as a string
6157 funcname = get_complete_funcname(ctrl_x_mode);
6158 if (*funcname == NUL)
6159 {
6160 semsg(_(e_option_str_is_not_set), ctrl_x_mode_function()
6161 ? "completefunc" : "omnifunc");
6162 return FAIL;
6163 }
6164 cb = get_insert_callback(ctrl_x_mode);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006165 }
6166
6167 args[0].v_type = VAR_NUMBER;
6168 args[0].vval.v_number = 1;
6169 args[1].v_type = VAR_STRING;
6170 args[1].vval.v_string = (char_u *)"";
6171 args[2].v_type = VAR_UNKNOWN;
6172 pos = curwin->w_cursor;
zeertzjqcfe45652022-05-27 17:26:55 +01006173 ++textlock;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006174 col = call_callback_retnr(cb, 2, args);
zeertzjqcfe45652022-05-27 17:26:55 +01006175 --textlock;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006176
6177 State = save_State;
6178 curwin->w_cursor = pos; // restore the cursor position
zeertzjq0a419e02024-04-02 19:01:14 +02006179 check_cursor(); // make sure cursor position is valid, just in case
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006180 validate_cursor();
6181 if (!EQUAL_POS(curwin->w_cursor, pos))
6182 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00006183 emsg(_(e_complete_function_deleted_text));
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006184 return FAIL;
6185 }
6186
Girish Palyacbe53192025-04-14 22:13:15 +02006187 if (startcol != NULL)
6188 *startcol = col;
6189
LemonBoy9bcb9ca2022-05-26 15:23:26 +01006190 // Return value -2 means the user complete function wants to cancel the
6191 // complete without an error, do the same if the function did not execute
6192 // successfully.
6193 if (col == -2 || aborting())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006194 return FAIL;
glepnir19e1dd62025-05-08 22:50:38 +02006195
LemonBoy9bcb9ca2022-05-26 15:23:26 +01006196 // Return value -3 does the same as -2 and leaves CTRL-X mode.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006197 if (col == -3)
6198 {
Girish Palyacbe53192025-04-14 22:13:15 +02006199 if (is_cpt_function)
6200 return FAIL;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006201 ctrl_x_mode = CTRL_X_NORMAL;
6202 edit_submode = NULL;
6203 if (!shortmess(SHM_COMPLETIONMENU))
6204 msg_clr_cmdline();
6205 return FAIL;
6206 }
6207
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00006208 // Reset extended parameters of completion, when starting new
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006209 // completion.
6210 compl_opt_refresh_always = FALSE;
6211 compl_opt_suppress_empty = FALSE;
6212
Girish Palyacbe53192025-04-14 22:13:15 +02006213 if (col < 0 || col > curs_col)
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006214 col = curs_col;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006215
6216 // Setup variables for completion. Need to obtain "line" again,
6217 // it may have become invalid.
6218 line = ml_get(curwin->w_cursor.lnum);
Girish Palyacbe53192025-04-14 22:13:15 +02006219 len = curs_col - col;
6220 compl_pat = is_cpt_function ? &cpt_compl_pattern : &compl_pattern;
6221 compl_pat->string = vim_strnsave(line + col, (size_t)len);
6222 if (compl_pat->string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02006223 {
Girish Palyacbe53192025-04-14 22:13:15 +02006224 compl_pat->length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006225 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02006226 }
Girish Palyacbe53192025-04-14 22:13:15 +02006227 compl_pat->length = (size_t)compl_length;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006228
Girish Palyacbe53192025-04-14 22:13:15 +02006229 if (!is_cpt_function)
6230 {
6231 compl_col = col;
6232 compl_length = len;
6233 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006234 ret = OK;
6235#endif
6236
6237 return ret;
6238}
6239
6240/*
6241 * Get the pattern, column and length for spell completion.
6242 * Sets the global variables: compl_col, compl_length and compl_pattern.
6243 * Uses the global variable: spell_bad_len
6244 */
6245 static int
6246get_spell_compl_info(int startcol UNUSED, colnr_T curs_col UNUSED)
6247{
6248 int ret = FAIL;
6249#ifdef FEAT_SPELL
glepnir19e1dd62025-05-08 22:50:38 +02006250 char_u *line = NULL;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006251
6252 if (spell_bad_len > 0)
6253 compl_col = curs_col - spell_bad_len;
6254 else
6255 compl_col = spell_word_start(startcol);
glepnir19e1dd62025-05-08 22:50:38 +02006256
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006257 if (compl_col >= (colnr_T)startcol)
6258 {
6259 compl_length = 0;
6260 compl_col = curs_col;
6261 }
6262 else
6263 {
6264 spell_expand_check_cap(compl_col);
6265 compl_length = (int)curs_col - compl_col;
6266 }
6267 // Need to obtain "line" again, it may have become invalid.
6268 line = ml_get(curwin->w_cursor.lnum);
John Marriott5e6ea922024-11-23 14:01:57 +01006269 compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
6270 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02006271 {
John Marriott5e6ea922024-11-23 14:01:57 +01006272 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006273 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02006274 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006275
John Marriott5e6ea922024-11-23 14:01:57 +01006276 compl_pattern.length = (size_t)compl_length;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006277 ret = OK;
6278#endif
6279
6280 return ret;
6281}
6282
6283/*
6284 * Get the completion pattern, column and length.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006285 * "startcol" - start column number of the completion pattern/text
6286 * "cur_col" - current cursor column
6287 * On return, "line_invalid" is set to TRUE, if the current line may have
6288 * become invalid and needs to be fetched again.
6289 * Returns OK on success.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006290 */
6291 static int
6292compl_get_info(char_u *line, int startcol, colnr_T curs_col, int *line_invalid)
6293{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006294 if (ctrl_x_mode_normal()
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006295 || (ctrl_x_mode & CTRL_X_WANT_IDENT
6296 && !thesaurus_func_complete(ctrl_x_mode)))
6297 {
6298 return get_normal_compl_info(line, startcol, curs_col);
6299 }
6300 else if (ctrl_x_mode_line_or_eval())
6301 {
6302 return get_wholeline_compl_info(line, curs_col);
6303 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006304 else if (ctrl_x_mode_files())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006305 {
6306 return get_filename_compl_info(line, startcol, curs_col);
6307 }
6308 else if (ctrl_x_mode == CTRL_X_CMDLINE)
6309 {
6310 return get_cmdline_compl_info(line, curs_col);
6311 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006312 else if (ctrl_x_mode_function() || ctrl_x_mode_omni()
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006313 || thesaurus_func_complete(ctrl_x_mode))
6314 {
Girish Palyacbe53192025-04-14 22:13:15 +02006315 if (get_userdefined_compl_info(curs_col, NULL, NULL) != OK)
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006316 return FAIL;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006317 *line_invalid = TRUE; // "line" may have become invalid
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006318 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006319 else if (ctrl_x_mode_spell())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006320 {
6321 if (get_spell_compl_info(startcol, curs_col) == FAIL)
6322 return FAIL;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006323 *line_invalid = TRUE; // "line" may have become invalid
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006324 }
glepnir05460682025-05-26 18:23:27 +02006325 else if (ctrl_x_mode_register())
6326 {
6327 return get_normal_compl_info(line, startcol, curs_col);
6328 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006329 else
6330 {
6331 internal_error("ins_complete()");
6332 return FAIL;
6333 }
6334
6335 return OK;
6336}
6337
6338/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006339 * Continue an interrupted completion mode search in "line".
6340 *
6341 * If this same ctrl_x_mode has been interrupted use the text from
6342 * "compl_startpos" to the cursor as a pattern to add a new word instead of
6343 * expand the one before the cursor, in word-wise if "compl_startpos" is not in
6344 * the same line as the cursor then fix it (the line has been split because it
6345 * was longer than 'tw'). if SOL is set then skip the previous pattern, a word
6346 * at the beginning of the line has been inserted, we'll look for that.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006347 */
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006348 static void
6349ins_compl_continue_search(char_u *line)
6350{
6351 // it is a continued search
6352 compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006353 if (ctrl_x_mode_normal() || ctrl_x_mode_path_patterns()
6354 || ctrl_x_mode_path_defines())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006355 {
6356 if (compl_startpos.lnum != curwin->w_cursor.lnum)
6357 {
6358 // line (probably) wrapped, set compl_startpos to the
6359 // first non_blank in the line, if it is not a wordchar
6360 // include it to get a better pattern, but then we don't
6361 // want the "\\<" prefix, check it below
6362 compl_col = (colnr_T)getwhitecols(line);
6363 compl_startpos.col = compl_col;
6364 compl_startpos.lnum = curwin->w_cursor.lnum;
6365 compl_cont_status &= ~CONT_SOL; // clear SOL if present
6366 }
6367 else
6368 {
6369 // S_IPOS was set when we inserted a word that was at the
6370 // beginning of the line, which means that we'll go to SOL
6371 // mode but first we need to redefine compl_startpos
6372 if (compl_cont_status & CONT_S_IPOS)
6373 {
6374 compl_cont_status |= CONT_SOL;
6375 compl_startpos.col = (colnr_T)(skipwhite(
6376 line + compl_length
6377 + compl_startpos.col) - line);
6378 }
6379 compl_col = compl_startpos.col;
6380 }
6381 compl_length = curwin->w_cursor.col - (int)compl_col;
6382 // IObuff is used to add a "word from the next line" would we
6383 // have enough space? just being paranoid
6384#define MIN_SPACE 75
6385 if (compl_length > (IOSIZE - MIN_SPACE))
6386 {
6387 compl_cont_status &= ~CONT_SOL;
6388 compl_length = (IOSIZE - MIN_SPACE);
6389 compl_col = curwin->w_cursor.col - compl_length;
6390 }
6391 compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
6392 if (compl_length < 1)
6393 compl_cont_status &= CONT_LOCAL;
6394 }
6395 else if (ctrl_x_mode_line_or_eval())
6396 compl_cont_status = CONT_ADDING | CONT_N_ADDS;
6397 else
6398 compl_cont_status = 0;
6399}
6400
6401/*
6402 * start insert mode completion
6403 */
6404 static int
6405ins_compl_start(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006406{
glepnir19e1dd62025-05-08 22:50:38 +02006407 char_u *line = NULL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006408 int startcol = 0; // column where searched text starts
6409 colnr_T curs_col; // cursor column
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006410 int line_invalid = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006411 int save_did_ai = did_ai;
Bram Moolenaard9eefe32019-04-06 14:22:21 +02006412 int flags = CP_ORIGINAL_TEXT;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006413
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006414 // First time we hit ^N or ^P (in a row, I mean)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006415 did_ai = FALSE;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006416 did_si = FALSE;
6417 can_si = FALSE;
6418 can_si_back = FALSE;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006419 if (stop_arrow() == FAIL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006420 return FAIL;
6421
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006422 line = ml_get(curwin->w_cursor.lnum);
6423 curs_col = curwin->w_cursor.col;
6424 compl_pending = 0;
glepnir76bdb822025-02-08 19:04:51 +01006425 compl_lnum = curwin->w_cursor.lnum;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006426
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006427 if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
6428 && compl_cont_mode == ctrl_x_mode)
6429 // this same ctrl-x_mode was interrupted previously. Continue the
6430 // completion.
6431 ins_compl_continue_search(line);
6432 else
6433 compl_cont_status &= CONT_LOCAL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006434
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006435 if (!compl_status_adding()) // normal expansion
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006436 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006437 compl_cont_mode = ctrl_x_mode;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006438 if (ctrl_x_mode_not_default())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006439 // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
6440 compl_cont_status = 0;
6441 compl_cont_status |= CONT_N_ADDS;
6442 compl_startpos = curwin->w_cursor;
6443 startcol = (int)curs_col;
6444 compl_col = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006445 }
6446
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006447 // Work out completion pattern and original text -- webb
6448 if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL)
6449 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006450 if (ctrl_x_mode_function() || ctrl_x_mode_omni()
6451 || thesaurus_func_complete(ctrl_x_mode))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006452 // restore did_ai, so that adding comment leader works
6453 did_ai = save_did_ai;
6454 return FAIL;
6455 }
6456 // If "line" was changed while getting completion info get it again.
6457 if (line_invalid)
6458 line = ml_get(curwin->w_cursor.lnum);
6459
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006460 if (compl_status_adding())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006461 {
6462 edit_submode_pre = (char_u *)_(" Adding");
6463 if (ctrl_x_mode_line_or_eval())
6464 {
6465 // Insert a new line, keep indentation but ignore 'comments'.
6466 char_u *old = curbuf->b_p_com;
6467
6468 curbuf->b_p_com = (char_u *)"";
6469 compl_startpos.lnum = curwin->w_cursor.lnum;
6470 compl_startpos.col = compl_col;
6471 ins_eol('\r');
6472 curbuf->b_p_com = old;
6473 compl_length = 0;
6474 compl_col = curwin->w_cursor.col;
glepnir76bdb822025-02-08 19:04:51 +01006475 compl_lnum = curwin->w_cursor.lnum;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006476 }
glepnircfe502c2025-04-17 20:17:53 +02006477 else if (ctrl_x_mode_normal() && cfc_has_mode())
glepnir7cfe6932024-09-15 20:06:28 +02006478 {
6479 compl_startpos = curwin->w_cursor;
6480 compl_cont_status &= CONT_S_IPOS;
6481 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006482 }
6483 else
6484 {
6485 edit_submode_pre = NULL;
6486 compl_startpos.col = compl_col;
6487 }
6488
6489 if (compl_cont_status & CONT_LOCAL)
6490 edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
6491 else
6492 edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
6493
6494 // If any of the original typed text has been changed we need to fix
6495 // the redo buffer.
6496 ins_compl_fixRedoBufForLeader(NULL);
6497
6498 // Always add completion for the original text.
John Marriott5e6ea922024-11-23 14:01:57 +01006499 VIM_CLEAR_STRING(compl_orig_text);
6500 compl_orig_text.length = (size_t)compl_length;
6501 compl_orig_text.string = vim_strnsave(line + compl_col, (size_t)compl_length);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006502 if (p_ic)
6503 flags |= CP_ICASE;
zeertzjq4422de62025-03-07 19:06:02 +01006504 if (compl_orig_text.string == NULL
6505 || ins_compl_add(compl_orig_text.string,
6506 (int)compl_orig_text.length,
6507 NULL, NULL, NULL, 0, flags, FALSE, NULL, 0) != OK)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006508 {
John Marriott5e6ea922024-11-23 14:01:57 +01006509 VIM_CLEAR_STRING(compl_pattern);
6510 VIM_CLEAR_STRING(compl_orig_text);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006511 return FAIL;
6512 }
6513
6514 // showmode might reset the internal line pointers, so it must
6515 // be called before line = ml_get(), or when this address is no
6516 // longer needed. -- Acevedo.
6517 edit_submode_extra = (char_u *)_("-- Searching...");
6518 edit_submode_highl = HLF_COUNT;
6519 showmode();
6520 edit_submode_extra = NULL;
6521 out_flush();
6522
6523 return OK;
6524}
6525
6526/*
6527 * display the completion status message
6528 */
6529 static void
6530ins_compl_show_statusmsg(void)
6531{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006532 // we found no match if the list has only the "compl_orig_text"-entry
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006533 if (is_first_match(compl_first_match->cp_next))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006534 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006535 edit_submode_extra = compl_status_adding() && compl_length > 1
Bram Moolenaarb53a1902022-11-15 13:57:38 +00006536 ? (char_u *)_("Hit end of paragraph")
6537 : (char_u *)_("Pattern not found");
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006538 edit_submode_highl = HLF_E;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006539 }
6540
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006541 if (edit_submode_extra == NULL)
6542 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006543 if (match_at_original_text(compl_curr_match))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006544 {
6545 edit_submode_extra = (char_u *)_("Back at original");
6546 edit_submode_highl = HLF_W;
6547 }
6548 else if (compl_cont_status & CONT_S_IPOS)
6549 {
6550 edit_submode_extra = (char_u *)_("Word from other line");
6551 edit_submode_highl = HLF_COUNT;
6552 }
6553 else if (compl_curr_match->cp_next == compl_curr_match->cp_prev)
6554 {
6555 edit_submode_extra = (char_u *)_("The only match");
6556 edit_submode_highl = HLF_COUNT;
Bram Moolenaarf9d51352020-10-26 19:22:42 +01006557 compl_curr_match->cp_number = 1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006558 }
6559 else
6560 {
Bram Moolenaar977fd0b2020-10-27 09:12:45 +01006561#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006562 // Update completion sequence number when needed.
6563 if (compl_curr_match->cp_number == -1)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01006564 ins_compl_update_sequence_numbers();
Bram Moolenaar977fd0b2020-10-27 09:12:45 +01006565#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006566 // The match should always have a sequence number now, this is
6567 // just a safety check.
6568 if (compl_curr_match->cp_number != -1)
6569 {
6570 // Space for 10 text chars. + 2x10-digit no.s = 31.
6571 // Translations may need more than twice that.
6572 static char_u match_ref[81];
6573
6574 if (compl_matches > 0)
6575 vim_snprintf((char *)match_ref, sizeof(match_ref),
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006576 _("match %d of %d"),
6577 compl_curr_match->cp_number, compl_matches);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006578 else
6579 vim_snprintf((char *)match_ref, sizeof(match_ref),
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006580 _("match %d"),
6581 compl_curr_match->cp_number);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006582 edit_submode_extra = match_ref;
6583 edit_submode_highl = HLF_R;
6584 if (dollar_vcol >= 0)
6585 curs_columns(FALSE);
6586 }
6587 }
6588 }
6589
6590 // Show a message about what (completion) mode we're in.
6591 if (!compl_opt_suppress_empty)
6592 {
6593 showmode();
6594 if (!shortmess(SHM_COMPLETIONMENU))
6595 {
6596 if (edit_submode_extra != NULL)
6597 {
6598 if (!p_smd)
Bram Moolenaarcc233582020-12-12 13:32:07 +01006599 {
6600 msg_hist_off = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006601 msg_attr((char *)edit_submode_extra,
6602 edit_submode_highl < HLF_COUNT
6603 ? HL_ATTR(edit_submode_highl) : 0);
Bram Moolenaarcc233582020-12-12 13:32:07 +01006604 msg_hist_off = FALSE;
6605 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006606 }
6607 else
6608 msg_clr_cmdline(); // necessary for "noshowmode"
6609 }
6610 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006611}
6612
6613/*
6614 * Do Insert mode completion.
6615 * Called when character "c" was typed, which has a meaning for completion.
6616 * Returns OK if completion was done, FAIL if something failed (out of mem).
6617 */
6618 int
6619ins_complete(int c, int enable_pum)
6620{
6621 int n;
6622 int save_w_wrow;
6623 int save_w_leftcol;
6624 int insert_match;
6625
6626 compl_direction = ins_compl_key2dir(c);
6627 insert_match = ins_compl_use_match(c);
6628
6629 if (!compl_started)
6630 {
6631 if (ins_compl_start() == FAIL)
6632 return FAIL;
6633 }
6634 else if (insert_match && stop_arrow() == FAIL)
6635 return FAIL;
6636
glepnircf7f0122025-04-15 19:02:00 +02006637 compl_curr_win = curwin;
6638 compl_curr_buf = curwin->w_buffer;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006639 compl_shown_match = compl_curr_match;
6640 compl_shows_dir = compl_direction;
6641
6642 // Find next match (and following matches).
6643 save_w_wrow = curwin->w_wrow;
6644 save_w_leftcol = curwin->w_leftcol;
6645 n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE);
6646
6647 // may undisplay the popup menu
6648 ins_compl_upd_pum();
6649
6650 if (n > 1) // all matches have been found
6651 compl_matches = n;
6652 compl_curr_match = compl_shown_match;
6653 compl_direction = compl_shows_dir;
6654
6655 // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
6656 // mode.
6657 if (got_int && !global_busy)
6658 {
6659 (void)vgetc();
6660 got_int = FALSE;
6661 }
6662
6663 // we found no match if the list has only the "compl_orig_text"-entry
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006664 if (is_first_match(compl_first_match->cp_next))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006665 {
6666 // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
6667 // because we couldn't expand anything at first place, but if we used
6668 // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
6669 // (such as M in M'exico) if not tried already. -- Acevedo
6670 if (compl_length > 1
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006671 || compl_status_adding()
6672 || (ctrl_x_mode_not_default()
6673 && !ctrl_x_mode_path_patterns()
6674 && !ctrl_x_mode_path_defines()))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006675 compl_cont_status &= ~CONT_N_ADDS;
6676 }
6677
6678 if (compl_curr_match->cp_flags & CP_CONT_S_IPOS)
6679 compl_cont_status |= CONT_S_IPOS;
6680 else
6681 compl_cont_status &= ~CONT_S_IPOS;
6682
6683 ins_compl_show_statusmsg();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006684
6685 // Show the popup menu, unless we got interrupted.
6686 if (enable_pum && !compl_interrupted)
6687 show_pum(save_w_wrow, save_w_leftcol);
6688
6689 compl_was_interrupted = compl_interrupted;
6690 compl_interrupted = FALSE;
6691
6692 return OK;
6693}
6694
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00006695/*
6696 * Remove (if needed) and show the popup menu
6697 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006698 static void
6699show_pum(int prev_w_wrow, int prev_w_leftcol)
6700{
6701 // RedrawingDisabled may be set when invoked through complete().
Bram Moolenaar79cdf022023-05-20 14:07:00 +01006702 int save_RedrawingDisabled = RedrawingDisabled;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006703 RedrawingDisabled = 0;
6704
6705 // If the cursor moved or the display scrolled we need to remove the pum
6706 // first.
6707 setcursor();
6708 if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol)
6709 ins_compl_del_pum();
6710
6711 ins_compl_show_pum();
6712 setcursor();
Bram Moolenaar79cdf022023-05-20 14:07:00 +01006713
6714 RedrawingDisabled = save_RedrawingDisabled;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006715}
6716
6717/*
6718 * Looks in the first "len" chars. of "src" for search-metachars.
6719 * If dest is not NULL the chars. are copied there quoting (with
6720 * a backslash) the metachars, and dest would be NUL terminated.
6721 * Returns the length (needed) of dest
6722 */
6723 static unsigned
6724quote_meta(char_u *dest, char_u *src, int len)
6725{
6726 unsigned m = (unsigned)len + 1; // one extra for the NUL
6727
6728 for ( ; --len >= 0; src++)
6729 {
6730 switch (*src)
6731 {
6732 case '.':
6733 case '*':
6734 case '[':
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006735 if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006736 break;
6737 // FALLTHROUGH
6738 case '~':
Bram Moolenaarf4e20992020-12-21 19:59:08 +01006739 if (!magic_isset()) // quote these only if magic is set
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006740 break;
6741 // FALLTHROUGH
6742 case '\\':
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006743 if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006744 break;
6745 // FALLTHROUGH
6746 case '^': // currently it's not needed.
6747 case '$':
6748 m++;
6749 if (dest != NULL)
6750 *dest++ = '\\';
6751 break;
6752 }
6753 if (dest != NULL)
6754 *dest++ = *src;
6755 // Copy remaining bytes of a multibyte character.
6756 if (has_mbyte)
6757 {
glepnir19e1dd62025-05-08 22:50:38 +02006758 int mb_len = (*mb_ptr2len)(src) - 1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006759 if (mb_len > 0 && len >= mb_len)
glepnir19e1dd62025-05-08 22:50:38 +02006760 for (int i = 0; i < mb_len; ++i)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006761 {
6762 --len;
6763 ++src;
6764 if (dest != NULL)
6765 *dest++ = *src;
6766 }
6767 }
6768 }
6769 if (dest != NULL)
6770 *dest = NUL;
6771
6772 return m;
6773}
6774
Bram Moolenaare2c453d2019-08-21 14:37:09 +02006775#if defined(EXITFREE) || defined(PROTO)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006776 void
6777free_insexpand_stuff(void)
6778{
John Marriott5e6ea922024-11-23 14:01:57 +01006779 VIM_CLEAR_STRING(compl_orig_text);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00006780# ifdef FEAT_EVAL
6781 free_callback(&cfu_cb);
6782 free_callback(&ofu_cb);
6783 free_callback(&tsrfu_cb);
6784# endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006785}
Bram Moolenaare2c453d2019-08-21 14:37:09 +02006786#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006787
Bram Moolenaare2c453d2019-08-21 14:37:09 +02006788#ifdef FEAT_SPELL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006789/*
6790 * Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly
6791 * spelled word, if there is one.
6792 */
6793 static void
6794spell_back_to_badword(void)
6795{
6796 pos_T tpos = curwin->w_cursor;
6797
Christ van Willegen - van Noort8e4c4c72024-05-17 18:49:27 +02006798 spell_bad_len = spell_move_to(curwin, BACKWARD, SMT_ALL, TRUE, NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006799 if (curwin->w_cursor.col != tpos.col)
6800 start_arrow(&tpos);
6801}
Bram Moolenaare2c453d2019-08-21 14:37:09 +02006802#endif
Girish Palyacbe53192025-04-14 22:13:15 +02006803
6804/*
6805 * Reset the info associated with completion sources.
6806 */
6807 static void
Girish Palya0ac1eb32025-04-16 20:18:33 +02006808cpt_sources_clear(void)
Girish Palyacbe53192025-04-14 22:13:15 +02006809{
Girish Palya0ac1eb32025-04-16 20:18:33 +02006810 VIM_CLEAR(cpt_sources_array);
6811 cpt_sources_index = -1;
6812 cpt_sources_count = 0;
Girish Palyacbe53192025-04-14 22:13:15 +02006813}
6814
6815/*
6816 * Initialize the info associated with completion sources.
6817 */
6818 static int
Girish Palya0ac1eb32025-04-16 20:18:33 +02006819cpt_sources_init(void)
Girish Palyacbe53192025-04-14 22:13:15 +02006820{
Girish Palya0ac1eb32025-04-16 20:18:33 +02006821 char_u buf[LSIZE];
6822 int slen;
Girish Palyacbe53192025-04-14 22:13:15 +02006823 int count = 0;
Girish Palya0ac1eb32025-04-16 20:18:33 +02006824 char_u *p;
Girish Palyacbe53192025-04-14 22:13:15 +02006825
Girish Palya0ac1eb32025-04-16 20:18:33 +02006826 for (p = curbuf->b_p_cpt; *p;)
Girish Palyacbe53192025-04-14 22:13:15 +02006827 {
6828 while (*p == ',' || *p == ' ') // Skip delimiters
6829 p++;
6830 if (*p) // If not end of string, count this segment
6831 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02006832 (void)copy_option_part(&p, buf, LSIZE, ","); // Advance p
Girish Palyacbe53192025-04-14 22:13:15 +02006833 count++;
Girish Palyacbe53192025-04-14 22:13:15 +02006834 }
6835 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02006836 cpt_sources_clear();
6837 cpt_sources_count = count;
Girish Palyacbe53192025-04-14 22:13:15 +02006838 if (count > 0)
6839 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02006840 cpt_sources_array = ALLOC_CLEAR_MULT(cpt_source_T, count);
6841 if (cpt_sources_array == NULL)
Girish Palyacbe53192025-04-14 22:13:15 +02006842 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02006843 cpt_sources_count = 0;
Girish Palyacbe53192025-04-14 22:13:15 +02006844 return FAIL;
6845 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02006846 count = 0;
6847 for (p = curbuf->b_p_cpt; *p;)
6848 {
6849 while (*p == ',' || *p == ' ') // Skip delimiters
6850 p++;
6851 if (*p) // If not end of string, count this segment
6852 {
6853 char_u *t;
6854
6855 vim_memset(buf, 0, LSIZE);
6856 slen = copy_option_part(&p, buf, LSIZE, ","); // Advance p
6857 if (slen > 0 && (t = vim_strchr(buf, '^')) != NULL)
6858 cpt_sources_array[count].max_matches = atoi((char *)t + 1);
6859 count++;
6860 }
6861 }
Girish Palyacbe53192025-04-14 22:13:15 +02006862 }
6863 return OK;
6864}
6865
6866/*
6867 * Return TRUE if any of the completion sources have 'refresh' set to 'always'.
6868 */
6869 static int
6870is_cpt_func_refresh_always(void)
6871{
6872#ifdef FEAT_COMPL_FUNC
glepnir19e1dd62025-05-08 22:50:38 +02006873 for (int i = 0; i < cpt_sources_count; i++)
Girish Palya0ac1eb32025-04-16 20:18:33 +02006874 if (cpt_sources_array[i].refresh_always)
Girish Palyacbe53192025-04-14 22:13:15 +02006875 return TRUE;
6876#endif
6877 return FALSE;
6878}
6879
6880/*
6881 * Make the completion list non-cyclic.
6882 */
6883#ifdef FEAT_COMPL_FUNC
6884 static void
6885ins_compl_make_linear(void)
6886{
6887 compl_T *m;
6888
6889 if (compl_first_match == NULL || compl_first_match->cp_prev == NULL)
6890 return;
6891 m = compl_first_match->cp_prev;
6892 m->cp_next = NULL;
6893 compl_first_match->cp_prev = NULL;
6894}
6895#endif
6896
6897/*
6898 * Remove the matches linked to the current completion source (as indicated by
Girish Palya0ac1eb32025-04-16 20:18:33 +02006899 * cpt_sources_index) from the completion list.
Girish Palyacbe53192025-04-14 22:13:15 +02006900 */
6901#ifdef FEAT_COMPL_FUNC
6902 static compl_T *
6903remove_old_matches(void)
6904{
6905 compl_T *sublist_start = NULL, *sublist_end = NULL, *insert_at = NULL;
6906 compl_T *current, *next;
Girish Palya0ac1eb32025-04-16 20:18:33 +02006907 int compl_shown_removed = FALSE;
6908 int forward = (compl_first_match->cp_cpt_source_idx < 0);
6909
6910 compl_direction = forward ? FORWARD : BACKWARD;
6911 compl_shows_dir = compl_direction;
Girish Palyacbe53192025-04-14 22:13:15 +02006912
6913 // Identify the sublist of old matches that needs removal
6914 for (current = compl_first_match; current != NULL; current = current->cp_next)
6915 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02006916 if (current->cp_cpt_source_idx < cpt_sources_index &&
6917 (forward || (!forward && !insert_at)))
Girish Palyacbe53192025-04-14 22:13:15 +02006918 insert_at = current;
6919
Girish Palya0ac1eb32025-04-16 20:18:33 +02006920 if (current->cp_cpt_source_idx == cpt_sources_index)
Girish Palyacbe53192025-04-14 22:13:15 +02006921 {
6922 if (!sublist_start)
6923 sublist_start = current;
6924 sublist_end = current;
6925 if (!compl_shown_removed && compl_shown_match == current)
6926 compl_shown_removed = TRUE;
6927 }
6928
Girish Palya0ac1eb32025-04-16 20:18:33 +02006929 if ((forward && current->cp_cpt_source_idx > cpt_sources_index)
6930 || (!forward && insert_at))
Girish Palyacbe53192025-04-14 22:13:15 +02006931 break;
6932 }
6933
6934 // Re-assign compl_shown_match if necessary
6935 if (compl_shown_removed)
6936 {
6937 if (forward)
6938 compl_shown_match = compl_first_match;
6939 else
6940 { // Last node will have the prefix that is being completed
Girish Palya0ac1eb32025-04-16 20:18:33 +02006941 for (current = compl_first_match; current->cp_next != NULL;
6942 current = current->cp_next)
Girish Palyacbe53192025-04-14 22:13:15 +02006943 ;
6944 compl_shown_match = current;
6945 }
6946 }
6947
6948 if (!sublist_start) // No nodes to remove
6949 return insert_at;
6950
6951 // Update links to remove sublist
6952 if (sublist_start->cp_prev)
6953 sublist_start->cp_prev->cp_next = sublist_end->cp_next;
6954 else
6955 compl_first_match = sublist_end->cp_next;
6956
6957 if (sublist_end->cp_next)
6958 sublist_end->cp_next->cp_prev = sublist_start->cp_prev;
6959
6960 // Free all nodes in the sublist
6961 sublist_end->cp_next = NULL;
6962 for (current = sublist_start; current != NULL; current = next)
6963 {
6964 next = current->cp_next;
6965 ins_compl_item_free(current);
6966 }
6967
6968 return insert_at;
6969}
6970#endif
6971
6972/*
6973 * Retrieve completion matches using the callback function "cb" and store the
6974 * 'refresh:always' flag.
6975 */
6976#ifdef FEAT_COMPL_FUNC
6977 static void
6978get_cpt_func_completion_matches(callback_T *cb UNUSED)
6979{
glepnir19e1dd62025-05-08 22:50:38 +02006980 int ret;
6981 int startcol;
Girish Palyacbe53192025-04-14 22:13:15 +02006982
6983 VIM_CLEAR_STRING(cpt_compl_pattern);
6984 ret = get_userdefined_compl_info(curwin->w_cursor.col, cb, &startcol);
6985 if (ret == FAIL && startcol == -3)
Girish Palya0ac1eb32025-04-16 20:18:33 +02006986 cpt_sources_array[cpt_sources_index].refresh_always = FALSE;
Girish Palyacbe53192025-04-14 22:13:15 +02006987 else if (ret == OK)
6988 {
6989 expand_by_function(0, cpt_compl_pattern.string, cb);
Girish Palya0ac1eb32025-04-16 20:18:33 +02006990 cpt_sources_array[cpt_sources_index].refresh_always =
6991 compl_opt_refresh_always;
Girish Palyacbe53192025-04-14 22:13:15 +02006992 compl_opt_refresh_always = FALSE;
6993 }
6994}
6995#endif
6996
6997/*
6998 * Retrieve completion matches from functions in the 'cpt' option where the
6999 * 'refresh:always' flag is set.
7000 */
7001 static void
7002cpt_compl_refresh(void)
7003{
7004#ifdef FEAT_COMPL_FUNC
7005 char_u *cpt;
7006 char_u *p;
Christian Brabandtd2079cf2025-04-15 18:10:26 +02007007 callback_T *cb = NULL;
Girish Palyacbe53192025-04-14 22:13:15 +02007008
7009 // Make the completion list linear (non-cyclic)
7010 ins_compl_make_linear();
7011 // Make a copy of 'cpt' in case the buffer gets wiped out
7012 cpt = vim_strsave(curbuf->b_p_cpt);
Girish Palya0ac1eb32025-04-16 20:18:33 +02007013 strip_caret_numbers_in_place(cpt);
Girish Palyacbe53192025-04-14 22:13:15 +02007014
Girish Palya0ac1eb32025-04-16 20:18:33 +02007015 cpt_sources_index = 0;
Girish Palya7c621052025-05-26 19:41:59 +02007016 for (p = cpt; *p;)
Girish Palyacbe53192025-04-14 22:13:15 +02007017 {
7018 while (*p == ',' || *p == ' ') // Skip delimiters
7019 p++;
7020
Girish Palya0ac1eb32025-04-16 20:18:33 +02007021 if (cpt_sources_array[cpt_sources_index].refresh_always)
Girish Palyacbe53192025-04-14 22:13:15 +02007022 {
7023 if (*p == 'o')
7024 cb = &curbuf->b_ofu_cb;
Girish Palya14f6da52025-05-26 19:04:25 +02007025 else if (*p == 'F')
Girish Palyacbe53192025-04-14 22:13:15 +02007026 cb = (*(p + 1) != ',' && *(p + 1) != NUL)
7027 ? get_cpt_func_callback(p + 1) : &curbuf->b_cfu_cb;
7028 if (cb)
7029 {
7030 compl_curr_match = remove_old_matches();
7031 get_cpt_func_completion_matches(cb);
7032 }
7033 }
7034
Girish Palya7c621052025-05-26 19:41:59 +02007035 (void)copy_option_part(&p, IObuff, IOSIZE, ","); // Advance p
7036 if (may_advance_cpt_index(p))
7037 (void)advance_cpt_sources_index_safe();
Girish Palyacbe53192025-04-14 22:13:15 +02007038 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02007039 cpt_sources_index = -1;
Girish Palyacbe53192025-04-14 22:13:15 +02007040
7041 vim_free(cpt);
7042 // Make the list cyclic
7043 compl_matches = ins_compl_make_cyclic();
7044#endif
7045}