blob: 3839586dbb728cfc4ea382322f5cf21320d96e68 [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
Bram Moolenaar7591bb32019-03-30 13:53:47 +010041
42# define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
43
44// Message for CTRL-X mode, index is ctrl_x_mode.
45static char *ctrl_x_msgs[] =
46{
47 N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl.
48 N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"),
49 NULL, // CTRL_X_SCROLL: depends on state
50 N_(" Whole line completion (^L^N^P)"),
51 N_(" File name completion (^F^N^P)"),
52 N_(" Tag completion (^]^N^P)"),
53 N_(" Path pattern completion (^N^P)"),
54 N_(" Definition completion (^D^N^P)"),
55 NULL, // CTRL_X_FINISHED
56 N_(" Dictionary completion (^K^N^P)"),
57 N_(" Thesaurus completion (^T^N^P)"),
58 N_(" Command-line completion (^V^N^P)"),
59 N_(" User defined completion (^U^N^P)"),
60 N_(" Omni completion (^O^N^P)"),
zeertzjq7002c052024-06-21 07:55:07 +020061 N_(" Spelling suggestion (^S^N^P)"),
Bram Moolenaar7591bb32019-03-30 13:53:47 +010062 N_(" Keyword Local completion (^N^P)"),
63 NULL, // CTRL_X_EVAL doesn't use msg.
zeertzjqdca29d92021-08-31 19:12:51 +020064 N_(" Command-line completion (^V^N^P)"),
Bram Moolenaar7591bb32019-03-30 13:53:47 +010065};
66
Bram Moolenaar9cb698d2019-08-21 15:30:45 +020067#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +010068static char *ctrl_x_mode_names[] = {
Hirohito Higashi355db992025-04-28 18:07:02 +020069 "keyword",
70 "ctrl_x",
71 "scroll",
72 "whole_line",
73 "files",
74 "tags",
75 "path_patterns",
76 "path_defines",
77 "unknown", // CTRL_X_FINISHED
78 "dictionary",
79 "thesaurus",
80 "cmdline",
81 "function",
82 "omni",
83 "spell",
84 NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
85 "eval",
86 "cmdline",
Bram Moolenaar7591bb32019-03-30 13:53:47 +010087};
Bram Moolenaar9cb698d2019-08-21 15:30:45 +020088#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +010089
90/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +010091 * Structure used to store one match for insert completion.
92 */
93typedef struct compl_S compl_T;
94struct compl_S
95{
96 compl_T *cp_next;
97 compl_T *cp_prev;
glepnir80b66202024-11-27 21:53:53 +010098 compl_T *cp_match_next; // matched next compl_T
John Marriott5e6ea922024-11-23 14:01:57 +010099 string_T cp_str; // matched text
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200100 char_u *(cp_text[CPT_COUNT]); // text for the menu
Bram Moolenaarab782c52020-01-04 19:00:11 +0100101#ifdef FEAT_EVAL
Bram Moolenaar08928322020-01-04 14:32:48 +0100102 typval_T cp_user_data;
Bram Moolenaarab782c52020-01-04 19:00:11 +0100103#endif
glepnir0fe17f82024-10-08 22:26:44 +0200104 char_u *cp_fname; // file containing the match, allocated when
105 // cp_flags has CP_FREE_FNAME
106 int cp_flags; // CP_ values
107 int cp_number; // sequence number
Girish Palyab1565882025-04-15 20:16:00 +0200108 int cp_score; // fuzzy match score or proximity score
glepnird4088ed2024-12-31 10:55:22 +0100109 int cp_in_match_array; // collected by compl_match_array
glepnir7baa0142024-10-09 20:19:25 +0200110 int cp_user_abbr_hlattr; // highlight attribute for abbr
glepnir0fe17f82024-10-08 22:26:44 +0200111 int cp_user_kind_hlattr; // highlight attribute for kind
Girish Palya0ac1eb32025-04-16 20:18:33 +0200112 int cp_cpt_source_idx; // index of this match's source in 'cpt' option
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100113};
114
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200115// values for cp_flags
116# define CP_ORIGINAL_TEXT 1 // the original text when the expansion begun
117# define CP_FREE_FNAME 2 // cp_fname is allocated
118# define CP_CONT_S_IPOS 4 // use CONT_S_IPOS for compl_cont_status
119# define CP_EQUAL 8 // ins_compl_equal() always returns TRUE
120# define CP_ICASE 16 // ins_compl_equal() ignores case
Bram Moolenaar440cf092021-04-03 20:13:30 +0200121# define CP_FAST 32 // use fast_breakcheck instead of ui_breakcheck
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100122
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100123/*
124 * All the current matches are stored in a list.
125 * "compl_first_match" points to the start of the list.
126 * "compl_curr_match" points to the currently selected entry.
127 * "compl_shown_match" is different from compl_curr_match during
Girish Palyacbe53192025-04-14 22:13:15 +0200128 * ins_compl_get_exp(), when new matches are added to the list.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000129 * "compl_old_match" points to previous "compl_curr_match".
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100130 */
131static compl_T *compl_first_match = NULL;
132static compl_T *compl_curr_match = NULL;
133static compl_T *compl_shown_match = NULL;
134static compl_T *compl_old_match = NULL;
135
glepnirf31cfa22025-03-06 21:59:13 +0100136// list used to store the compl_T which have the max score
137// used for completefuzzycollect
138static compl_T **compl_best_matches = NULL;
139static int compl_num_bests = 0;
140// inserted a longest when completefuzzycollect enabled
141static int compl_cfc_longest_ins = FALSE;
142
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100143// After using a cursor key <Enter> selects a match in the popup menu,
144// otherwise it inserts a line break.
145static int compl_enter_selects = FALSE;
146
147// When "compl_leader" is not NULL only matches that start with this string
148// are used.
John Marriott5e6ea922024-11-23 14:01:57 +0100149static string_T compl_leader = {NULL, 0};
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100150
151static int compl_get_longest = FALSE; // put longest common string
152 // in compl_leader
153
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100154// Selected one of the matches. When FALSE the match was edited or using the
155// longest common string.
156static int compl_used_match;
157
158// didn't finish finding completions.
159static int compl_was_interrupted = FALSE;
160
161// Set when character typed while looking for matches and it means we should
162// stop looking for matches.
163static int compl_interrupted = FALSE;
164
165static int compl_restarting = FALSE; // don't insert match
166
167// When the first completion is done "compl_started" is set. When it's
168// FALSE the word to be completed must be located.
169static int compl_started = FALSE;
170
171// Which Ctrl-X mode are we in?
172static int ctrl_x_mode = CTRL_X_NORMAL;
173
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000174static int compl_matches = 0; // number of completion matches
Girish Palyacbe53192025-04-14 22:13:15 +0200175static string_T compl_pattern = {NULL, 0}; // search pattern for matching items
176#ifdef FEAT_COMPL_FUNC
177static string_T cpt_compl_pattern = {NULL, 0}; // pattern returned by func in 'cpt'
178#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100179static int compl_direction = FORWARD;
180static int compl_shows_dir = FORWARD;
181static int compl_pending = 0; // > 1 for postponed CTRL-N
182static pos_T compl_startpos;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000183// Length in bytes of the text being completed (this is deleted to be replaced
184// by the match.)
185static int compl_length = 0;
glepnir76bdb822025-02-08 19:04:51 +0100186static linenr_T compl_lnum = 0; // lnum where the completion start
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100187static colnr_T compl_col = 0; // column where the text starts
188 // that is being completed
glepnir6a38aff2024-12-16 21:56:16 +0100189static colnr_T compl_ins_end_col = 0;
John Marriott5e6ea922024-11-23 14:01:57 +0100190static string_T compl_orig_text = {NULL, 0}; // text as it was before
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100191 // completion started
192static int compl_cont_mode = 0;
193static expand_T compl_xp;
194
glepnircf7f0122025-04-15 19:02:00 +0200195static win_T *compl_curr_win = NULL; // win where completion is active
196static buf_T *compl_curr_buf = NULL; // buf where completion is active
197
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000198// List of flags for method of completion.
199static int compl_cont_status = 0;
200# define CONT_ADDING 1 // "normal" or "adding" expansion
201# define CONT_INTRPT (2 + 4) // a ^X interrupted the current expansion
202 // it's set only iff N_ADDS is set
203# define CONT_N_ADDS 4 // next ^X<> will add-new or expand-current
204# define CONT_S_IPOS 8 // next ^X<> will set initial_pos?
205 // if so, word-wise-expansion will set SOL
206# define CONT_SOL 16 // pattern includes start of line, just for
207 // word-wise expansion, not set for ^X^L
208# define CONT_LOCAL 32 // for ctrl_x_mode 0, ^X^P/^X^N do a local
209 // expansion, (eg use complete=.)
210
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100211static int compl_opt_refresh_always = FALSE;
212static int compl_opt_suppress_empty = FALSE;
213
glepnira218cc62024-06-03 19:32:39 +0200214static int compl_selected_item = -1;
215
glepnir8159fb12024-07-17 20:32:54 +0200216static int *compl_fuzzy_scores;
217
Girish Palya0ac1eb32025-04-16 20:18:33 +0200218// Define the structure for completion source (in 'cpt' option) information
219typedef struct cpt_source_T
220{
221 int refresh_always; // Flag array to indicate which 'cpt' functions have 'refresh:always' set
222 int max_matches; // Maximum number of items to display in the menu from the source
223} cpt_source_T;
224
225static cpt_source_T *cpt_sources_array; // Pointer to the array of completion sources
226static int cpt_sources_count; // Total number of completion sources specified in the 'cpt' option
227static int cpt_sources_index; // Index of the current completion source being expanded
Girish Palyacbe53192025-04-14 22:13:15 +0200228
glepnir6a38aff2024-12-16 21:56:16 +0100229// "compl_match_array" points the currently displayed list of entries in the
230// popup menu. It is NULL when there is no popup menu.
231static pumitem_T *compl_match_array = NULL;
232static int compl_match_arraysize;
233
glepnirf31cfa22025-03-06 21:59:13 +0100234static 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 +0100235static void ins_compl_longest_match(compl_T *match);
236static void ins_compl_del_pum(void);
237static 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 +0100238static void ins_compl_free(void);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100239static int ins_compl_need_restart(void);
240static void ins_compl_new_leader(void);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000241static int get_compl_len(void);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100242static void ins_compl_restart(void);
John Marriott5e6ea922024-11-23 14:01:57 +0100243static void ins_compl_set_original_text(char_u *str, size_t len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100244static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg);
245# if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
246static void ins_compl_add_list(list_T *list);
247static void ins_compl_add_dict(dict_T *dict);
Girish Palyacbe53192025-04-14 22:13:15 +0200248static int get_userdefined_compl_info(colnr_T curs_col, callback_T *cb, int *startcol);
Girish Palyacbe53192025-04-14 22:13:15 +0200249static void get_cpt_func_completion_matches(callback_T *cb);
Girish Palya0ac1eb32025-04-16 20:18:33 +0200250static callback_T *get_cpt_func_callback(char_u *funcname);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100251# endif
Girish Palya0ac1eb32025-04-16 20:18:33 +0200252static int cpt_sources_init(void);
Girish Palyacbe53192025-04-14 22:13:15 +0200253static int is_cpt_func_refresh_always(void);
Girish Palya0ac1eb32025-04-16 20:18:33 +0200254static void cpt_sources_clear(void);
Girish Palyacbe53192025-04-14 22:13:15 +0200255static void cpt_compl_refresh(void);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100256static int ins_compl_key2dir(int c);
257static int ins_compl_pum_key(int c);
258static int ins_compl_key2count(int c);
259static void show_pum(int prev_w_wrow, int prev_w_leftcol);
260static unsigned quote_meta(char_u *dest, char_u *str, int len);
glepnir76bdb822025-02-08 19:04:51 +0100261static int ins_compl_has_multiple(void);
262static void ins_compl_expand_multiple(char_u *str);
glepnirf31cfa22025-03-06 21:59:13 +0100263static void ins_compl_longest_insert(char_u *prefix);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100264
265#ifdef FEAT_SPELL
266static void spell_back_to_badword(void);
267static int spell_bad_len = 0; // length of located bad word
268#endif
269
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100270/*
271 * CTRL-X pressed in Insert mode.
272 */
273 void
274ins_ctrl_x(void)
275{
zeertzjqdca29d92021-08-31 19:12:51 +0200276 if (!ctrl_x_mode_cmdline())
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100277 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000278 // if the next ^X<> won't ADD nothing, then reset compl_cont_status
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100279 if (compl_cont_status & CONT_N_ADDS)
280 compl_cont_status |= CONT_INTRPT;
281 else
282 compl_cont_status = 0;
283 // We're not sure which CTRL-X mode it will be yet
284 ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
285 edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
286 edit_submode_pre = NULL;
287 showmode();
288 }
zeertzjqdca29d92021-08-31 19:12:51 +0200289 else
290 // CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X
291 // CTRL-V look like CTRL-N
292 ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +0100293
LemonBoy2bf52dd2022-04-09 18:17:34 +0100294 may_trigger_modechanged();
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100295}
296
297/*
298 * Functions to check the current CTRL-X mode.
299 */
Bram Moolenaarebfec1c2023-01-22 21:14:53 +0000300int ctrl_x_mode_none(void)
301 { return ctrl_x_mode == 0; }
302int ctrl_x_mode_normal(void)
303 { return ctrl_x_mode == CTRL_X_NORMAL; }
304int ctrl_x_mode_scroll(void)
305 { return ctrl_x_mode == CTRL_X_SCROLL; }
306int ctrl_x_mode_whole_line(void)
307 { return ctrl_x_mode == CTRL_X_WHOLE_LINE; }
308int ctrl_x_mode_files(void)
309 { return ctrl_x_mode == CTRL_X_FILES; }
310int ctrl_x_mode_tags(void)
311 { return ctrl_x_mode == CTRL_X_TAGS; }
312int ctrl_x_mode_path_patterns(void)
313 { return ctrl_x_mode == CTRL_X_PATH_PATTERNS; }
314int ctrl_x_mode_path_defines(void)
315 { return ctrl_x_mode == CTRL_X_PATH_DEFINES; }
316int ctrl_x_mode_dictionary(void)
317 { return ctrl_x_mode == CTRL_X_DICTIONARY; }
318int ctrl_x_mode_thesaurus(void)
319 { return ctrl_x_mode == CTRL_X_THESAURUS; }
320int ctrl_x_mode_cmdline(void)
321 { return ctrl_x_mode == CTRL_X_CMDLINE
zeertzjqdca29d92021-08-31 19:12:51 +0200322 || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X; }
Bram Moolenaarebfec1c2023-01-22 21:14:53 +0000323int ctrl_x_mode_function(void)
324 { return ctrl_x_mode == CTRL_X_FUNCTION; }
325int ctrl_x_mode_omni(void)
326 { return ctrl_x_mode == CTRL_X_OMNI; }
327int ctrl_x_mode_spell(void)
328 { return ctrl_x_mode == CTRL_X_SPELL; }
329static int ctrl_x_mode_eval(void)
330 { return ctrl_x_mode == CTRL_X_EVAL; }
331int ctrl_x_mode_line_or_eval(void)
332 { return ctrl_x_mode == CTRL_X_WHOLE_LINE || ctrl_x_mode == CTRL_X_EVAL; }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100333
334/*
335 * Whether other than default completion has been selected.
336 */
337 int
338ctrl_x_mode_not_default(void)
339{
340 return ctrl_x_mode != CTRL_X_NORMAL;
341}
342
343/*
zeertzjqdca29d92021-08-31 19:12:51 +0200344 * Whether CTRL-X was typed without a following character,
345 * not including when in CTRL-X CTRL-V mode.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100346 */
347 int
348ctrl_x_mode_not_defined_yet(void)
349{
350 return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET;
351}
352
353/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000354 * Return TRUE if currently in "normal" or "adding" insert completion matches
355 * state
356 */
357 int
358compl_status_adding(void)
359{
360 return compl_cont_status & CONT_ADDING;
361}
362
363/*
364 * Return TRUE if the completion pattern includes start of line, just for
365 * word-wise expansion.
366 */
367 int
368compl_status_sol(void)
369{
370 return compl_cont_status & CONT_SOL;
371}
372
373/*
374 * Return TRUE if ^X^P/^X^N will do a local completion (i.e. use complete=.)
375 */
376 int
377compl_status_local(void)
378{
379 return compl_cont_status & CONT_LOCAL;
380}
381
382/*
383 * Clear the completion status flags
384 */
385 void
386compl_status_clear(void)
387{
388 compl_cont_status = 0;
389}
390
391/*
392 * Return TRUE if completion is using the forward direction matches
393 */
394 static int
395compl_dir_forward(void)
396{
397 return compl_direction == FORWARD;
398}
399
400/*
401 * Return TRUE if currently showing forward completion matches
402 */
403 static int
404compl_shows_dir_forward(void)
405{
406 return compl_shows_dir == FORWARD;
407}
408
409/*
410 * Return TRUE if currently showing backward completion matches
411 */
412 static int
413compl_shows_dir_backward(void)
414{
415 return compl_shows_dir == BACKWARD;
416}
417
418/*
419 * Return TRUE if the 'dictionary' or 'thesaurus' option can be used.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100420 */
421 int
422has_compl_option(int dict_opt)
423{
424 if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL
Bram Moolenaare2c453d2019-08-21 14:37:09 +0200425#ifdef FEAT_SPELL
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100426 && !curwin->w_p_spell
Bram Moolenaare2c453d2019-08-21 14:37:09 +0200427#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100428 )
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +0100429 : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL
430#ifdef FEAT_COMPL_FUNC
Bram Moolenaarf4d8b762021-10-17 14:13:09 +0100431 && *curbuf->b_p_tsrfu == NUL && *p_tsrfu == NUL
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +0100432#endif
433 ))
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100434 {
435 ctrl_x_mode = CTRL_X_NORMAL;
436 edit_submode = NULL;
437 msg_attr(dict_opt ? _("'dictionary' option is empty")
438 : _("'thesaurus' option is empty"),
439 HL_ATTR(HLF_E));
Bram Moolenaar28ee8922020-10-28 20:20:00 +0100440 if (emsg_silent == 0 && !in_assert_fails)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100441 {
442 vim_beep(BO_COMPL);
443 setcursor();
444 out_flush();
445#ifdef FEAT_EVAL
446 if (!get_vim_var_nr(VV_TESTING))
447#endif
Bram Moolenaareda1da02019-11-17 17:06:33 +0100448 ui_delay(2004L, FALSE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100449 }
450 return FALSE;
451 }
452 return TRUE;
453}
454
455/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000456 * Is the character "c" a valid key to go to or keep us in CTRL-X mode?
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100457 * This depends on the current mode.
458 */
459 int
460vim_is_ctrl_x_key(int c)
461{
462 // Always allow ^R - let its results then be checked
463 if (c == Ctrl_R)
464 return TRUE;
465
466 // Accept <PageUp> and <PageDown> if the popup menu is visible.
467 if (ins_compl_pum_key(c))
468 return TRUE;
469
470 switch (ctrl_x_mode)
471 {
472 case 0: // Not in any CTRL-X mode
473 return (c == Ctrl_N || c == Ctrl_P || c == Ctrl_X);
474 case CTRL_X_NOT_DEFINED_YET:
zeertzjqdca29d92021-08-31 19:12:51 +0200475 case CTRL_X_CMDLINE_CTRL_X:
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100476 return ( c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E
477 || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB
478 || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P
479 || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V
480 || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O
zeertzjqdca29d92021-08-31 19:12:51 +0200481 || c == Ctrl_S || c == Ctrl_K || c == 's'
482 || c == Ctrl_Z);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100483 case CTRL_X_SCROLL:
484 return (c == Ctrl_Y || c == Ctrl_E);
485 case CTRL_X_WHOLE_LINE:
486 return (c == Ctrl_L || c == Ctrl_P || c == Ctrl_N);
487 case CTRL_X_FILES:
488 return (c == Ctrl_F || c == Ctrl_P || c == Ctrl_N);
489 case CTRL_X_DICTIONARY:
490 return (c == Ctrl_K || c == Ctrl_P || c == Ctrl_N);
491 case CTRL_X_THESAURUS:
492 return (c == Ctrl_T || c == Ctrl_P || c == Ctrl_N);
493 case CTRL_X_TAGS:
494 return (c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N);
495#ifdef FEAT_FIND_ID
496 case CTRL_X_PATH_PATTERNS:
497 return (c == Ctrl_P || c == Ctrl_N);
498 case CTRL_X_PATH_DEFINES:
499 return (c == Ctrl_D || c == Ctrl_P || c == Ctrl_N);
500#endif
501 case CTRL_X_CMDLINE:
502 return (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N
503 || c == Ctrl_X);
504#ifdef FEAT_COMPL_FUNC
505 case CTRL_X_FUNCTION:
506 return (c == Ctrl_U || c == Ctrl_P || c == Ctrl_N);
507 case CTRL_X_OMNI:
508 return (c == Ctrl_O || c == Ctrl_P || c == Ctrl_N);
509#endif
510 case CTRL_X_SPELL:
511 return (c == Ctrl_S || c == Ctrl_P || c == Ctrl_N);
512 case CTRL_X_EVAL:
513 return (c == Ctrl_P || c == Ctrl_N);
514 }
515 internal_error("vim_is_ctrl_x_key()");
516 return FALSE;
517}
518
519/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000520 * Return TRUE if "match" is the original text when the completion began.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000521 */
522 static int
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000523match_at_original_text(compl_T *match)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000524{
525 return match->cp_flags & CP_ORIGINAL_TEXT;
526}
527
528/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000529 * Returns TRUE if "match" is the first match in the completion list.
530 */
531 static int
532is_first_match(compl_T *match)
533{
534 return match == compl_first_match;
535}
536
537/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100538 * Return TRUE when character "c" is part of the item currently being
539 * completed. Used to decide whether to abandon complete mode when the menu
540 * is visible.
541 */
542 int
543ins_compl_accept_char(int c)
544{
545 if (ctrl_x_mode & CTRL_X_WANT_IDENT)
546 // When expanding an identifier only accept identifier chars.
547 return vim_isIDc(c);
548
549 switch (ctrl_x_mode)
550 {
551 case CTRL_X_FILES:
552 // When expanding file name only accept file name chars. But not
553 // path separators, so that "proto/<Tab>" expands files in
554 // "proto", not "proto/" as a whole
555 return vim_isfilec(c) && !vim_ispathsep(c);
556
557 case CTRL_X_CMDLINE:
zeertzjqdca29d92021-08-31 19:12:51 +0200558 case CTRL_X_CMDLINE_CTRL_X:
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100559 case CTRL_X_OMNI:
560 // Command line and Omni completion can work with just about any
561 // printable character, but do stop at white space.
562 return vim_isprintc(c) && !VIM_ISWHITE(c);
563
564 case CTRL_X_WHOLE_LINE:
565 // For while line completion a space can be part of the line.
566 return vim_isprintc(c);
567 }
568 return vim_iswordc(c);
569}
570
571/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000572 * Get the completed text by inferring the case of the originally typed text.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100573 * If the result is in allocated memory "tofree" is set to it.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000574 */
575 static char_u *
576ins_compl_infercase_gettext(
glepnir19e1dd62025-05-08 22:50:38 +0200577 char_u *str,
578 int char_len,
579 int compl_char_len,
580 int min_len,
581 char_u **tofree)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000582{
583 int *wca; // Wide character array.
584 char_u *p;
585 int i, c;
586 int has_lower = FALSE;
587 int was_letter = FALSE;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100588 garray_T gap;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000589
590 IObuff[0] = NUL;
591
592 // Allocate wide character array for the completion and fill it.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100593 wca = ALLOC_MULT(int, char_len);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000594 if (wca == NULL)
595 return IObuff;
596
597 p = str;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100598 for (i = 0; i < char_len; ++i)
glepnir6e199932024-12-14 21:13:27 +0100599 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000600 if (has_mbyte)
601 wca[i] = mb_ptr2char_adv(&p);
602 else
603 wca[i] = *(p++);
glepnir6e199932024-12-14 21:13:27 +0100604 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000605
606 // Rule 1: Were any chars converted to lower?
John Marriott5e6ea922024-11-23 14:01:57 +0100607 p = compl_orig_text.string;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000608 for (i = 0; i < min_len; ++i)
609 {
glepnir6e199932024-12-14 21:13:27 +0100610 c = has_mbyte ? mb_ptr2char_adv(&p) : *(p++);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000611 if (MB_ISLOWER(c))
612 {
613 has_lower = TRUE;
614 if (MB_ISUPPER(wca[i]))
615 {
616 // Rule 1 is satisfied.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100617 for (i = compl_char_len; i < char_len; ++i)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000618 wca[i] = MB_TOLOWER(wca[i]);
619 break;
620 }
621 }
622 }
623
624 // Rule 2: No lower case, 2nd consecutive letter converted to
625 // upper case.
626 if (!has_lower)
627 {
John Marriott5e6ea922024-11-23 14:01:57 +0100628 p = compl_orig_text.string;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000629 for (i = 0; i < min_len; ++i)
630 {
glepnir6e199932024-12-14 21:13:27 +0100631 c = has_mbyte ? mb_ptr2char_adv(&p) : *(p++);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000632 if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i]))
633 {
634 // Rule 2 is satisfied.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100635 for (i = compl_char_len; i < char_len; ++i)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000636 wca[i] = MB_TOUPPER(wca[i]);
637 break;
638 }
639 was_letter = MB_ISLOWER(c) || MB_ISUPPER(c);
640 }
641 }
642
643 // Copy the original case of the part we typed.
John Marriott5e6ea922024-11-23 14:01:57 +0100644 p = compl_orig_text.string;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000645 for (i = 0; i < min_len; ++i)
646 {
glepnir6e199932024-12-14 21:13:27 +0100647 c = has_mbyte ? mb_ptr2char_adv(&p) : *(p++);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000648 if (MB_ISLOWER(c))
649 wca[i] = MB_TOLOWER(wca[i]);
650 else if (MB_ISUPPER(c))
651 wca[i] = MB_TOUPPER(wca[i]);
652 }
653
654 // Generate encoding specific output from wide character array.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000655 p = IObuff;
656 i = 0;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100657 ga_init2(&gap, 1, 500);
658 while (i < char_len)
659 {
660 if (gap.ga_data != NULL)
661 {
662 if (ga_grow(&gap, 10) == FAIL)
663 {
664 ga_clear(&gap);
665 return (char_u *)"[failed]";
666 }
667 p = (char_u *)gap.ga_data + gap.ga_len;
668 if (has_mbyte)
669 gap.ga_len += (*mb_char2bytes)(wca[i++], p);
670 else
671 {
672 *p = wca[i++];
673 ++gap.ga_len;
674 }
675 }
676 else if ((p - IObuff) + 6 >= IOSIZE)
677 {
678 // Multi-byte characters can occupy up to five bytes more than
679 // ASCII characters, and we also need one byte for NUL, so when
680 // getting to six bytes from the edge of IObuff switch to using a
681 // growarray. Add the character in the next round.
682 if (ga_grow(&gap, IOSIZE) == FAIL)
zeertzjq70e566b2024-03-21 07:11:58 +0100683 {
684 vim_free(wca);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100685 return (char_u *)"[failed]";
zeertzjq70e566b2024-03-21 07:11:58 +0100686 }
Bram Moolenaarb9e71732022-07-23 06:53:08 +0100687 *p = NUL;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100688 STRCPY(gap.ga_data, IObuff);
John Marriott5e6ea922024-11-23 14:01:57 +0100689 gap.ga_len = (int)(p - IObuff);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100690 }
691 else if (has_mbyte)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000692 p += (*mb_char2bytes)(wca[i++], p);
693 else
694 *(p++) = wca[i++];
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100695 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000696 vim_free(wca);
697
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100698 if (gap.ga_data != NULL)
699 {
700 *tofree = gap.ga_data;
701 return gap.ga_data;
702 }
703
704 *p = NUL;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000705 return IObuff;
706}
707
708/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100709 * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
710 * case of the originally typed text is used, and the case of the completed
711 * text is inferred, ie this tries to work out what case you probably wanted
712 * the rest of the word to be in -- webb
713 */
714 int
715ins_compl_add_infercase(
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200716 char_u *str_arg,
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100717 int len,
718 int icase,
719 char_u *fname,
720 int dir,
glepnirf31cfa22025-03-06 21:59:13 +0100721 int cont_s_ipos, // next ^X<> will set initial_pos
722 int score)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100723{
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200724 char_u *str = str_arg;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100725 char_u *p;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100726 int char_len; // count multi-byte characters
727 int compl_char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100728 int min_len;
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200729 int flags = 0;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100730 int res;
731 char_u *tofree = NULL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100732
733 if (p_ic && curbuf->b_p_inf && len > 0)
734 {
735 // Infer case of completed part.
736
737 // Find actual length of completion.
738 if (has_mbyte)
739 {
740 p = str;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100741 char_len = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100742 while (*p != NUL)
743 {
744 MB_PTR_ADV(p);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100745 ++char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100746 }
747 }
748 else
glepnir6e199932024-12-14 21:13:27 +0100749 {
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100750 char_len = len;
glepnir6e199932024-12-14 21:13:27 +0100751 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100752
753 // Find actual length of original text.
754 if (has_mbyte)
755 {
John Marriott5e6ea922024-11-23 14:01:57 +0100756 p = compl_orig_text.string;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100757 compl_char_len = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100758 while (*p != NUL)
759 {
760 MB_PTR_ADV(p);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100761 ++compl_char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100762 }
763 }
764 else
glepnir6e199932024-12-14 21:13:27 +0100765 {
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100766 compl_char_len = compl_length;
glepnir6e199932024-12-14 21:13:27 +0100767 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100768
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100769 // "char_len" may be smaller than "compl_char_len" when using
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100770 // thesaurus, only use the minimum when comparing.
glepnir6e199932024-12-14 21:13:27 +0100771 min_len = MIN(char_len, compl_char_len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100772
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100773 str = ins_compl_infercase_gettext(str, char_len,
774 compl_char_len, min_len, &tofree);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100775 }
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200776 if (cont_s_ipos)
777 flags |= CP_CONT_S_IPOS;
778 if (icase)
779 flags |= CP_ICASE;
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200780
glepnirf31cfa22025-03-06 21:59:13 +0100781 res = ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE, NULL, score);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100782 vim_free(tofree);
783 return res;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100784}
785
786/*
glepnirf31cfa22025-03-06 21:59:13 +0100787 * Check if ctrl_x_mode has been configured in 'completefuzzycollect'
788 */
789 static int
790cfc_has_mode(void)
791{
glepnir58760162025-03-13 21:39:51 +0100792 if (ctrl_x_mode_normal() || ctrl_x_mode_dictionary())
793 return (cfc_flags & CFC_KEYWORD) != 0;
794 else if (ctrl_x_mode_files())
795 return (cfc_flags & CFC_FILES) != 0;
796 else if (ctrl_x_mode_whole_line())
797 return (cfc_flags & CFC_WHOLELINE) != 0;
798 else
799 return FALSE;
glepnirf31cfa22025-03-06 21:59:13 +0100800}
801
802/*
Girish Palyab1565882025-04-15 20:16:00 +0200803 * Returns TRUE if matches should be sorted based on proximity to the cursor.
804 */
805 static int
806is_nearest_active(void)
807{
glepnirc3fbaa02025-05-08 23:05:10 +0200808 return (get_cot_flags() & (COT_NEAREST | COT_FUZZY)) == COT_NEAREST;
Girish Palyab1565882025-04-15 20:16:00 +0200809}
810
811/*
812 * Repositions a match in the completion list based on its proximity score.
813 * If the match is at the head and has a higher score than the next node,
814 * or if it's in the middle/tail and has a lower score than the previous node,
815 * it is moved to the correct position while maintaining ascending order.
816 */
817 static void
818reposition_match(compl_T *match)
819{
820 compl_T *insert_before = NULL;
821 compl_T *insert_after = NULL;
822
823 // Node is at head and score is too big
824 if (!match->cp_prev)
825 {
826 if (match->cp_next && match->cp_next->cp_score > 0 &&
827 match->cp_next->cp_score < match->cp_score)
828 {
829 // <c-p>: compl_first_match is at head and newly inserted node
830 compl_first_match = compl_curr_match = match->cp_next;
831 // Find the correct position in ascending order
832 insert_before = match->cp_next;
833 do
834 {
835 insert_after = insert_before;
836 insert_before = insert_before->cp_next;
837 } while (insert_before && insert_before->cp_score > 0 &&
838 insert_before->cp_score < match->cp_score);
839 }
840 else
841 return;
842 }
843 // Node is at tail or in the middle but score is too small
844 else
845 {
846 if (match->cp_prev->cp_score > 0 && match->cp_prev->cp_score > match->cp_score)
847 {
848 // <c-n>: compl_curr_match (and newly inserted match) is at tail
849 if (!match->cp_next)
850 compl_curr_match = compl_curr_match->cp_prev;
851 // Find the correct position in ascending order
852 insert_after = match->cp_prev;
853 do
854 {
855 insert_before = insert_after;
856 insert_after = insert_after->cp_prev;
857 } while (insert_after && insert_after->cp_score > 0 &&
858 insert_after->cp_score > match->cp_score);
859 }
860 else
861 return;
862 }
863
864 if (insert_after)
865 {
866 // Remove the match from its current position
867 if (match->cp_prev)
868 match->cp_prev->cp_next = match->cp_next;
869 else
870 compl_first_match = match->cp_next;
871 if (match->cp_next)
872 match->cp_next->cp_prev = match->cp_prev;
873
874 // Insert the match at the correct position
875 match->cp_next = insert_before;
876 match->cp_prev = insert_after;
877 insert_after->cp_next = match;
878 insert_before->cp_prev = match;
879 }
880}
881
882/*
Yegappan Lakshmanan37079142022-01-08 10:38:48 +0000883 * Add a match to the list of matches. The arguments are:
884 * str - text of the match to add
885 * len - length of "str". If -1, then the length of "str" is
886 * computed.
887 * fname - file name to associate with this match.
888 * cptext - list of strings to use with this match (for abbr, menu, info
889 * and kind)
890 * user_data - user supplied data (any vim type) for this match
891 * cdir - match direction. If 0, use "compl_direction".
892 * flags_arg - match flags (cp_flags)
893 * adup - accept this match even if it is already present.
glepnir80b66202024-11-27 21:53:53 +0100894 * *user_hl - list of extra highlight attributes for abbr kind.
Yegappan Lakshmanan37079142022-01-08 10:38:48 +0000895 * If "cdir" is FORWARD, then the match is added after the current match.
896 * Otherwise, it is added before the current match.
897 *
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100898 * If the given string is already in the list of completions, then return
899 * NOTDONE, otherwise add it to the list and return OK. If there is an error,
900 * maybe because alloc() returns NULL, then FAIL is returned.
901 */
902 static int
903ins_compl_add(
904 char_u *str,
905 int len,
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100906 char_u *fname,
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100907 char_u **cptext, // extra text for popup menu or NULL
908 typval_T *user_data UNUSED, // "user_data" entry or NULL
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100909 int cdir,
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200910 int flags_arg,
glepnir508e7852024-07-25 21:39:08 +0200911 int adup, // accept duplicate match
glepnirf31cfa22025-03-06 21:59:13 +0100912 int *user_hl, // user abbr/kind hlattr
913 int score)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100914{
glepnirf31cfa22025-03-06 21:59:13 +0100915 compl_T *match, *current, *prev;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100916 int dir = (cdir == 0 ? compl_direction : cdir);
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200917 int flags = flags_arg;
glepnirf31cfa22025-03-06 21:59:13 +0100918 int inserted = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100919
Bram Moolenaarceb06192021-04-04 15:05:22 +0200920 if (flags & CP_FAST)
921 fast_breakcheck();
922 else
923 ui_breakcheck();
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100924 if (got_int)
925 return FAIL;
926 if (len < 0)
927 len = (int)STRLEN(str);
928
929 // If the same match is already present, don't add it.
930 if (compl_first_match != NULL && !adup)
931 {
932 match = compl_first_match;
933 do
934 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000935 if (!match_at_original_text(match)
John Marriott5e6ea922024-11-23 14:01:57 +0100936 && STRNCMP(match->cp_str.string, str, len) == 0
937 && ((int)match->cp_str.length <= len
938 || match->cp_str.string[len] == NUL))
Girish Palyab1565882025-04-15 20:16:00 +0200939 {
940 if (is_nearest_active() && score > 0 && score < match->cp_score)
941 {
942 match->cp_score = score;
943 reposition_match(match);
944 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100945 return NOTDONE;
Girish Palyab1565882025-04-15 20:16:00 +0200946 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100947 match = match->cp_next;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000948 } while (match != NULL && !is_first_match(match));
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100949 }
950
951 // Remove any popup menu before changing the list of matches.
952 ins_compl_del_pum();
953
954 // Allocate a new match structure.
955 // Copy the values to the new match structure.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200956 match = ALLOC_CLEAR_ONE(compl_T);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100957 if (match == NULL)
958 return FAIL;
glepnir40891ba2025-02-10 22:18:00 +0100959 match->cp_number = flags & CP_ORIGINAL_TEXT ? 0 : -1;
John Marriott5e6ea922024-11-23 14:01:57 +0100960 if ((match->cp_str.string = vim_strnsave(str, len)) == NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100961 {
962 vim_free(match);
963 return FAIL;
964 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100965
John Marriott5e6ea922024-11-23 14:01:57 +0100966 match->cp_str.length = len;
967
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100968 // match-fname is:
969 // - compl_curr_match->cp_fname if it is a string equal to fname.
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200970 // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100971 // - NULL otherwise. --Acevedo
972 if (fname != NULL
973 && compl_curr_match != NULL
974 && compl_curr_match->cp_fname != NULL
975 && STRCMP(fname, compl_curr_match->cp_fname) == 0)
976 match->cp_fname = compl_curr_match->cp_fname;
977 else if (fname != NULL)
978 {
979 match->cp_fname = vim_strsave(fname);
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200980 flags |= CP_FREE_FNAME;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100981 }
982 else
983 match->cp_fname = NULL;
984 match->cp_flags = flags;
glepnir80b66202024-11-27 21:53:53 +0100985 match->cp_user_abbr_hlattr = user_hl ? user_hl[0] : -1;
986 match->cp_user_kind_hlattr = user_hl ? user_hl[1] : -1;
glepnirf31cfa22025-03-06 21:59:13 +0100987 match->cp_score = score;
Girish Palya0ac1eb32025-04-16 20:18:33 +0200988 match->cp_cpt_source_idx = cpt_sources_index;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100989
990 if (cptext != NULL)
991 {
glepnir19e1dd62025-05-08 22:50:38 +0200992 for (int i = 0; i < CPT_COUNT; ++i)
glepnir6e199932024-12-14 21:13:27 +0100993 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100994 if (cptext[i] != NULL && *cptext[i] != NUL)
995 match->cp_text[i] = vim_strsave(cptext[i]);
glepnir6e199932024-12-14 21:13:27 +0100996 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100997 }
Bram Moolenaarab782c52020-01-04 19:00:11 +0100998#ifdef FEAT_EVAL
Bram Moolenaar08928322020-01-04 14:32:48 +0100999 if (user_data != NULL)
1000 match->cp_user_data = *user_data;
Bram Moolenaarab782c52020-01-04 19:00:11 +01001001#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001002
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00001003 // Link the new match structure after (FORWARD) or before (BACKWARD) the
1004 // current match in the list of matches .
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001005 if (compl_first_match == NULL)
1006 match->cp_next = match->cp_prev = NULL;
glepnirf31cfa22025-03-06 21:59:13 +01001007 else if (cfc_has_mode() && score > 0 && compl_get_longest)
1008 {
1009 current = compl_first_match->cp_next;
1010 prev = compl_first_match;
1011 inserted = FALSE;
1012 // The direction is ignored when using longest and
1013 // completefuzzycollect, because matches are inserted
1014 // and sorted by score.
1015 while (current != NULL && current != compl_first_match)
1016 {
1017 if (current->cp_score < score)
1018 {
Hirohito Higashi355db992025-04-28 18:07:02 +02001019 match->cp_next = current;
1020 match->cp_prev = current->cp_prev;
1021 if (current->cp_prev)
glepnirf31cfa22025-03-06 21:59:13 +01001022 current->cp_prev->cp_next = match;
Hirohito Higashi355db992025-04-28 18:07:02 +02001023 current->cp_prev = match;
1024 inserted = TRUE;
1025 break;
glepnirf31cfa22025-03-06 21:59:13 +01001026 }
1027 prev = current;
1028 current = current->cp_next;
1029 }
1030 if (!inserted)
1031 {
1032 prev->cp_next = match;
1033 match->cp_prev = prev;
1034 match->cp_next = compl_first_match;
1035 compl_first_match->cp_prev = match;
1036 }
1037 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001038 else if (dir == FORWARD)
1039 {
1040 match->cp_next = compl_curr_match->cp_next;
1041 match->cp_prev = compl_curr_match;
1042 }
1043 else // BACKWARD
1044 {
1045 match->cp_next = compl_curr_match;
1046 match->cp_prev = compl_curr_match->cp_prev;
1047 }
1048 if (match->cp_next)
1049 match->cp_next->cp_prev = match;
1050 if (match->cp_prev)
1051 match->cp_prev->cp_next = match;
1052 else // if there's nothing before, it is the first match
1053 compl_first_match = match;
1054 compl_curr_match = match;
1055
Girish Palyab1565882025-04-15 20:16:00 +02001056 if (is_nearest_active() && score > 0)
1057 reposition_match(match);
1058
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001059 // Find the longest common string if still doing that.
glepnirf31cfa22025-03-06 21:59:13 +01001060 if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0 && !cfc_has_mode())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001061 ins_compl_longest_match(match);
1062
1063 return OK;
1064}
1065
1066/*
1067 * Return TRUE if "str[len]" matches with match->cp_str, considering
Bram Moolenaard9eefe32019-04-06 14:22:21 +02001068 * match->cp_flags.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001069 */
1070 static int
1071ins_compl_equal(compl_T *match, char_u *str, int len)
1072{
Bram Moolenaard9eefe32019-04-06 14:22:21 +02001073 if (match->cp_flags & CP_EQUAL)
Bram Moolenaar73655cf2019-04-06 13:45:55 +02001074 return TRUE;
Bram Moolenaard9eefe32019-04-06 14:22:21 +02001075 if (match->cp_flags & CP_ICASE)
John Marriott5e6ea922024-11-23 14:01:57 +01001076 return STRNICMP(match->cp_str.string, str, (size_t)len) == 0;
1077 return STRNCMP(match->cp_str.string, str, (size_t)len) == 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001078}
1079
1080/*
glepnir6a38aff2024-12-16 21:56:16 +01001081 * when len is -1 mean use whole length of p otherwise part of p
1082 */
1083 static void
1084ins_compl_insert_bytes(char_u *p, int len)
1085{
1086 if (len == -1)
1087 len = (int)STRLEN(p);
1088 ins_bytes_len(p, len);
zeertzjqf25d8f92024-12-18 21:12:25 +01001089 compl_ins_end_col = curwin->w_cursor.col;
glepnir6a38aff2024-12-16 21:56:16 +01001090}
1091
1092/*
zeertzjqd32bf0a2024-12-17 20:55:13 +01001093 * Checks if the column is within the currently inserted completion text
1094 * column range. If it is, it returns a special highlight attribute.
glepnir76bdb822025-02-08 19:04:51 +01001095 * -1 means normal item.
glepnir6a38aff2024-12-16 21:56:16 +01001096 */
1097 int
glepnir76bdb822025-02-08 19:04:51 +01001098ins_compl_col_range_attr(linenr_T lnum, int col)
glepnir6a38aff2024-12-16 21:56:16 +01001099{
glepnir76bdb822025-02-08 19:04:51 +01001100 int start_col;
1101 int attr;
1102
1103 if ((get_cot_flags() & COT_FUZZY)
1104 || (attr = syn_name2attr((char_u *)"ComplMatchIns")) == 0)
glepnire8908872025-01-08 18:30:45 +01001105 return -1;
1106
glepnir76bdb822025-02-08 19:04:51 +01001107 start_col = compl_col + (int)ins_compl_leader_len();
1108 if (!ins_compl_has_multiple())
1109 return (col >= start_col && col < compl_ins_end_col) ? attr : -1;
1110
1111 // Multiple lines
1112 if ((lnum == compl_lnum && col >= start_col && col < MAXCOL) ||
1113 (lnum > compl_lnum && lnum < curwin->w_cursor.lnum) ||
1114 (lnum == curwin->w_cursor.lnum && col <= compl_ins_end_col))
1115 return attr;
glepnir6a38aff2024-12-16 21:56:16 +01001116
1117 return -1;
1118}
1119
1120/*
glepnir76bdb822025-02-08 19:04:51 +01001121 * Returns TRUE if the current completion string contains newline characters,
1122 * indicating it's a multi-line completion.
1123 */
1124 static int
1125ins_compl_has_multiple(void)
1126{
1127 return vim_strchr(compl_shown_match->cp_str.string, '\n') != NULL;
1128}
1129
1130/*
1131 * Returns TRUE if the given line number falls within the range of a multi-line
1132 * completion, i.e. between the starting line (compl_lnum) and current cursor
1133 * line. Always returns FALSE for single-line completions.
1134 */
1135 int
1136ins_compl_lnum_in_range(linenr_T lnum)
1137{
1138 if (!ins_compl_has_multiple())
1139 return FALSE;
1140 return lnum >= compl_lnum && lnum <= curwin->w_cursor.lnum;
1141}
1142
1143/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001144 * Reduce the longest common string for match "match".
1145 */
1146 static void
1147ins_compl_longest_match(compl_T *match)
1148{
1149 char_u *p, *s;
1150 int c1, c2;
1151 int had_match;
1152
John Marriott5e6ea922024-11-23 14:01:57 +01001153 if (compl_leader.string == NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001154 {
1155 // First match, use it as a whole.
John Marriott5e6ea922024-11-23 14:01:57 +01001156 compl_leader.string = vim_strnsave(match->cp_str.string, match->cp_str.length);
1157 if (compl_leader.string == NULL)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001158 return;
1159
John Marriott5e6ea922024-11-23 14:01:57 +01001160 compl_leader.length = match->cp_str.length;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001161 had_match = (curwin->w_cursor.col > compl_col);
glepnirf31cfa22025-03-06 21:59:13 +01001162 ins_compl_longest_insert(compl_leader.string);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001163
1164 // When the match isn't there (to avoid matching itself) remove it
1165 // again after redrawing.
1166 if (!had_match)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001167 ins_compl_delete();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001168 compl_used_match = FALSE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001169
1170 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001171 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001172
1173 // Reduce the text if this match differs from compl_leader.
John Marriott5e6ea922024-11-23 14:01:57 +01001174 p = compl_leader.string;
1175 s = match->cp_str.string;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001176 while (*p != NUL)
1177 {
1178 if (has_mbyte)
1179 {
1180 c1 = mb_ptr2char(p);
1181 c2 = mb_ptr2char(s);
1182 }
1183 else
1184 {
1185 c1 = *p;
1186 c2 = *s;
1187 }
1188 if ((match->cp_flags & CP_ICASE)
1189 ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) : (c1 != c2))
1190 break;
1191 if (has_mbyte)
1192 {
1193 MB_PTR_ADV(p);
1194 MB_PTR_ADV(s);
1195 }
1196 else
1197 {
1198 ++p;
1199 ++s;
1200 }
1201 }
1202
1203 if (*p != NUL)
1204 {
1205 // Leader was shortened, need to change the inserted text.
1206 *p = NUL;
John Marriott5e6ea922024-11-23 14:01:57 +01001207 compl_leader.length = (size_t)(p - compl_leader.string);
1208
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001209 had_match = (curwin->w_cursor.col > compl_col);
glepnirf31cfa22025-03-06 21:59:13 +01001210 ins_compl_longest_insert(compl_leader.string);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001211
1212 // When the match isn't there (to avoid matching itself) remove it
1213 // again after redrawing.
1214 if (!had_match)
1215 ins_compl_delete();
1216 }
1217
1218 compl_used_match = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001219}
1220
1221/*
1222 * Add an array of matches to the list of matches.
1223 * Frees matches[].
1224 */
1225 static void
1226ins_compl_add_matches(
1227 int num_matches,
1228 char_u **matches,
1229 int icase)
1230{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001231 int add_r = OK;
1232 int dir = compl_direction;
1233
glepnir19e1dd62025-05-08 22:50:38 +02001234 for (int i = 0; i < num_matches && add_r != FAIL; i++)
glepnir6e199932024-12-14 21:13:27 +01001235 {
1236 add_r = ins_compl_add(matches[i], -1, NULL, NULL, NULL, dir,
glepnirf31cfa22025-03-06 21:59:13 +01001237 CP_FAST | (icase ? CP_ICASE : 0), FALSE, NULL, 0);
glepnir6e199932024-12-14 21:13:27 +01001238 if (add_r == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001239 // if dir was BACKWARD then honor it just once
1240 dir = FORWARD;
glepnir6e199932024-12-14 21:13:27 +01001241 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001242 FreeWild(num_matches, matches);
1243}
1244
1245/*
1246 * Make the completion list cyclic.
1247 * Return the number of matches (excluding the original).
1248 */
1249 static int
1250ins_compl_make_cyclic(void)
1251{
1252 compl_T *match;
1253 int count = 0;
1254
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001255 if (compl_first_match == NULL)
1256 return 0;
1257
1258 // Find the end of the list.
1259 match = compl_first_match;
1260 // there's always an entry for the compl_orig_text, it doesn't count.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001261 while (match->cp_next != NULL && !is_first_match(match->cp_next))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001262 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001263 match = match->cp_next;
1264 ++count;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001265 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001266 match->cp_next = compl_first_match;
1267 compl_first_match->cp_prev = match;
1268
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001269 return count;
1270}
1271
1272/*
1273 * Return whether there currently is a shown match.
1274 */
1275 int
1276ins_compl_has_shown_match(void)
1277{
1278 return compl_shown_match == NULL
1279 || compl_shown_match != compl_shown_match->cp_next;
1280}
1281
1282/*
1283 * Return whether the shown match is long enough.
1284 */
1285 int
1286ins_compl_long_shown_match(void)
1287{
John Marriott5e6ea922024-11-23 14:01:57 +01001288 return (int)compl_shown_match->cp_str.length
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001289 > curwin->w_cursor.col - compl_col;
1290}
1291
1292/*
zeertzjq529b9ad2024-06-05 20:27:06 +02001293 * Get the local or global value of 'completeopt' flags.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001294 */
zeertzjq529b9ad2024-06-05 20:27:06 +02001295 unsigned int
1296get_cot_flags(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001297{
zeertzjq529b9ad2024-06-05 20:27:06 +02001298 return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001299}
1300
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001301/*
1302 * Update the screen and when there is any scrolling remove the popup menu.
1303 */
1304 static void
1305ins_compl_upd_pum(void)
1306{
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001307 if (compl_match_array == NULL)
1308 return;
1309
glepnir19e1dd62025-05-08 22:50:38 +02001310 int h = curwin->w_cline_height;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001311 // Update the screen later, before drawing the popup menu over it.
1312 pum_call_update_screen();
1313 if (h != curwin->w_cline_height)
1314 ins_compl_del_pum();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001315}
1316
1317/*
1318 * Remove any popup menu.
1319 */
1320 static void
1321ins_compl_del_pum(void)
1322{
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001323 if (compl_match_array == NULL)
1324 return;
1325
1326 pum_undisplay();
1327 VIM_CLEAR(compl_match_array);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001328}
1329
1330/*
1331 * Return TRUE if the popup menu should be displayed.
1332 */
1333 int
1334pum_wanted(void)
1335{
1336 // 'completeopt' must contain "menu" or "menuone"
zeertzjq529b9ad2024-06-05 20:27:06 +02001337 if ((get_cot_flags() & COT_ANY_MENU) == 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001338 return FALSE;
1339
1340 // The display looks bad on a B&W display.
1341 if (t_colors < 8
1342#ifdef FEAT_GUI
1343 && !gui.in_use
1344#endif
1345 )
1346 return FALSE;
1347 return TRUE;
1348}
1349
1350/*
1351 * Return TRUE if there are two or more matches to be shown in the popup menu.
1352 * One if 'completopt' contains "menuone".
1353 */
1354 static int
1355pum_enough_matches(void)
1356{
1357 compl_T *compl;
glepnir6e199932024-12-14 21:13:27 +01001358 int i = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001359
1360 // Don't display the popup menu if there are no matches or there is only
1361 // one (ignoring the original text).
1362 compl = compl_first_match;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001363 do
1364 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001365 if (compl == NULL || (!match_at_original_text(compl) && ++i == 2))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001366 break;
1367 compl = compl->cp_next;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001368 } while (!is_first_match(compl));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001369
zeertzjq529b9ad2024-06-05 20:27:06 +02001370 if (get_cot_flags() & COT_MENUONE)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001371 return (i >= 1);
1372 return (i >= 2);
1373}
1374
Bram Moolenaar3075a452021-11-17 15:51:52 +00001375#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001376/*
1377 * Allocate Dict for the completed item.
1378 * { word, abbr, menu, kind, info }
1379 */
1380 static dict_T *
1381ins_compl_dict_alloc(compl_T *match)
1382{
1383 dict_T *dict = dict_alloc_lock(VAR_FIXED);
1384
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001385 if (dict == NULL)
1386 return NULL;
1387
John Marriott5e6ea922024-11-23 14:01:57 +01001388 dict_add_string(dict, "word", match->cp_str.string);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001389 dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]);
1390 dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
1391 dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
1392 dict_add_string(dict, "info", match->cp_text[CPT_INFO]);
1393 if (match->cp_user_data.v_type == VAR_UNKNOWN)
1394 dict_add_string(dict, "user_data", (char_u *)"");
1395 else
1396 dict_add_tv(dict, "user_data", &match->cp_user_data);
1397
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001398 return dict;
1399}
1400
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001401/*
1402 * Trigger the CompleteChanged autocmd event. Invoked each time the Insert mode
1403 * completion menu is changed.
1404 */
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001405 static void
1406trigger_complete_changed_event(int cur)
1407{
1408 dict_T *v_event;
1409 dict_T *item;
1410 static int recursive = FALSE;
Bram Moolenaar3075a452021-11-17 15:51:52 +00001411 save_v_event_T save_v_event;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001412
1413 if (recursive)
1414 return;
1415
glepnir40891ba2025-02-10 22:18:00 +01001416 item = cur < 0 ? dict_alloc() : ins_compl_dict_alloc(compl_curr_match);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001417 if (item == NULL)
1418 return;
Bram Moolenaar3075a452021-11-17 15:51:52 +00001419 v_event = get_v_event(&save_v_event);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001420 dict_add_dict(v_event, "completed_item", item);
1421 pum_set_event_info(v_event);
1422 dict_set_items_ro(v_event);
1423
1424 recursive = TRUE;
zeertzjqcfe45652022-05-27 17:26:55 +01001425 textlock++;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001426 apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, FALSE, curbuf);
zeertzjqcfe45652022-05-27 17:26:55 +01001427 textlock--;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001428 recursive = FALSE;
1429
Bram Moolenaar3075a452021-11-17 15:51:52 +00001430 restore_v_event(v_event, &save_v_event);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001431}
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001432#endif
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001433
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001434/*
glepnira218cc62024-06-03 19:32:39 +02001435 * pumitem qsort compare func
1436 */
1437 static int
zeertzjq8e567472024-06-14 20:04:42 +02001438ins_compl_fuzzy_cmp(const void *a, const void *b)
glepnira218cc62024-06-03 19:32:39 +02001439{
1440 const int sa = (*(pumitem_T *)a).pum_score;
1441 const int sb = (*(pumitem_T *)b).pum_score;
zeertzjq8e567472024-06-14 20:04:42 +02001442 const int ia = (*(pumitem_T *)a).pum_idx;
1443 const int ib = (*(pumitem_T *)b).pum_idx;
1444 return sa == sb ? (ia == ib ? 0 : (ia < ib ? -1 : 1)) : (sa < sb ? 1 : -1);
glepnira218cc62024-06-03 19:32:39 +02001445}
1446
1447/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001448 * Build a popup menu to show the completion matches.
1449 * Returns the popup menu entry that should be selected. Returns -1 if nothing
1450 * should be selected.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001451 */
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001452 static int
1453ins_compl_build_pum(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001454{
1455 compl_T *compl;
1456 compl_T *shown_compl = NULL;
1457 int did_find_shown_match = FALSE;
1458 int shown_match_ok = FALSE;
glepnira49c0772024-11-30 10:56:30 +01001459 int i = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001460 int cur = -1;
glepnira218cc62024-06-03 19:32:39 +02001461 int max_fuzzy_score = 0;
zeertzjqaa925ee2024-06-09 18:24:05 +02001462 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02001463 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
zeertzjqd65aa1b2025-01-25 15:29:03 +01001464 int fuzzy_filter = (cur_cot_flags & COT_FUZZY) != 0;
1465 int fuzzy_sort = fuzzy_filter && !(cur_cot_flags & COT_NOSORT);
glepnir80b66202024-11-27 21:53:53 +01001466 compl_T *match_head = NULL;
1467 compl_T *match_tail = NULL;
1468 compl_T *match_next = NULL;
glepnire4e4d1c2025-04-02 20:18:25 +02001469 int update_shown_match = fuzzy_filter;
Christian Brabandtb53d4fb2025-04-16 21:12:30 +02001470 int match_count = 0;
Girish Palya0ac1eb32025-04-16 20:18:33 +02001471 int cur_source = -1;
1472 int max_matches_found = FALSE;
1473 int is_forward = compl_shows_dir_forward() && !fuzzy_filter;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001474
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001475 // Need to build the popup menu list.
1476 compl_match_arraysize = 0;
1477 compl = compl_first_match;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001478
glepnira49c0772024-11-30 10:56:30 +01001479 // If the current match is the original text don't find the first
1480 // match after it, don't highlight anything.
1481 if (match_at_original_text(compl_shown_match))
1482 shown_match_ok = TRUE;
1483
glepnire4e4d1c2025-04-02 20:18:25 +02001484 if (fuzzy_filter && ctrl_x_mode_normal() && compl_leader.string == NULL
1485 && compl_shown_match->cp_score > 0)
1486 update_shown_match = FALSE;
1487
glepnira49c0772024-11-30 10:56:30 +01001488 if (compl_leader.string != NULL
1489 && STRCMP(compl_leader.string, compl_orig_text.string) == 0
1490 && shown_match_ok == FALSE)
1491 compl_shown_match = compl_no_select ? compl_first_match
1492 : compl_first_match->cp_next;
1493
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001494 do
1495 {
glepnird4088ed2024-12-31 10:55:22 +01001496 compl->cp_in_match_array = FALSE;
zeertzjq551d8c32024-06-05 19:53:32 +02001497 // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
1498 // set the cp_score for later comparisons.
glepnirf400a0c2025-01-23 19:55:14 +01001499 if (fuzzy_filter && compl_leader.string != NULL && compl_leader.length > 0)
John Marriott5e6ea922024-11-23 14:01:57 +01001500 compl->cp_score = fuzzy_match_str(compl->cp_str.string, compl_leader.string);
glepnira218cc62024-06-03 19:32:39 +02001501
Girish Palya0ac1eb32025-04-16 20:18:33 +02001502 if (is_forward && compl->cp_cpt_source_idx != -1)
1503 {
1504 if (cur_source != compl->cp_cpt_source_idx)
1505 {
1506 cur_source = compl->cp_cpt_source_idx;
1507 match_count = 1;
1508 max_matches_found = FALSE;
1509 }
1510 else if (cpt_sources_array && !max_matches_found)
1511 {
1512 int max_matches = cpt_sources_array[cur_source].max_matches;
1513 if (max_matches > 0 && match_count > max_matches)
1514 max_matches_found = TRUE;
1515 }
1516 }
1517
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001518 if (!match_at_original_text(compl)
Girish Palya0ac1eb32025-04-16 20:18:33 +02001519 && !max_matches_found
John Marriott5e6ea922024-11-23 14:01:57 +01001520 && (compl_leader.string == NULL
1521 || ins_compl_equal(compl, compl_leader.string, (int)compl_leader.length)
glepnirf400a0c2025-01-23 19:55:14 +01001522 || (fuzzy_filter && compl->cp_score > 0)))
glepnir80b66202024-11-27 21:53:53 +01001523 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001524 ++compl_match_arraysize;
glepnird4088ed2024-12-31 10:55:22 +01001525 compl->cp_in_match_array = TRUE;
glepnir80b66202024-11-27 21:53:53 +01001526 if (match_head == NULL)
1527 match_head = compl;
1528 else
glepnira49c0772024-11-30 10:56:30 +01001529 match_tail->cp_match_next = compl;
glepnir80b66202024-11-27 21:53:53 +01001530 match_tail = compl;
glepnira49c0772024-11-30 10:56:30 +01001531
glepnirf400a0c2025-01-23 19:55:14 +01001532 if (!shown_match_ok && !fuzzy_filter)
glepnira49c0772024-11-30 10:56:30 +01001533 {
1534 if (compl == compl_shown_match || did_find_shown_match)
1535 {
1536 // This item is the shown match or this is the
1537 // first displayed item after the shown match.
1538 compl_shown_match = compl;
1539 did_find_shown_match = TRUE;
1540 shown_match_ok = TRUE;
1541 }
1542 else
1543 // Remember this displayed match for when the
1544 // shown match is just below it.
1545 shown_compl = compl;
1546 cur = i;
1547 }
glepnirf400a0c2025-01-23 19:55:14 +01001548 else if (fuzzy_filter)
glepnira49c0772024-11-30 10:56:30 +01001549 {
1550 if (i == 0)
1551 shown_compl = compl;
1552 // Update the maximum fuzzy score and the shown match
1553 // if the current item's score is higher
glepnire4e4d1c2025-04-02 20:18:25 +02001554 if (fuzzy_sort && compl->cp_score > max_fuzzy_score
1555 && update_shown_match)
glepnira49c0772024-11-30 10:56:30 +01001556 {
1557 did_find_shown_match = TRUE;
1558 max_fuzzy_score = compl->cp_score;
1559 if (!compl_no_select)
1560 compl_shown_match = compl;
1561 }
1562
glepnir3af0a8d2025-02-20 22:06:16 +01001563 if (!shown_match_ok && compl == compl_shown_match)
glepnira49c0772024-11-30 10:56:30 +01001564 {
1565 cur = i;
1566 shown_match_ok = TRUE;
1567 }
1568 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02001569 if (is_forward && compl->cp_cpt_source_idx != -1)
1570 match_count++;
glepnira49c0772024-11-30 10:56:30 +01001571 i++;
glepnir80b66202024-11-27 21:53:53 +01001572 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001573
glepnirf400a0c2025-01-23 19:55:14 +01001574 if (compl == compl_shown_match && !fuzzy_filter)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001575 {
1576 did_find_shown_match = TRUE;
1577
1578 // When the original text is the shown match don't set
1579 // compl_shown_match.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001580 if (match_at_original_text(compl))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001581 shown_match_ok = TRUE;
1582
1583 if (!shown_match_ok && shown_compl != NULL)
1584 {
1585 // The shown match isn't displayed, set it to the
1586 // previously displayed match.
1587 compl_shown_match = shown_compl;
1588 shown_match_ok = TRUE;
1589 }
1590 }
glepnira49c0772024-11-30 10:56:30 +01001591 compl = compl->cp_next;
1592 } while (compl != NULL && !is_first_match(compl));
1593
1594 if (compl_match_arraysize == 0)
1595 return -1;
1596
glepnirc0b7ca42025-02-13 20:27:44 +01001597 if (fuzzy_filter && !fuzzy_sort && !compl_no_select && !shown_match_ok)
1598 {
1599 compl_shown_match = shown_compl;
1600 shown_match_ok = TRUE;
1601 cur = 0;
1602 }
1603
glepnira49c0772024-11-30 10:56:30 +01001604 compl_match_array = ALLOC_CLEAR_MULT(pumitem_T, compl_match_arraysize);
1605 if (compl_match_array == NULL)
1606 return -1;
1607
1608 compl = match_head;
1609 i = 0;
1610 while (compl != NULL)
1611 {
glepnir6e199932024-12-14 21:13:27 +01001612 compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR] != NULL
1613 ? compl->cp_text[CPT_ABBR] : compl->cp_str.string;
glepnira49c0772024-11-30 10:56:30 +01001614 compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
1615 compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
1616 compl_match_array[i].pum_score = compl->cp_score;
1617 compl_match_array[i].pum_user_abbr_hlattr = compl->cp_user_abbr_hlattr;
1618 compl_match_array[i].pum_user_kind_hlattr = compl->cp_user_kind_hlattr;
glepnir6e199932024-12-14 21:13:27 +01001619 compl_match_array[i++].pum_extra = compl->cp_text[CPT_MENU] != NULL
1620 ? compl->cp_text[CPT_MENU] : compl->cp_fname;
glepnir80b66202024-11-27 21:53:53 +01001621 match_next = compl->cp_match_next;
1622 compl->cp_match_next = NULL;
1623 compl = match_next;
1624 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001625
zeertzjqd65aa1b2025-01-25 15:29:03 +01001626 if (fuzzy_sort && compl_leader.string != NULL && compl_leader.length > 0)
zeertzjq8e567472024-06-14 20:04:42 +02001627 {
1628 for (i = 0; i < compl_match_arraysize; i++)
1629 compl_match_array[i].pum_idx = i;
glepnira218cc62024-06-03 19:32:39 +02001630 // sort by the largest score of fuzzy match
zeertzjq8e567472024-06-14 20:04:42 +02001631 qsort(compl_match_array, (size_t)compl_match_arraysize,
1632 sizeof(pumitem_T), ins_compl_fuzzy_cmp);
glepnir65407ce2024-07-06 16:09:19 +02001633 shown_match_ok = TRUE;
zeertzjq8e567472024-06-14 20:04:42 +02001634 }
glepnira218cc62024-06-03 19:32:39 +02001635
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001636 if (!shown_match_ok) // no displayed match at all
1637 cur = -1;
1638
1639 return cur;
1640}
1641
1642/*
1643 * Show the popup menu for the list of matches.
1644 * Also adjusts "compl_shown_match" to an entry that is actually displayed.
1645 */
1646 void
1647ins_compl_show_pum(void)
1648{
1649 int i;
1650 int cur = -1;
1651 colnr_T col;
1652
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001653 if (!pum_wanted() || !pum_enough_matches())
1654 return;
1655
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001656 // Update the screen later, before drawing the popup menu over it.
1657 pum_call_update_screen();
1658
1659 if (compl_match_array == NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001660 // Need to build the popup menu list.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001661 cur = ins_compl_build_pum();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001662 else
1663 {
1664 // popup menu already exists, only need to find the current item.
1665 for (i = 0; i < compl_match_arraysize; ++i)
glepnir19e1dd62025-05-08 22:50:38 +02001666 {
John Marriott5e6ea922024-11-23 14:01:57 +01001667 if (compl_match_array[i].pum_text == compl_shown_match->cp_str.string
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001668 || compl_match_array[i].pum_text
1669 == compl_shown_match->cp_text[CPT_ABBR])
1670 {
1671 cur = i;
1672 break;
1673 }
glepnir19e1dd62025-05-08 22:50:38 +02001674 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001675 }
1676
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001677 if (compl_match_array == NULL)
glepnir0d3c0a62024-02-11 17:52:40 +01001678 {
1679#ifdef FEAT_EVAL
1680 if (compl_started && has_completechanged())
1681 trigger_complete_changed_event(cur);
1682#endif
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001683 return;
glepnir0d3c0a62024-02-11 17:52:40 +01001684 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001685
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001686 // In Replace mode when a $ is displayed at the end of the line only
1687 // part of the screen would be updated. We do need to redraw here.
1688 dollar_vcol = -1;
1689
1690 // Compute the screen column of the start of the completed text.
1691 // Use the cursor to get all wrapping and other settings right.
1692 col = curwin->w_cursor.col;
1693 curwin->w_cursor.col = compl_col;
glepnira218cc62024-06-03 19:32:39 +02001694 compl_selected_item = cur;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001695 pum_display(compl_match_array, compl_match_arraysize, cur);
1696 curwin->w_cursor.col = col;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001697
glepnircbb46b42024-02-03 18:11:13 +01001698 // After adding leader, set the current match to shown match.
1699 if (compl_started && compl_curr_match != compl_shown_match)
1700 compl_curr_match = compl_shown_match;
1701
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001702#ifdef FEAT_EVAL
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001703 if (has_completechanged())
1704 trigger_complete_changed_event(cur);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001705#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001706}
1707
1708#define DICT_FIRST (1) // use just first element in "dict"
1709#define DICT_EXACT (2) // "dict" is the exact name of a file
1710
1711/*
glepnir40c1c332024-06-11 19:37:04 +02001712 * Get current completion leader
1713 */
1714 char_u *
1715ins_compl_leader(void)
1716{
John Marriott5e6ea922024-11-23 14:01:57 +01001717 return compl_leader.string != NULL ? compl_leader.string : compl_orig_text.string;
1718}
1719
1720/*
1721 * Get current completion leader length
1722 */
1723 size_t
1724ins_compl_leader_len(void)
1725{
1726 return compl_leader.string != NULL ? compl_leader.length : compl_orig_text.length;
glepnir40c1c332024-06-11 19:37:04 +02001727}
1728
1729/*
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001730 * Add any identifiers that match the given pattern "pat" in the list of
1731 * dictionary files "dict_start" to the list of completions.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001732 */
1733 static void
1734ins_compl_dictionaries(
1735 char_u *dict_start,
1736 char_u *pat,
1737 int flags, // DICT_FIRST and/or DICT_EXACT
1738 int thesaurus) // Thesaurus completion
1739{
1740 char_u *dict = dict_start;
1741 char_u *ptr;
1742 char_u *buf;
1743 regmatch_T regmatch;
1744 char_u **files;
1745 int count;
1746 int save_p_scs;
1747 int dir = compl_direction;
1748
1749 if (*dict == NUL)
1750 {
1751#ifdef FEAT_SPELL
1752 // When 'dictionary' is empty and spell checking is enabled use
1753 // "spell".
1754 if (!thesaurus && curwin->w_p_spell)
1755 dict = (char_u *)"spell";
1756 else
1757#endif
1758 return;
1759 }
1760
1761 buf = alloc(LSIZE);
1762 if (buf == NULL)
1763 return;
1764 regmatch.regprog = NULL; // so that we can goto theend
1765
1766 // If 'infercase' is set, don't use 'smartcase' here
1767 save_p_scs = p_scs;
1768 if (curbuf->b_p_inf)
1769 p_scs = FALSE;
1770
1771 // When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern
1772 // to only match at the start of a line. Otherwise just match the
1773 // pattern. Also need to double backslashes.
1774 if (ctrl_x_mode_line_or_eval())
1775 {
1776 char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\");
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001777
1778 if (pat_esc == NULL)
1779 goto theend;
glepnir19e1dd62025-05-08 22:50:38 +02001780 size_t len = STRLEN(pat_esc) + 10;
Bram Moolenaar964b3742019-05-24 18:54:09 +02001781 ptr = alloc(len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001782 if (ptr == NULL)
1783 {
1784 vim_free(pat_esc);
1785 goto theend;
1786 }
1787 vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc);
1788 regmatch.regprog = vim_regcomp(ptr, RE_MAGIC);
1789 vim_free(pat_esc);
1790 vim_free(ptr);
1791 }
1792 else
1793 {
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001794 regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001795 if (regmatch.regprog == NULL)
1796 goto theend;
1797 }
1798
1799 // ignore case depends on 'ignorecase', 'smartcase' and "pat"
1800 regmatch.rm_ic = ignorecase(pat);
1801 while (*dict != NUL && !got_int && !compl_interrupted)
1802 {
1803 // copy one dictionary file name into buf
1804 if (flags == DICT_EXACT)
1805 {
1806 count = 1;
1807 files = &dict;
1808 }
1809 else
1810 {
1811 // Expand wildcards in the dictionary name, but do not allow
1812 // backticks (for security, the 'dict' option may have been set in
1813 // a modeline).
1814 copy_option_part(&dict, buf, LSIZE, ",");
1815# ifdef FEAT_SPELL
1816 if (!thesaurus && STRCMP(buf, "spell") == 0)
1817 count = -1;
1818 else
1819# endif
1820 if (vim_strchr(buf, '`') != NULL
1821 || expand_wildcards(1, &buf, &count, &files,
1822 EW_FILE|EW_SILENT) != OK)
1823 count = 0;
1824 }
1825
1826# ifdef FEAT_SPELL
1827 if (count == -1)
1828 {
1829 // Complete from active spelling. Skip "\<" in the pattern, we
1830 // don't use it as a RE.
1831 if (pat[0] == '\\' && pat[1] == '<')
1832 ptr = pat + 2;
1833 else
1834 ptr = pat;
1835 spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0);
1836 }
1837 else
1838# endif
1839 if (count > 0) // avoid warning for using "files" uninit
1840 {
1841 ins_compl_files(count, files, thesaurus, flags,
glepnirf31cfa22025-03-06 21:59:13 +01001842 (cfc_has_mode() ? NULL : &regmatch), buf, &dir);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001843 if (flags != DICT_EXACT)
1844 FreeWild(count, files);
1845 }
1846 if (flags != 0)
1847 break;
1848 }
1849
1850theend:
1851 p_scs = save_p_scs;
1852 vim_regfree(regmatch.regprog);
1853 vim_free(buf);
1854}
1855
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001856/*
1857 * Add all the words in the line "*buf_arg" from the thesaurus file "fname"
1858 * skipping the word at 'skip_word'. Returns OK on success.
1859 */
1860 static int
zeertzjq5fb3aab2022-08-24 16:48:23 +01001861thesaurus_add_words_in_line(
glepnir19e1dd62025-05-08 22:50:38 +02001862 char_u *fname,
1863 char_u **buf_arg,
1864 int dir,
1865 char_u *skip_word)
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001866{
1867 int status = OK;
1868 char_u *ptr;
1869 char_u *wstart;
1870
1871 // Add the other matches on the line
1872 ptr = *buf_arg;
1873 while (!got_int)
1874 {
1875 // Find start of the next word. Skip white
1876 // space and punctuation.
1877 ptr = find_word_start(ptr);
1878 if (*ptr == NUL || *ptr == NL)
1879 break;
1880 wstart = ptr;
1881
1882 // Find end of the word.
1883 if (has_mbyte)
1884 // Japanese words may have characters in
1885 // different classes, only separate words
1886 // with single-byte non-word characters.
1887 while (*ptr != NUL)
1888 {
1889 int l = (*mb_ptr2len)(ptr);
1890
1891 if (l < 2 && !vim_iswordc(*ptr))
1892 break;
1893 ptr += l;
1894 }
1895 else
1896 ptr = find_word_end(ptr);
1897
1898 // Add the word. Skip the regexp match.
1899 if (wstart != skip_word)
1900 {
1901 status = ins_compl_add_infercase(wstart, (int)(ptr - wstart), p_ic,
glepnirf31cfa22025-03-06 21:59:13 +01001902 fname, dir, FALSE, 0);
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001903 if (status == FAIL)
1904 break;
1905 }
1906 }
1907
1908 *buf_arg = ptr;
1909 return status;
1910}
1911
1912/*
1913 * Process "count" dictionary/thesaurus "files" and add the text matching
1914 * "regmatch".
1915 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001916 static void
1917ins_compl_files(
1918 int count,
1919 char_u **files,
1920 int thesaurus,
1921 int flags,
1922 regmatch_T *regmatch,
1923 char_u *buf,
1924 int *dir)
1925{
1926 char_u *ptr;
1927 int i;
1928 FILE *fp;
1929 int add_r;
glepnirf31cfa22025-03-06 21:59:13 +01001930 char_u *leader = NULL;
1931 int leader_len = 0;
glepnir58760162025-03-13 21:39:51 +01001932 int in_fuzzy_collect = cfc_has_mode();
glepnirf31cfa22025-03-06 21:59:13 +01001933 int score = 0;
1934 int len = 0;
1935 char_u *line_end = NULL;
1936
1937 if (in_fuzzy_collect)
1938 {
1939 leader = ins_compl_leader();
Yegappan Lakshmanan7b6add02025-04-01 20:38:37 +02001940 leader_len = (int)ins_compl_leader_len();
glepnirf31cfa22025-03-06 21:59:13 +01001941 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001942
1943 for (i = 0; i < count && !got_int && !compl_interrupted; i++)
1944 {
1945 fp = mch_fopen((char *)files[i], "r"); // open dictionary file
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01001946 if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001947 {
Bram Moolenaarcc233582020-12-12 13:32:07 +01001948 msg_hist_off = TRUE; // reset in msg_trunc_attr()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001949 vim_snprintf((char *)IObuff, IOSIZE,
1950 _("Scanning dictionary: %s"), (char *)files[i]);
1951 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
1952 }
1953
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001954 if (fp == NULL)
1955 continue;
1956
1957 // Read dictionary file line by line.
1958 // Check each line for a match.
1959 while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001960 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001961 ptr = buf;
glepnirf31cfa22025-03-06 21:59:13 +01001962 if (regmatch != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001963 {
glepnirf31cfa22025-03-06 21:59:13 +01001964 while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf)))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001965 {
glepnirf31cfa22025-03-06 21:59:13 +01001966 ptr = regmatch->startp[0];
1967 ptr = ctrl_x_mode_line_or_eval() ? find_line_end(ptr)
1968 : find_word_end(ptr);
1969 add_r = ins_compl_add_infercase(regmatch->startp[0],
1970 (int)(ptr - regmatch->startp[0]),
1971 p_ic, files[i], *dir, FALSE, 0);
1972 if (thesaurus)
1973 {
1974 // For a thesaurus, add all the words in the line
1975 ptr = buf;
1976 add_r = thesaurus_add_words_in_line(files[i], &ptr, *dir,
1977 regmatch->startp[0]);
1978 }
1979 if (add_r == OK)
1980 // if dir was BACKWARD then honor it just once
1981 *dir = FORWARD;
1982 else if (add_r == FAIL)
1983 break;
1984 // avoid expensive call to vim_regexec() when at end
1985 // of line
1986 if (*ptr == '\n' || got_int)
1987 break;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001988 }
glepnirf31cfa22025-03-06 21:59:13 +01001989 }
1990 else if (in_fuzzy_collect && leader_len > 0)
1991 {
1992 line_end = find_line_end(ptr);
1993 while (ptr < line_end)
1994 {
1995 if (fuzzy_match_str_in_line(&ptr, leader, &len, NULL, &score))
1996 {
1997 char_u *end_ptr = ctrl_x_mode_line_or_eval()
1998 ? find_line_end(ptr) : find_word_end(ptr);
1999 add_r = ins_compl_add_infercase(ptr, (int)(end_ptr - ptr),
2000 p_ic, files[i], *dir, FALSE, score);
2001 if (add_r == FAIL)
2002 break;
2003 ptr = end_ptr; // start from next word
2004 if (compl_get_longest && ctrl_x_mode_normal()
2005 && compl_first_match->cp_next
2006 && score == compl_first_match->cp_next->cp_score)
2007 compl_num_bests++;
2008 }
glepnirf31cfa22025-03-06 21:59:13 +01002009 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002010 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002011 line_breakcheck();
2012 ins_compl_check_keys(50, FALSE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002013 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002014 fclose(fp);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002015 }
2016}
2017
2018/*
2019 * Find the start of the next word.
2020 * Returns a pointer to the first char of the word. Also stops at a NUL.
2021 */
2022 char_u *
2023find_word_start(char_u *ptr)
2024{
2025 if (has_mbyte)
glepnir19e1dd62025-05-08 22:50:38 +02002026 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002027 while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1)
2028 ptr += (*mb_ptr2len)(ptr);
glepnir19e1dd62025-05-08 22:50:38 +02002029 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002030 else
glepnir19e1dd62025-05-08 22:50:38 +02002031 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002032 while (*ptr != NUL && *ptr != '\n' && !vim_iswordc(*ptr))
2033 ++ptr;
glepnir19e1dd62025-05-08 22:50:38 +02002034 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002035 return ptr;
2036}
2037
2038/*
2039 * Find the end of the word. Assumes it starts inside a word.
2040 * Returns a pointer to just after the word.
2041 */
2042 char_u *
2043find_word_end(char_u *ptr)
2044{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002045 if (has_mbyte)
2046 {
glepnir19e1dd62025-05-08 22:50:38 +02002047 int start_class = mb_get_class(ptr);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002048 if (start_class > 1)
2049 while (*ptr != NUL)
2050 {
2051 ptr += (*mb_ptr2len)(ptr);
2052 if (mb_get_class(ptr) != start_class)
2053 break;
2054 }
2055 }
2056 else
2057 while (vim_iswordc(*ptr))
2058 ++ptr;
2059 return ptr;
2060}
2061
2062/*
2063 * Find the end of the line, omitting CR and NL at the end.
2064 * Returns a pointer to just after the line.
2065 */
glepnirdd42b052025-03-08 16:52:55 +01002066 char_u *
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002067find_line_end(char_u *ptr)
2068{
glepnir19e1dd62025-05-08 22:50:38 +02002069 char_u *s = ptr + STRLEN(ptr);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002070 while (s > ptr && (s[-1] == CAR || s[-1] == NL))
2071 --s;
2072 return s;
2073}
2074
2075/*
Girish Palyacbe53192025-04-14 22:13:15 +02002076 * Free a completion item in the list
2077 */
2078 static void
2079ins_compl_item_free(compl_T *match)
2080{
Girish Palyacbe53192025-04-14 22:13:15 +02002081 VIM_CLEAR_STRING(match->cp_str);
2082 // several entries may use the same fname, free it just once.
2083 if (match->cp_flags & CP_FREE_FNAME)
2084 vim_free(match->cp_fname);
glepnir19e1dd62025-05-08 22:50:38 +02002085 for (int i = 0; i < CPT_COUNT; ++i)
Girish Palyacbe53192025-04-14 22:13:15 +02002086 vim_free(match->cp_text[i]);
2087#ifdef FEAT_EVAL
2088 clear_tv(&match->cp_user_data);
2089#endif
2090 vim_free(match);
2091}
2092
2093/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002094 * Free the list of completions
2095 */
2096 static void
2097ins_compl_free(void)
2098{
2099 compl_T *match;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002100
John Marriott5e6ea922024-11-23 14:01:57 +01002101 VIM_CLEAR_STRING(compl_pattern);
2102 VIM_CLEAR_STRING(compl_leader);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002103
2104 if (compl_first_match == NULL)
2105 return;
2106
2107 ins_compl_del_pum();
2108 pum_clear();
2109
2110 compl_curr_match = compl_first_match;
2111 do
2112 {
2113 match = compl_curr_match;
2114 compl_curr_match = compl_curr_match->cp_next;
Girish Palyacbe53192025-04-14 22:13:15 +02002115 ins_compl_item_free(match);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002116 } while (compl_curr_match != NULL && !is_first_match(compl_curr_match));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002117 compl_first_match = compl_curr_match = NULL;
2118 compl_shown_match = NULL;
2119 compl_old_match = NULL;
2120}
2121
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002122/*
2123 * Reset/clear the completion state.
2124 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002125 void
2126ins_compl_clear(void)
2127{
2128 compl_cont_status = 0;
2129 compl_started = FALSE;
glepnirf31cfa22025-03-06 21:59:13 +01002130 compl_cfc_longest_ins = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002131 compl_matches = 0;
glepnir07f0dbe2025-02-18 20:27:30 +01002132 compl_selected_item = -1;
glepnir6a38aff2024-12-16 21:56:16 +01002133 compl_ins_end_col = 0;
glepnircf7f0122025-04-15 19:02:00 +02002134 compl_curr_win = NULL;
2135 compl_curr_buf = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002136 VIM_CLEAR_STRING(compl_pattern);
2137 VIM_CLEAR_STRING(compl_leader);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002138 edit_submode_extra = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002139 VIM_CLEAR_STRING(compl_orig_text);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002140 compl_enter_selects = FALSE;
Girish Palya0ac1eb32025-04-16 20:18:33 +02002141 cpt_sources_clear();
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002142#ifdef FEAT_EVAL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002143 // clear v:completed_item
2144 set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002145#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002146}
2147
2148/*
2149 * Return TRUE when Insert completion is active.
2150 */
2151 int
2152ins_compl_active(void)
2153{
2154 return compl_started;
2155}
2156
2157/*
glepnir8d0bb6d2024-12-24 09:44:35 +01002158 * Return True when wp is the actual completion window
2159 */
2160 int
glepnircf7f0122025-04-15 19:02:00 +02002161ins_compl_win_active(win_T *wp)
glepnir8d0bb6d2024-12-24 09:44:35 +01002162{
glepnircf7f0122025-04-15 19:02:00 +02002163 return ins_compl_active() && wp == compl_curr_win
2164 && wp->w_buffer == compl_curr_buf;
glepnir8d0bb6d2024-12-24 09:44:35 +01002165}
2166
2167/*
Girish Palyacbe53192025-04-14 22:13:15 +02002168 * Selected one of the matches. When FALSE, the match was either edited or
2169 * using the longest common string.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002170 */
2171 int
2172ins_compl_used_match(void)
2173{
2174 return compl_used_match;
2175}
2176
2177/*
2178 * Initialize get longest common string.
2179 */
2180 void
2181ins_compl_init_get_longest(void)
2182{
2183 compl_get_longest = FALSE;
2184}
2185
2186/*
2187 * Returns TRUE when insert completion is interrupted.
2188 */
2189 int
2190ins_compl_interrupted(void)
2191{
2192 return compl_interrupted;
2193}
2194
2195/*
2196 * Returns TRUE if the <Enter> key selects a match in the completion popup
2197 * menu.
2198 */
2199 int
2200ins_compl_enter_selects(void)
2201{
2202 return compl_enter_selects;
2203}
2204
2205/*
2206 * Return the column where the text starts that is being completed
2207 */
2208 colnr_T
2209ins_compl_col(void)
2210{
2211 return compl_col;
2212}
2213
2214/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002215 * Return the length in bytes of the text being completed
2216 */
2217 int
2218ins_compl_len(void)
2219{
2220 return compl_length;
2221}
2222
2223/*
glepnir94a045e2025-03-01 16:12:23 +01002224 * Return TRUE when the 'completeopt' "preinsert" flag is in effect,
2225 * otherwise return FALSE.
glepniredd4ac32025-01-29 18:53:51 +01002226 */
2227 static int
2228ins_compl_has_preinsert(void)
2229{
glepnira2c55592025-02-28 17:43:42 +01002230 int cur_cot_flags = get_cot_flags();
glepnir94a045e2025-03-01 16:12:23 +01002231 return (cur_cot_flags & (COT_PREINSERT | COT_FUZZY | COT_MENUONE))
2232 == (COT_PREINSERT | COT_MENUONE);
glepniredd4ac32025-01-29 18:53:51 +01002233}
2234
2235/*
2236 * Returns TRUE if the pre-insert effect is valid and the cursor is within
2237 * the `compl_ins_end_col` range.
2238 */
2239 int
2240ins_compl_preinsert_effect(void)
2241{
2242 if (!ins_compl_has_preinsert())
2243 return FALSE;
2244
2245 return curwin->w_cursor.col < compl_ins_end_col;
2246}
2247
2248/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002249 * Delete one character before the cursor and show the subset of the matches
2250 * that match the word that is now before the cursor.
2251 * Returns the character to be used, NUL if the work is done and another char
2252 * to be got from the user.
2253 */
2254 int
2255ins_compl_bs(void)
2256{
2257 char_u *line;
2258 char_u *p;
2259
glepniredd4ac32025-01-29 18:53:51 +01002260 if (ins_compl_preinsert_effect())
2261 ins_compl_delete();
2262
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002263 line = ml_get_curline();
2264 p = line + curwin->w_cursor.col;
2265 MB_PTR_BACK(line, p);
2266
2267 // Stop completion when the whole word was deleted. For Omni completion
2268 // allow the word to be deleted, we won't match everything.
2269 // Respect the 'backspace' option.
2270 if ((int)(p - line) - (int)compl_col < 0
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002271 || ((int)(p - line) - (int)compl_col == 0 && !ctrl_x_mode_omni())
2272 || ctrl_x_mode_eval()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002273 || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col
2274 - compl_length < 0))
2275 return K_BS;
2276
2277 // Deleted more than what was used to find matches or didn't finish
2278 // finding all matches: need to look for matches all over again.
2279 if (curwin->w_cursor.col <= compl_col + compl_length
2280 || ins_compl_need_restart())
2281 ins_compl_restart();
2282
John Marriott5e6ea922024-11-23 14:01:57 +01002283 VIM_CLEAR_STRING(compl_leader);
2284 compl_leader.length = (size_t)((p - line) - compl_col);
2285 compl_leader.string = vim_strnsave(line + compl_col, compl_leader.length);
2286 if (compl_leader.string == NULL)
2287 {
2288 compl_leader.length = 0;
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00002289 return K_BS;
John Marriott5e6ea922024-11-23 14:01:57 +01002290 }
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00002291
2292 ins_compl_new_leader();
2293 if (compl_shown_match != NULL)
2294 // Make sure current match is not a hidden item.
2295 compl_curr_match = compl_shown_match;
2296 return NUL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002297}
2298
2299/*
2300 * Return TRUE when we need to find matches again, ins_compl_restart() is to
2301 * be called.
2302 */
2303 static int
2304ins_compl_need_restart(void)
2305{
2306 // Return TRUE if we didn't complete finding matches or when the
2307 // 'completefunc' returned "always" in the "refresh" dictionary item.
2308 return compl_was_interrupted
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002309 || ((ctrl_x_mode_function() || ctrl_x_mode_omni())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002310 && compl_opt_refresh_always);
2311}
2312
2313/*
2314 * Called after changing "compl_leader".
2315 * Show the popup menu with a different set of matches.
2316 * May also search for matches again if the previous search was interrupted.
2317 */
2318 static void
2319ins_compl_new_leader(void)
2320{
2321 ins_compl_del_pum();
2322 ins_compl_delete();
glepnir6a38aff2024-12-16 21:56:16 +01002323 ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002324 compl_used_match = FALSE;
2325
2326 if (compl_started)
Girish Palyacbe53192025-04-14 22:13:15 +02002327 {
John Marriott5e6ea922024-11-23 14:01:57 +01002328 ins_compl_set_original_text(compl_leader.string, compl_leader.length);
Girish Palyacbe53192025-04-14 22:13:15 +02002329 if (is_cpt_func_refresh_always())
2330 cpt_compl_refresh();
2331 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002332 else
2333 {
2334#ifdef FEAT_SPELL
2335 spell_bad_len = 0; // need to redetect bad word
2336#endif
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002337 // Matches were cleared, need to search for them now. Before drawing
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002338 // the popup menu display the changed text before the cursor. Set
2339 // "compl_restarting" to avoid that the first match is inserted.
2340 pum_call_update_screen();
2341#ifdef FEAT_GUI
2342 if (gui.in_use)
2343 {
2344 // Show the cursor after the match, not after the redrawn text.
2345 setcursor();
2346 out_flush_cursor(FALSE, FALSE);
2347 }
2348#endif
2349 compl_restarting = TRUE;
2350 if (ins_complete(Ctrl_N, TRUE) == FAIL)
2351 compl_cont_status = 0;
2352 compl_restarting = FALSE;
2353 }
2354
glepnir44180412025-02-20 22:09:48 +01002355 compl_enter_selects = !compl_used_match && compl_selected_item != -1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002356
2357 // Show the popup menu with a different set of matches.
2358 ins_compl_show_pum();
2359
2360 // Don't let Enter select the original text when there is no popup menu.
2361 if (compl_match_array == NULL)
2362 compl_enter_selects = FALSE;
glepniredd4ac32025-01-29 18:53:51 +01002363 else if (ins_compl_has_preinsert() && compl_leader.length > 0)
2364 ins_compl_insert(FALSE, TRUE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002365}
2366
2367/*
2368 * Return the length of the completion, from the completion start column to
2369 * the cursor column. Making sure it never goes below zero.
2370 */
2371 static int
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002372get_compl_len(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002373{
2374 int off = (int)curwin->w_cursor.col - (int)compl_col;
glepnir40891ba2025-02-10 22:18:00 +01002375 return MAX(0, off);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002376}
2377
2378/*
2379 * Append one character to the match leader. May reduce the number of
2380 * matches.
2381 */
2382 void
2383ins_compl_addleader(int c)
2384{
glepnir19e1dd62025-05-08 22:50:38 +02002385 int cc;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002386
glepniredd4ac32025-01-29 18:53:51 +01002387 if (ins_compl_preinsert_effect())
2388 ins_compl_delete();
2389
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002390 if (stop_arrow() == FAIL)
2391 return;
2392 if (has_mbyte && (cc = (*mb_char2len)(c)) > 1)
2393 {
2394 char_u buf[MB_MAXBYTES + 1];
2395
2396 (*mb_char2bytes)(c, buf);
2397 buf[cc] = NUL;
2398 ins_char_bytes(buf, cc);
2399 if (compl_opt_refresh_always)
2400 AppendToRedobuff(buf);
2401 }
2402 else
2403 {
2404 ins_char(c);
2405 if (compl_opt_refresh_always)
2406 AppendCharToRedobuff(c);
2407 }
2408
2409 // If we didn't complete finding matches we must search again.
2410 if (ins_compl_need_restart())
2411 ins_compl_restart();
2412
2413 // When 'always' is set, don't reset compl_leader. While completing,
2414 // cursor doesn't point original position, changing compl_leader would
2415 // break redo.
2416 if (!compl_opt_refresh_always)
2417 {
John Marriott5e6ea922024-11-23 14:01:57 +01002418 VIM_CLEAR_STRING(compl_leader);
2419 compl_leader.length = (size_t)(curwin->w_cursor.col - compl_col);
2420 compl_leader.string = vim_strnsave(ml_get_curline() + compl_col,
2421 compl_leader.length);
2422 if (compl_leader.string == NULL)
2423 {
2424 compl_leader.length = 0;
2425 return;
2426 }
2427
2428 ins_compl_new_leader();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002429 }
2430}
2431
2432/*
2433 * Setup for finding completions again without leaving CTRL-X mode. Used when
2434 * BS or a key was typed while still searching for matches.
2435 */
2436 static void
2437ins_compl_restart(void)
2438{
2439 ins_compl_free();
2440 compl_started = FALSE;
2441 compl_matches = 0;
2442 compl_cont_status = 0;
2443 compl_cont_mode = 0;
Girish Palya0ac1eb32025-04-16 20:18:33 +02002444 cpt_sources_clear();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002445}
2446
2447/*
2448 * Set the first match, the original text.
2449 */
2450 static void
John Marriott5e6ea922024-11-23 14:01:57 +01002451ins_compl_set_original_text(char_u *str, size_t len)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002452{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002453 // Replace the original text entry.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002454 // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
2455 // be at the last item for backward completion
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002456 if (match_at_original_text(compl_first_match)) // safety check
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002457 {
John Marriott5e6ea922024-11-23 14:01:57 +01002458 char_u *p = vim_strnsave(str, len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002459 if (p != NULL)
2460 {
John Marriott5e6ea922024-11-23 14:01:57 +01002461 VIM_CLEAR_STRING(compl_first_match->cp_str);
2462 compl_first_match->cp_str.string = p;
2463 compl_first_match->cp_str.length = len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002464 }
2465 }
2466 else if (compl_first_match->cp_prev != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002467 && match_at_original_text(compl_first_match->cp_prev))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002468 {
John Marriott5e6ea922024-11-23 14:01:57 +01002469 char_u *p = vim_strnsave(str, len);
2470 if (p != NULL)
2471 {
2472 VIM_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
2473 compl_first_match->cp_prev->cp_str.string = p;
2474 compl_first_match->cp_prev->cp_str.length = len;
2475 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002476 }
2477}
2478
2479/*
2480 * Append one character to the match leader. May reduce the number of
2481 * matches.
2482 */
2483 void
2484ins_compl_addfrommatch(void)
2485{
2486 char_u *p;
2487 int len = (int)curwin->w_cursor.col - (int)compl_col;
2488 int c;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002489
John Marriott5e6ea922024-11-23 14:01:57 +01002490 p = compl_shown_match->cp_str.string;
2491 if ((int)compl_shown_match->cp_str.length <= len) // the match is too short
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002492 {
John Marriott5e6ea922024-11-23 14:01:57 +01002493 size_t plen;
2494 compl_T *cp;
2495
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002496 // When still at the original match use the first entry that matches
2497 // the leader.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002498 if (!match_at_original_text(compl_shown_match))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002499 return;
2500
2501 p = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002502 plen = 0;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002503 for (cp = compl_shown_match->cp_next; cp != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002504 && !is_first_match(cp); cp = cp->cp_next)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002505 {
John Marriott5e6ea922024-11-23 14:01:57 +01002506 if (compl_leader.string == NULL
2507 || ins_compl_equal(cp, compl_leader.string,
2508 (int)compl_leader.length))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002509 {
John Marriott5e6ea922024-11-23 14:01:57 +01002510 p = cp->cp_str.string;
2511 plen = cp->cp_str.length;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002512 break;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002513 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002514 }
John Marriott5e6ea922024-11-23 14:01:57 +01002515 if (p == NULL || (int)plen <= len)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002516 return;
2517 }
2518 p += len;
2519 c = PTR2CHAR(p);
2520 ins_compl_addleader(c);
2521}
2522
2523/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002524 * Set the CTRL-X completion mode based on the key "c" typed after a CTRL-X.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002525 * Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre,
2526 * compl_cont_mode and compl_cont_status.
2527 * Returns TRUE when the character is not to be inserted.
2528 */
2529 static int
2530set_ctrl_x_mode(int c)
2531{
2532 int retval = FALSE;
2533
2534 switch (c)
2535 {
2536 case Ctrl_E:
2537 case Ctrl_Y:
2538 // scroll the window one line up or down
2539 ctrl_x_mode = CTRL_X_SCROLL;
2540 if (!(State & REPLACE_FLAG))
2541 edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
2542 else
2543 edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
2544 edit_submode_pre = NULL;
2545 showmode();
2546 break;
2547 case Ctrl_L:
2548 // complete whole line
2549 ctrl_x_mode = CTRL_X_WHOLE_LINE;
2550 break;
2551 case Ctrl_F:
2552 // complete filenames
2553 ctrl_x_mode = CTRL_X_FILES;
2554 break;
2555 case Ctrl_K:
zeertzjq5fb3aab2022-08-24 16:48:23 +01002556 // complete words from a dictionary
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002557 ctrl_x_mode = CTRL_X_DICTIONARY;
2558 break;
2559 case Ctrl_R:
2560 // Register insertion without exiting CTRL-X mode
2561 // Simply allow ^R to happen without affecting ^X mode
2562 break;
2563 case Ctrl_T:
2564 // complete words from a thesaurus
2565 ctrl_x_mode = CTRL_X_THESAURUS;
2566 break;
2567#ifdef FEAT_COMPL_FUNC
2568 case Ctrl_U:
2569 // user defined completion
2570 ctrl_x_mode = CTRL_X_FUNCTION;
2571 break;
2572 case Ctrl_O:
2573 // omni completion
2574 ctrl_x_mode = CTRL_X_OMNI;
2575 break;
2576#endif
2577 case 's':
2578 case Ctrl_S:
2579 // complete spelling suggestions
2580 ctrl_x_mode = CTRL_X_SPELL;
2581#ifdef FEAT_SPELL
2582 ++emsg_off; // Avoid getting the E756 error twice.
2583 spell_back_to_badword();
2584 --emsg_off;
2585#endif
2586 break;
2587 case Ctrl_RSB:
2588 // complete tag names
2589 ctrl_x_mode = CTRL_X_TAGS;
2590 break;
2591#ifdef FEAT_FIND_ID
2592 case Ctrl_I:
2593 case K_S_TAB:
2594 // complete keywords from included files
2595 ctrl_x_mode = CTRL_X_PATH_PATTERNS;
2596 break;
2597 case Ctrl_D:
2598 // complete definitions from included files
2599 ctrl_x_mode = CTRL_X_PATH_DEFINES;
2600 break;
2601#endif
2602 case Ctrl_V:
2603 case Ctrl_Q:
2604 // complete vim commands
2605 ctrl_x_mode = CTRL_X_CMDLINE;
2606 break;
2607 case Ctrl_Z:
2608 // stop completion
2609 ctrl_x_mode = CTRL_X_NORMAL;
2610 edit_submode = NULL;
2611 showmode();
2612 retval = TRUE;
2613 break;
2614 case Ctrl_P:
2615 case Ctrl_N:
2616 // ^X^P means LOCAL expansion if nothing interrupted (eg we
2617 // just started ^X mode, or there were enough ^X's to cancel
2618 // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
2619 // do normal expansion when interrupting a different mode (say
2620 // ^X^F^X^P or ^P^X^X^P, see below)
2621 // nothing changes if interrupting mode 0, (eg, the flag
2622 // doesn't change when going to ADDING mode -- Acevedo
2623 if (!(compl_cont_status & CONT_INTRPT))
2624 compl_cont_status |= CONT_LOCAL;
2625 else if (compl_cont_mode != 0)
2626 compl_cont_status &= ~CONT_LOCAL;
2627 // FALLTHROUGH
2628 default:
2629 // If we have typed at least 2 ^X's... for modes != 0, we set
2630 // compl_cont_status = 0 (eg, as if we had just started ^X
2631 // mode).
2632 // For mode 0, we set "compl_cont_mode" to an impossible
2633 // value, in both cases ^X^X can be used to restart the same
2634 // mode (avoiding ADDING mode).
2635 // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
2636 // 'complete' and local ^P expansions respectively.
2637 // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
2638 // mode -- Acevedo
2639 if (c == Ctrl_X)
2640 {
2641 if (compl_cont_mode != 0)
2642 compl_cont_status = 0;
2643 else
2644 compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
2645 }
2646 ctrl_x_mode = CTRL_X_NORMAL;
2647 edit_submode = NULL;
2648 showmode();
2649 break;
2650 }
2651
2652 return retval;
2653}
2654
2655/*
glepnir1c5a1202024-12-04 20:27:34 +01002656 * Trigger CompleteDone event and adds relevant information to v:event
2657 */
2658 static void
2659trigger_complete_done_event(int mode UNUSED, char_u *word UNUSED)
2660{
2661#if defined(FEAT_EVAL)
2662 save_v_event_T save_v_event;
2663 dict_T *v_event = get_v_event(&save_v_event);
2664 char_u *mode_str = NULL;
2665
2666 mode = mode & ~CTRL_X_WANT_IDENT;
2667 if (ctrl_x_mode_names[mode])
2668 mode_str = (char_u *)ctrl_x_mode_names[mode];
2669
2670 (void)dict_add_string(v_event, "complete_word",
2671 word == NULL ? (char_u *)"" : word);
2672 (void)dict_add_string(v_event, "complete_type",
2673 mode_str != NULL ? mode_str : (char_u *)"");
2674
2675 dict_set_items_ro(v_event);
2676#endif
2677 ins_apply_autocmds(EVENT_COMPLETEDONE);
2678
2679#if defined(FEAT_EVAL)
2680 restore_v_event(v_event, &save_v_event);
2681#endif
2682}
2683
2684/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002685 * Stop insert completion mode
2686 */
2687 static int
2688ins_compl_stop(int c, int prev_mode, int retval)
2689{
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002690 int want_cindent;
glepnir1c5a1202024-12-04 20:27:34 +01002691 char_u *word = NULL;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002692
glepnir84a75032025-03-15 09:59:22 +01002693 // Remove pre-inserted text when present.
zeertzjq13436812025-04-23 20:46:35 +02002694 if (ins_compl_preinsert_effect() && ins_compl_win_active(curwin))
glepnir84a75032025-03-15 09:59:22 +01002695 ins_compl_delete();
2696
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002697 // Get here when we have finished typing a sequence of ^N and
2698 // ^P or other completion characters in CTRL-X mode. Free up
2699 // memory that was used, and make sure we can redo the insert.
John Marriott5e6ea922024-11-23 14:01:57 +01002700 if (compl_curr_match != NULL || compl_leader.string != NULL || c == Ctrl_E)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002701 {
glepnir40891ba2025-02-10 22:18:00 +01002702 char_u *ptr = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002703
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002704 // If any of the original typed text has been changed, eg when
2705 // ignorecase is set, we must add back-spaces to the redo
2706 // buffer. We add as few as necessary to delete just the part
2707 // of the original text that has changed.
2708 // When using the longest match, edited the match or used
2709 // CTRL-E then don't use the current match.
2710 if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E)
John Marriott5e6ea922024-11-23 14:01:57 +01002711 ptr = compl_curr_match->cp_str.string;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002712 ins_compl_fixRedoBufForLeader(ptr);
2713 }
2714
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002715 want_cindent = (get_can_cindent() && cindent_on());
Bram Moolenaar8e145b82022-05-21 20:17:31 +01002716
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002717 // When completing whole lines: fix indent for 'cindent'.
2718 // Otherwise, break line if it's too long.
2719 if (compl_cont_mode == CTRL_X_WHOLE_LINE)
2720 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002721 // re-indent the current line
2722 if (want_cindent)
2723 {
2724 do_c_expr_indent();
2725 want_cindent = FALSE; // don't do it again
2726 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002727 }
2728 else
2729 {
2730 int prev_col = curwin->w_cursor.col;
2731
2732 // put the cursor on the last char, for 'tw' formatting
2733 if (prev_col > 0)
2734 dec_cursor();
2735 // only format when something was inserted
2736 if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E)
2737 insertchar(NUL, 0, -1);
2738 if (prev_col > 0
2739 && ml_get_curline()[curwin->w_cursor.col] != NUL)
2740 inc_cursor();
2741 }
2742
2743 // If the popup menu is displayed pressing CTRL-Y means accepting
2744 // the selection without inserting anything. When
2745 // compl_enter_selects is set the Enter key does the same.
2746 if ((c == Ctrl_Y || (compl_enter_selects
2747 && (c == CAR || c == K_KENTER || c == NL)))
2748 && pum_visible())
glepnir1c5a1202024-12-04 20:27:34 +01002749 {
2750 word = vim_strsave(compl_shown_match->cp_str.string);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002751 retval = TRUE;
glepnir1c5a1202024-12-04 20:27:34 +01002752 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002753
2754 // CTRL-E means completion is Ended, go back to the typed text.
2755 // but only do this, if the Popup is still visible
2756 if (c == Ctrl_E)
2757 {
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002758 char_u *p = NULL;
John Marriott5e6ea922024-11-23 14:01:57 +01002759 size_t plen = 0;
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002760
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002761 ins_compl_delete();
John Marriott5e6ea922024-11-23 14:01:57 +01002762 if (compl_leader.string != NULL)
2763 {
2764 p = compl_leader.string;
2765 plen = compl_leader.length;
2766 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002767 else if (compl_first_match != NULL)
John Marriott5e6ea922024-11-23 14:01:57 +01002768 {
2769 p = compl_orig_text.string;
2770 plen = compl_orig_text.length;
2771 }
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002772 if (p != NULL)
2773 {
2774 int compl_len = get_compl_len();
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002775
John Marriott5e6ea922024-11-23 14:01:57 +01002776 if ((int)plen > compl_len)
zeertzjqf25d8f92024-12-18 21:12:25 +01002777 ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len);
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002778 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002779 retval = TRUE;
2780 }
2781
2782 auto_format(FALSE, TRUE);
2783
2784 // Trigger the CompleteDonePre event to give scripts a chance to
2785 // act upon the completion before clearing the info, and restore
2786 // ctrl_x_mode, so that complete_info() can be used.
2787 ctrl_x_mode = prev_mode;
2788 ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
2789
2790 ins_compl_free();
2791 compl_started = FALSE;
2792 compl_matches = 0;
2793 if (!shortmess(SHM_COMPLETIONMENU))
2794 msg_clr_cmdline(); // necessary for "noshowmode"
2795 ctrl_x_mode = CTRL_X_NORMAL;
2796 compl_enter_selects = FALSE;
2797 if (edit_submode != NULL)
2798 {
2799 edit_submode = NULL;
2800 showmode();
2801 }
2802
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002803 if (c == Ctrl_C && cmdwin_type != 0)
2804 // Avoid the popup menu remains displayed when leaving the
2805 // command line window.
2806 update_screen(0);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002807 // Indent now if a key was typed that is in 'cinkeys'.
2808 if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0)))
2809 do_c_expr_indent();
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002810 // Trigger the CompleteDone event to give scripts a chance to act
2811 // upon the end of completion.
glepnir1c5a1202024-12-04 20:27:34 +01002812 trigger_complete_done_event(prev_mode, word);
2813 vim_free(word);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002814
2815 return retval;
2816}
2817
2818/*
glepnircf7f0122025-04-15 19:02:00 +02002819 * Cancel completion.
2820 */
2821 int
2822ins_compl_cancel(void)
2823{
2824 return ins_compl_stop(' ', ctrl_x_mode, TRUE);
2825}
2826
2827/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002828 * Prepare for Insert mode completion, or stop it.
2829 * Called just after typing a character in Insert mode.
2830 * Returns TRUE when the character is not to be inserted;
2831 */
2832 int
2833ins_compl_prep(int c)
2834{
glepnir19e1dd62025-05-08 22:50:38 +02002835 int retval = FALSE;
2836 int prev_mode = ctrl_x_mode;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002837
2838 // Forget any previous 'special' messages if this is actually
2839 // a ^X mode key - bar ^R, in which case we wait to see what it gives us.
2840 if (c != Ctrl_R && vim_is_ctrl_x_key(c))
2841 edit_submode_extra = NULL;
2842
zeertzjq440d4cb2023-03-02 17:51:32 +00002843 // Ignore end of Select mode mapping and mouse scroll/movement.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002844 if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
zeertzjq440d4cb2023-03-02 17:51:32 +00002845 || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_MOUSEMOVE
Bram Moolenaare32c3c42022-01-15 18:26:04 +00002846 || c == K_COMMAND || c == K_SCRIPT_COMMAND)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002847 return retval;
2848
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002849#ifdef FEAT_PROP_POPUP
Bram Moolenaarf0bc15c2019-08-18 19:23:45 +02002850 // Ignore mouse events in a popup window
2851 if (is_mouse_key(c))
2852 {
2853 // Ignore drag and release events, the position does not need to be in
2854 // the popup and it may have just closed.
2855 if (c == K_LEFTRELEASE
2856 || c == K_LEFTRELEASE_NM
2857 || c == K_MIDDLERELEASE
2858 || c == K_RIGHTRELEASE
2859 || c == K_X1RELEASE
2860 || c == K_X2RELEASE
2861 || c == K_LEFTDRAG
2862 || c == K_MIDDLEDRAG
2863 || c == K_RIGHTDRAG
2864 || c == K_X1DRAG
2865 || c == K_X2DRAG)
2866 return retval;
2867 if (popup_visible)
2868 {
2869 int row = mouse_row;
2870 int col = mouse_col;
2871 win_T *wp = mouse_find_win(&row, &col, FIND_POPUP);
2872
2873 if (wp != NULL && WIN_IS_POPUP(wp))
2874 return retval;
2875 }
2876 }
2877#endif
2878
zeertzjqdca29d92021-08-31 19:12:51 +02002879 if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X)
2880 {
2881 if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c)
2882 || !vim_is_ctrl_x_key(c))
2883 {
2884 // Not starting another completion mode.
2885 ctrl_x_mode = CTRL_X_CMDLINE;
2886
2887 // CTRL-X CTRL-Z should stop completion without inserting anything
2888 if (c == Ctrl_Z)
2889 retval = TRUE;
2890 }
2891 else
2892 {
2893 ctrl_x_mode = CTRL_X_CMDLINE;
2894
2895 // Other CTRL-X keys first stop completion, then start another
2896 // completion mode.
2897 ins_compl_prep(' ');
2898 ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
2899 }
2900 }
2901
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002902 // Set "compl_get_longest" when finding the first matches.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002903 if (ctrl_x_mode_not_defined_yet()
2904 || (ctrl_x_mode_normal() && !compl_started))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002905 {
zeertzjq529b9ad2024-06-05 20:27:06 +02002906 compl_get_longest = (get_cot_flags() & COT_LONGEST) != 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002907 compl_used_match = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002908 }
2909
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002910 if (ctrl_x_mode_not_defined_yet())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002911 // We have just typed CTRL-X and aren't quite sure which CTRL-X mode
2912 // it will be yet. Now we decide.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002913 retval = set_ctrl_x_mode(c);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002914 else if (ctrl_x_mode_not_default())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002915 {
2916 // We're already in CTRL-X mode, do we stay in it?
2917 if (!vim_is_ctrl_x_key(c))
2918 {
glepnir40891ba2025-02-10 22:18:00 +01002919 ctrl_x_mode = ctrl_x_mode_scroll() ? CTRL_X_NORMAL : CTRL_X_FINISHED;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002920 edit_submode = NULL;
2921 }
2922 showmode();
2923 }
2924
2925 if (compl_started || ctrl_x_mode == CTRL_X_FINISHED)
2926 {
2927 // Show error message from attempted keyword completion (probably
2928 // 'Pattern not found') until another key is hit, then go back to
2929 // showing what mode we are in.
2930 showmode();
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002931 if ((ctrl_x_mode_normal() && c != Ctrl_N && c != Ctrl_P
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002932 && c != Ctrl_R && !ins_compl_pum_key(c))
2933 || ctrl_x_mode == CTRL_X_FINISHED)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002934 retval = ins_compl_stop(c, prev_mode, retval);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002935 }
2936 else if (ctrl_x_mode == CTRL_X_LOCAL_MSG)
2937 // Trigger the CompleteDone event to give scripts a chance to act
2938 // upon the (possibly failed) completion.
glepnir1c5a1202024-12-04 20:27:34 +01002939 trigger_complete_done_event(ctrl_x_mode, NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002940
LemonBoy2bf52dd2022-04-09 18:17:34 +01002941 may_trigger_modechanged();
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01002942
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002943 // reset continue_* if we left expansion-mode, if we stay they'll be
2944 // (re)set properly in ins_complete()
2945 if (!vim_is_ctrl_x_key(c))
2946 {
2947 compl_cont_status = 0;
2948 compl_cont_mode = 0;
2949 }
2950
2951 return retval;
2952}
2953
2954/*
2955 * Fix the redo buffer for the completion leader replacing some of the typed
2956 * text. This inserts backspaces and appends the changed text.
2957 * "ptr" is the known leader text or NUL.
2958 */
2959 static void
2960ins_compl_fixRedoBufForLeader(char_u *ptr_arg)
2961{
John Marriott5e6ea922024-11-23 14:01:57 +01002962 int len = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002963 char_u *p;
2964 char_u *ptr = ptr_arg;
2965
2966 if (ptr == NULL)
2967 {
John Marriott5e6ea922024-11-23 14:01:57 +01002968 if (compl_leader.string != NULL)
2969 ptr = compl_leader.string;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002970 else
2971 return; // nothing to do
2972 }
John Marriott5e6ea922024-11-23 14:01:57 +01002973 if (compl_orig_text.string != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002974 {
John Marriott5e6ea922024-11-23 14:01:57 +01002975 p = compl_orig_text.string;
glepnir40891ba2025-02-10 22:18:00 +01002976 // Find length of common prefix between original text and new completion
2977 while (p[len] != NUL && p[len] == ptr[len])
2978 len++;
2979 // Adjust length to not break inside a multi-byte character
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002980 if (len > 0)
2981 len -= (*mb_head_off)(p, p + len);
glepnir40891ba2025-02-10 22:18:00 +01002982 // Add backspace characters for each remaining character in
2983 // original text
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002984 for (p += len; *p != NUL; MB_PTR_ADV(p))
2985 AppendCharToRedobuff(K_BS);
2986 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002987 if (ptr != NULL)
2988 AppendToRedobuffLit(ptr + len, -1);
2989}
2990
2991/*
2992 * Loops through the list of windows, loaded-buffers or non-loaded-buffers
2993 * (depending on flag) starting from buf and looking for a non-scanned
2994 * buffer (other than curbuf). curbuf is special, if it is called with
2995 * buf=curbuf then it has to be the first call for a given flag/expansion.
2996 *
2997 * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo
2998 */
2999 static buf_T *
3000ins_compl_next_buf(buf_T *buf, int flag)
3001{
glepnir19e1dd62025-05-08 22:50:38 +02003002 static win_T *wp = NULL;
3003 int skip_buffer;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003004
3005 if (flag == 'w') // just windows
3006 {
Bram Moolenaar0ff01832022-09-24 19:20:30 +01003007 if (buf == curbuf || !win_valid(wp))
3008 // first call for this flag/expansion or window was closed
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003009 wp = curwin;
glepnir40891ba2025-02-10 22:18:00 +01003010
3011 while (TRUE)
3012 {
Hirohito Higashi355db992025-04-28 18:07:02 +02003013 // Move to next window (wrap to first window if at the end)
3014 wp = (wp->w_next != NULL) ? wp->w_next : firstwin;
3015 // Break if we're back at start or found an unscanned buffer
3016 if (wp == curwin || !wp->w_buffer->b_scanned)
3017 break;
glepnir40891ba2025-02-10 22:18:00 +01003018 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003019 buf = wp->w_buffer;
3020 }
3021 else
glepnir40891ba2025-02-10 22:18:00 +01003022 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003023 // 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U'
3024 // (unlisted buffers)
3025 // When completing whole lines skip unloaded buffers.
glepnir40891ba2025-02-10 22:18:00 +01003026 while (TRUE)
3027 {
3028 // Move to next buffer (wrap to first buffer if at the end)
3029 buf = (buf->b_next != NULL) ? buf->b_next : firstbuf;
3030 // Break if we're back at start buffer
3031 if (buf == curbuf)
3032 break;
3033
3034 // Check buffer conditions based on flag
3035 if (flag == 'U')
3036 skip_buffer = buf->b_p_bl;
3037 else
3038 skip_buffer = !buf->b_p_bl ||
3039 (buf->b_ml.ml_mfp == NULL) != (flag == 'u');
3040
3041 // Break if we found a buffer that matches our criteria
3042 if (!skip_buffer && !buf->b_scanned)
3043 break;
3044 }
3045 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003046 return buf;
3047}
3048
3049#ifdef FEAT_COMPL_FUNC
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003050
3051# ifdef FEAT_EVAL
3052static callback_T cfu_cb; // 'completefunc' callback function
3053static callback_T ofu_cb; // 'omnifunc' callback function
3054static callback_T tsrfu_cb; // 'thesaurusfunc' callback function
3055# endif
3056
3057/*
3058 * Copy a global callback function to a buffer local callback.
3059 */
3060 static void
3061copy_global_to_buflocal_cb(callback_T *globcb, callback_T *bufcb)
3062{
3063 free_callback(bufcb);
3064 if (globcb->cb_name != NULL && *globcb->cb_name != NUL)
3065 copy_callback(bufcb, globcb);
3066}
3067
3068/*
3069 * Parse the 'completefunc' option value and set the callback function.
3070 * Invoked when the 'completefunc' option is set. The option value can be a
3071 * name of a function (string), or function(<name>) or funcref(<name>) or a
3072 * lambda expression.
3073 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003074 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003075did_set_completefunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003076{
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003077 if (option_set_callback_func(curbuf->b_p_cfu, &cfu_cb) == FAIL)
3078 return e_invalid_argument;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003079
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003080 set_buflocal_cfu_callback(curbuf);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003081
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003082 return NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003083}
3084
3085/*
3086 * Copy the global 'completefunc' callback function to the buffer-local
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003087 * 'completefunc' callback for "buf".
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003088 */
3089 void
3090set_buflocal_cfu_callback(buf_T *buf UNUSED)
3091{
3092# ifdef FEAT_EVAL
3093 copy_global_to_buflocal_cb(&cfu_cb, &buf->b_cfu_cb);
3094# endif
3095}
3096
3097/*
3098 * Parse the 'omnifunc' option value and set the callback function.
3099 * Invoked when the 'omnifunc' option is set. The option value can be a
3100 * name of a function (string), or function(<name>) or funcref(<name>) or a
3101 * lambda expression.
3102 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003103 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003104did_set_omnifunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003105{
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003106 if (option_set_callback_func(curbuf->b_p_ofu, &ofu_cb) == FAIL)
3107 return e_invalid_argument;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003108
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003109 set_buflocal_ofu_callback(curbuf);
3110 return NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003111}
3112
3113/*
3114 * Copy the global 'omnifunc' callback function to the buffer-local 'omnifunc'
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003115 * callback for "buf".
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003116 */
3117 void
3118set_buflocal_ofu_callback(buf_T *buf UNUSED)
3119{
3120# ifdef FEAT_EVAL
3121 copy_global_to_buflocal_cb(&ofu_cb, &buf->b_ofu_cb);
3122# endif
3123}
3124
3125/*
3126 * Parse the 'thesaurusfunc' option value and set the callback function.
3127 * Invoked when the 'thesaurusfunc' option is set. The option value can be a
3128 * name of a function (string), or function(<name>) or funcref(<name>) or a
3129 * lambda expression.
3130 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003131 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00003132did_set_thesaurusfunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003133{
3134 int retval;
3135
zeertzjq6eda2692024-11-03 09:23:33 +01003136 if (args->os_flags & OPT_LOCAL)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003137 // buffer-local option set
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003138 retval = option_set_callback_func(curbuf->b_p_tsrfu,
3139 &curbuf->b_tsrfu_cb);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003140 else
3141 {
3142 // global option set
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003143 retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
zeertzjq6eda2692024-11-03 09:23:33 +01003144 // when using :set, free the local callback
3145 if (!(args->os_flags & OPT_GLOBAL))
3146 free_callback(&curbuf->b_tsrfu_cb);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003147 }
3148
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00003149 return retval == FAIL ? e_invalid_argument : NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003150}
3151
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00003152/*
3153 * Mark the global 'completefunc' 'omnifunc' and 'thesaurusfunc' callbacks with
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003154 * "copyID" so that they are not garbage collected.
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00003155 */
3156 int
3157set_ref_in_insexpand_funcs(int copyID)
3158{
glepnir19e1dd62025-05-08 22:50:38 +02003159 int abort = set_ref_in_callback(&cfu_cb, copyID);
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00003160 abort = abort || set_ref_in_callback(&ofu_cb, copyID);
3161 abort = abort || set_ref_in_callback(&tsrfu_cb, copyID);
3162
3163 return abort;
3164}
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003165
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003166/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003167 * Get the user-defined completion function name for completion "type"
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01003168 */
3169 static char_u *
3170get_complete_funcname(int type)
3171{
3172 switch (type)
3173 {
3174 case CTRL_X_FUNCTION:
3175 return curbuf->b_p_cfu;
3176 case CTRL_X_OMNI:
3177 return curbuf->b_p_ofu;
3178 case CTRL_X_THESAURUS:
Bram Moolenaarf4d8b762021-10-17 14:13:09 +01003179 return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu;
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01003180 default:
3181 return (char_u *)"";
3182 }
3183}
3184
3185/*
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003186 * Get the callback to use for insert mode completion.
3187 */
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003188 static callback_T *
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003189get_insert_callback(int type)
3190{
3191 if (type == CTRL_X_FUNCTION)
3192 return &curbuf->b_cfu_cb;
3193 if (type == CTRL_X_OMNI)
3194 return &curbuf->b_ofu_cb;
3195 // CTRL_X_THESAURUS
3196 return (*curbuf->b_p_tsrfu != NUL) ? &curbuf->b_tsrfu_cb : &tsrfu_cb;
3197}
3198
3199/*
Yegappan Lakshmanan05e59e32021-12-01 10:30:07 +00003200 * Execute user defined complete function 'completefunc', 'omnifunc' or
3201 * 'thesaurusfunc', and get matches in "matches".
Girish Palyacbe53192025-04-14 22:13:15 +02003202 * "type" can be one of CTRL_X_OMNI, CTRL_X_FUNCTION, or CTRL_X_THESAURUS.
3203 * Callback function "cb" is set if triggered by a function in the 'cpt'
3204 * option; otherwise, it is NULL.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003205 */
3206 static void
Girish Palyacbe53192025-04-14 22:13:15 +02003207expand_by_function(int type, char_u *base, callback_T *cb)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003208{
3209 list_T *matchlist = NULL;
3210 dict_T *matchdict = NULL;
3211 typval_T args[3];
3212 char_u *funcname;
3213 pos_T pos;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003214 typval_T rettv;
3215 int save_State = State;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003216 int retval;
Girish Palyacbe53192025-04-14 22:13:15 +02003217 int is_cpt_function = (cb != NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003218
Girish Palyacbe53192025-04-14 22:13:15 +02003219 if (!is_cpt_function)
3220 {
3221 funcname = get_complete_funcname(type);
3222 if (*funcname == NUL)
3223 return;
3224 cb = get_insert_callback(type);
3225 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003226
3227 // Call 'completefunc' to obtain the list of matches.
3228 args[0].v_type = VAR_NUMBER;
3229 args[0].vval.v_number = 0;
3230 args[1].v_type = VAR_STRING;
3231 args[1].vval.v_string = base != NULL ? base : (char_u *)"";
3232 args[2].v_type = VAR_UNKNOWN;
3233
3234 pos = curwin->w_cursor;
Bram Moolenaar28976e22021-01-29 21:07:07 +01003235 // Lock the text to avoid weird things from happening. Also disallow
3236 // switching to another window, it should not be needed and may end up in
3237 // Insert mode in another buffer.
zeertzjqcfe45652022-05-27 17:26:55 +01003238 ++textlock;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003239
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003240 retval = call_callback(cb, 0, &rettv, 2, args);
3241
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003242 // Call a function, which returns a list or dict.
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00003243 if (retval == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003244 {
3245 switch (rettv.v_type)
3246 {
3247 case VAR_LIST:
3248 matchlist = rettv.vval.v_list;
3249 break;
3250 case VAR_DICT:
3251 matchdict = rettv.vval.v_dict;
3252 break;
3253 case VAR_SPECIAL:
3254 if (rettv.vval.v_number == VVAL_NONE)
3255 compl_opt_suppress_empty = TRUE;
3256 // FALLTHROUGH
3257 default:
3258 // TODO: Give error message?
3259 clear_tv(&rettv);
3260 break;
3261 }
3262 }
zeertzjqcfe45652022-05-27 17:26:55 +01003263 --textlock;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003264
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003265 curwin->w_cursor = pos; // restore the cursor position
zeertzjq0a419e02024-04-02 19:01:14 +02003266 check_cursor(); // make sure cursor position is valid, just in case
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003267 validate_cursor();
3268 if (!EQUAL_POS(curwin->w_cursor, pos))
3269 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003270 emsg(_(e_complete_function_deleted_text));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003271 goto theend;
3272 }
3273
3274 if (matchlist != NULL)
3275 ins_compl_add_list(matchlist);
3276 else if (matchdict != NULL)
3277 ins_compl_add_dict(matchdict);
3278
3279theend:
3280 // Restore State, it might have been changed.
3281 State = save_State;
3282
3283 if (matchdict != NULL)
3284 dict_unref(matchdict);
3285 if (matchlist != NULL)
3286 list_unref(matchlist);
3287}
3288#endif // FEAT_COMPL_FUNC
3289
3290#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) || defined(PROTO)
glepnir38f99a12024-08-23 18:31:06 +02003291
3292 static inline int
3293get_user_highlight_attr(char_u *hlname)
3294{
3295 if (hlname != NULL && *hlname != NUL)
Hirohito Higashi355db992025-04-28 18:07:02 +02003296 return syn_name2attr(hlname);
glepnir38f99a12024-08-23 18:31:06 +02003297 return -1;
3298}
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003299/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003300 * Add a match to the list of matches from a typeval_T.
3301 * If the given string is already in the list of completions, then return
3302 * NOTDONE, otherwise add it to the list and return OK. If there is an error,
3303 * maybe because alloc() returns NULL, then FAIL is returned.
Bram Moolenaar440cf092021-04-03 20:13:30 +02003304 * When "fast" is TRUE use fast_breakcheck() instead of ui_breakcheck().
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003305 */
3306 static int
Bram Moolenaar440cf092021-04-03 20:13:30 +02003307ins_compl_add_tv(typval_T *tv, int dir, int fast)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003308{
3309 char_u *word;
3310 int dup = FALSE;
3311 int empty = FALSE;
Bram Moolenaar440cf092021-04-03 20:13:30 +02003312 int flags = fast ? CP_FAST : 0;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003313 char_u *(cptext[CPT_COUNT]);
Bram Moolenaar08928322020-01-04 14:32:48 +01003314 typval_T user_data;
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003315 int status;
glepnir0fe17f82024-10-08 22:26:44 +02003316 char_u *user_abbr_hlname;
glepnir38f99a12024-08-23 18:31:06 +02003317 char_u *user_kind_hlname;
glepnir80b66202024-11-27 21:53:53 +01003318 int user_hl[2] = { -1, -1 };
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003319
Bram Moolenaar08928322020-01-04 14:32:48 +01003320 user_data.v_type = VAR_UNKNOWN;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003321 if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL)
3322 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01003323 word = dict_get_string(tv->vval.v_dict, "word", FALSE);
3324 cptext[CPT_ABBR] = dict_get_string(tv->vval.v_dict, "abbr", FALSE);
3325 cptext[CPT_MENU] = dict_get_string(tv->vval.v_dict, "menu", FALSE);
3326 cptext[CPT_KIND] = dict_get_string(tv->vval.v_dict, "kind", FALSE);
3327 cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict, "info", FALSE);
glepnir38f99a12024-08-23 18:31:06 +02003328
glepnir0fe17f82024-10-08 22:26:44 +02003329 user_abbr_hlname = dict_get_string(tv->vval.v_dict, "abbr_hlgroup", FALSE);
glepnir80b66202024-11-27 21:53:53 +01003330 user_hl[0] = get_user_highlight_attr(user_abbr_hlname);
glepnir38f99a12024-08-23 18:31:06 +02003331
3332 user_kind_hlname = dict_get_string(tv->vval.v_dict, "kind_hlgroup", FALSE);
glepnir80b66202024-11-27 21:53:53 +01003333 user_hl[1] = get_user_highlight_attr(user_kind_hlname);
glepnir508e7852024-07-25 21:39:08 +02003334
Bram Moolenaard61efa52022-07-23 09:52:04 +01003335 dict_get_tv(tv->vval.v_dict, "user_data", &user_data);
3336 if (dict_get_string(tv->vval.v_dict, "icase", FALSE) != NULL
3337 && dict_get_number(tv->vval.v_dict, "icase"))
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003338 flags |= CP_ICASE;
Bram Moolenaard61efa52022-07-23 09:52:04 +01003339 if (dict_get_string(tv->vval.v_dict, "dup", FALSE) != NULL)
3340 dup = dict_get_number(tv->vval.v_dict, "dup");
3341 if (dict_get_string(tv->vval.v_dict, "empty", FALSE) != NULL)
3342 empty = dict_get_number(tv->vval.v_dict, "empty");
3343 if (dict_get_string(tv->vval.v_dict, "equal", FALSE) != NULL
3344 && dict_get_number(tv->vval.v_dict, "equal"))
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003345 flags |= CP_EQUAL;
3346 }
3347 else
3348 {
3349 word = tv_get_string_chk(tv);
Bram Moolenaara80faa82020-04-12 19:37:17 +02003350 CLEAR_FIELD(cptext);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003351 }
3352 if (word == NULL || (!empty && *word == NUL))
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003353 {
3354 clear_tv(&user_data);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003355 return FAIL;
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003356 }
glepnir508e7852024-07-25 21:39:08 +02003357 status = ins_compl_add(word, -1, NULL, cptext,
glepnirf31cfa22025-03-06 21:59:13 +01003358 &user_data, dir, flags, dup, user_hl, 0);
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003359 if (status != OK)
3360 clear_tv(&user_data);
3361 return status;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003362}
3363
3364/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003365 * Add completions from a list.
3366 */
3367 static void
3368ins_compl_add_list(list_T *list)
3369{
3370 listitem_T *li;
3371 int dir = compl_direction;
3372
3373 // Go through the List with matches and add each of them.
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02003374 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02003375 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003376 {
Bram Moolenaar440cf092021-04-03 20:13:30 +02003377 if (ins_compl_add_tv(&li->li_tv, dir, TRUE) == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003378 // if dir was BACKWARD then honor it just once
3379 dir = FORWARD;
3380 else if (did_emsg)
3381 break;
3382 }
3383}
3384
3385/*
3386 * Add completions from a dict.
3387 */
3388 static void
3389ins_compl_add_dict(dict_T *dict)
3390{
3391 dictitem_T *di_refresh;
3392 dictitem_T *di_words;
3393
3394 // Check for optional "refresh" item.
3395 compl_opt_refresh_always = FALSE;
3396 di_refresh = dict_find(dict, (char_u *)"refresh", 7);
3397 if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING)
3398 {
3399 char_u *v = di_refresh->di_tv.vval.v_string;
3400
3401 if (v != NULL && STRCMP(v, (char_u *)"always") == 0)
3402 compl_opt_refresh_always = TRUE;
3403 }
3404
3405 // Add completions from a "words" list.
3406 di_words = dict_find(dict, (char_u *)"words", 5);
3407 if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST)
3408 ins_compl_add_list(di_words->di_tv.vval.v_list);
3409}
3410
3411/*
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003412 * Start completion for the complete() function.
3413 * "startcol" is where the matched text starts (1 is first column).
3414 * "list" is the list of matches.
3415 */
3416 static void
3417set_completion(colnr_T startcol, list_T *list)
3418{
3419 int save_w_wrow = curwin->w_wrow;
3420 int save_w_leftcol = curwin->w_leftcol;
3421 int flags = CP_ORIGINAL_TEXT;
zeertzjqaa925ee2024-06-09 18:24:05 +02003422 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02003423 int compl_longest = (cur_cot_flags & COT_LONGEST) != 0;
3424 int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
3425 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003426
3427 // If already doing completions stop it.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003428 if (ctrl_x_mode_not_default())
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003429 ins_compl_prep(' ');
3430 ins_compl_clear();
3431 ins_compl_free();
bfredl87af60c2022-09-24 11:17:51 +01003432 compl_get_longest = compl_longest;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003433
3434 compl_direction = FORWARD;
3435 if (startcol > curwin->w_cursor.col)
3436 startcol = curwin->w_cursor.col;
3437 compl_col = startcol;
glepnir76bdb822025-02-08 19:04:51 +01003438 compl_lnum = curwin->w_cursor.lnum;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003439 compl_length = (int)curwin->w_cursor.col - (int)startcol;
3440 // compl_pattern doesn't need to be set
glepniredd4ac32025-01-29 18:53:51 +01003441 compl_orig_text.string = vim_strnsave(ml_get_curline() + compl_col,
3442 (size_t)compl_length);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003443 if (p_ic)
3444 flags |= CP_ICASE;
John Marriott5e6ea922024-11-23 14:01:57 +01003445 if (compl_orig_text.string == NULL)
3446 {
3447 compl_orig_text.length = 0;
3448 return;
3449 }
3450 compl_orig_text.length = (size_t)compl_length;
3451 if (ins_compl_add(compl_orig_text.string,
Hirohito Higashi355db992025-04-28 18:07:02 +02003452 (int)compl_orig_text.length, NULL, NULL, NULL, 0,
3453 flags | CP_FAST, FALSE, NULL, 0) != OK)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003454 return;
3455
3456 ctrl_x_mode = CTRL_X_EVAL;
3457
3458 ins_compl_add_list(list);
3459 compl_matches = ins_compl_make_cyclic();
3460 compl_started = TRUE;
3461 compl_used_match = TRUE;
3462 compl_cont_status = 0;
3463
3464 compl_curr_match = compl_first_match;
bfredl87af60c2022-09-24 11:17:51 +01003465 int no_select = compl_no_select || compl_longest;
3466 if (compl_no_insert || no_select)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003467 {
3468 ins_complete(K_DOWN, FALSE);
bfredl87af60c2022-09-24 11:17:51 +01003469 if (no_select)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003470 // Down/Up has no real effect.
3471 ins_complete(K_UP, FALSE);
3472 }
3473 else
3474 ins_complete(Ctrl_N, FALSE);
3475 compl_enter_selects = compl_no_insert;
3476
3477 // Lazily show the popup menu, unless we got interrupted.
3478 if (!compl_interrupted)
3479 show_pum(save_w_wrow, save_w_leftcol);
LemonBoy2bf52dd2022-04-09 18:17:34 +01003480 may_trigger_modechanged();
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003481 out_flush();
3482}
3483
3484/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003485 * "complete()" function
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003486 */
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003487 void
3488f_complete(typval_T *argvars, typval_T *rettv UNUSED)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003489{
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003490 if (in_vim9script()
3491 && (check_for_number_arg(argvars, 0) == FAIL
3492 || check_for_list_arg(argvars, 1) == FAIL))
3493 return;
3494
Bram Moolenaar24959102022-05-07 20:01:16 +01003495 if ((State & MODE_INSERT) == 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003496 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00003497 emsg(_(e_complete_can_only_be_used_in_insert_mode));
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003498 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003499 }
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003500
3501 // Check for undo allowed here, because if something was already inserted
3502 // the line was already saved for undo and this check isn't done.
3503 if (!undo_allowed())
3504 return;
3505
Bram Moolenaard83392a2022-09-01 12:22:46 +01003506 if (check_for_nonnull_list_arg(argvars, 1) != FAIL)
Bram Moolenaarff06f282020-04-21 22:01:14 +02003507 {
glepnir19e1dd62025-05-08 22:50:38 +02003508 int startcol = (int)tv_get_number_chk(&argvars[0], NULL);
Bram Moolenaarff06f282020-04-21 22:01:14 +02003509 if (startcol > 0)
3510 set_completion(startcol - 1, argvars[1].vval.v_list);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003511 }
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003512}
3513
3514/*
3515 * "complete_add()" function
3516 */
3517 void
3518f_complete_add(typval_T *argvars, typval_T *rettv)
3519{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003520 if (in_vim9script() && check_for_string_or_dict_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003521 return;
3522
Bram Moolenaar440cf092021-04-03 20:13:30 +02003523 rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, FALSE);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003524}
3525
3526/*
3527 * "complete_check()" function
3528 */
3529 void
3530f_complete_check(typval_T *argvars UNUSED, typval_T *rettv)
3531{
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003532 int save_RedrawingDisabled = RedrawingDisabled;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003533 RedrawingDisabled = 0;
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003534
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003535 ins_compl_check_keys(0, TRUE);
3536 rettv->vval.v_number = ins_compl_interrupted();
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003537
3538 RedrawingDisabled = save_RedrawingDisabled;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003539}
3540
3541/*
glepnirbcd59952025-04-24 21:48:35 +02003542 * Add match item to the return list.
3543 * Returns FAIL if out of memory, OK otherwise.
3544 */
3545 static int
3546add_match_to_list(
3547 typval_T *rettv,
3548 char_u *str,
3549 int len,
3550 int pos)
3551{
3552 list_T *match;
3553 int ret;
3554
3555 match = list_alloc();
3556 if (match == NULL)
Hirohito Higashi355db992025-04-28 18:07:02 +02003557 return FAIL;
glepnirbcd59952025-04-24 21:48:35 +02003558
3559 if ((ret = list_append_number(match, pos + 1)) == FAIL
3560 || (ret = list_append_string(match, str, len)) == FAIL
3561 || (ret = list_append_list(rettv->vval.v_list, match)) == FAIL)
3562 {
Hirohito Higashi355db992025-04-28 18:07:02 +02003563 vim_free(match);
3564 return FAIL;
glepnirbcd59952025-04-24 21:48:35 +02003565 }
3566
3567 return OK;
3568}
3569
3570/*
3571 * "complete_match()" function
3572 */
3573 void
3574f_complete_match(typval_T *argvars, typval_T *rettv)
3575{
3576 linenr_T lnum;
3577 colnr_T col;
3578 char_u *line = NULL;
3579 char_u *ise = NULL;
3580 regmatch_T regmatch;
3581 char_u *before_cursor = NULL;
3582 char_u *cur_end = NULL;
glepnirbcd59952025-04-24 21:48:35 +02003583 int bytepos = 0;
3584 char_u part[MAXPATHL];
3585 int ret;
3586
3587 if (rettv_list_alloc(rettv) == FAIL)
3588 return;
3589
3590 ise = curbuf->b_p_ise[0] != NUL ? curbuf->b_p_ise : p_ise;
3591
3592 if (argvars[0].v_type == VAR_UNKNOWN)
3593 {
3594 lnum = curwin->w_cursor.lnum;
3595 col = curwin->w_cursor.col;
3596 }
3597 else if (argvars[1].v_type == VAR_UNKNOWN)
3598 {
3599 emsg(_(e_invalid_argument));
3600 return;
3601 }
3602 else
3603 {
3604 lnum = (linenr_T)tv_get_number(&argvars[0]);
3605 col = (colnr_T)tv_get_number(&argvars[1]);
3606 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3607 {
3608 semsg(_(e_invalid_line_number_nr), lnum);
3609 return;
3610 }
3611 if (col < 1 || col > ml_get_buf_len(curbuf, lnum))
3612 {
3613 semsg(_(e_invalid_column_number_nr), col + 1);
3614 return;
3615 }
3616 }
3617
3618 line = ml_get_buf(curbuf, lnum, FALSE);
3619 if (line == NULL)
3620 return;
3621
3622 before_cursor = vim_strnsave(line, col);
3623 if (before_cursor == NULL)
3624 return;
3625
3626 if (ise == NULL || *ise == NUL)
3627 {
3628 regmatch.regprog = vim_regcomp((char_u *)"\\k\\+$", RE_MAGIC);
3629 if (regmatch.regprog != NULL)
3630 {
3631 if (vim_regexec_nl(&regmatch, before_cursor, (colnr_T)0))
3632 {
Christian Brabandt3accf042025-04-25 19:01:06 +02003633 char_u *trig = vim_strnsave(regmatch.startp[0],
glepnirbcd59952025-04-24 21:48:35 +02003634 regmatch.endp[0] - regmatch.startp[0]);
3635 if (trig == NULL)
3636 {
3637 vim_free(before_cursor);
Christian Brabandt3accf042025-04-25 19:01:06 +02003638 vim_regfree(regmatch.regprog);
glepnirbcd59952025-04-24 21:48:35 +02003639 return;
3640 }
3641
Christian Brabandt3accf042025-04-25 19:01:06 +02003642 bytepos = (int)(regmatch.startp[0] - before_cursor);
glepnirbcd59952025-04-24 21:48:35 +02003643 ret = add_match_to_list(rettv, trig, -1, bytepos);
3644 vim_free(trig);
3645 if (ret == FAIL)
3646 {
Christian Brabandt3accf042025-04-25 19:01:06 +02003647 vim_free(before_cursor);
glepnirbcd59952025-04-24 21:48:35 +02003648 vim_regfree(regmatch.regprog);
3649 return;
3650 }
3651 }
3652 vim_regfree(regmatch.regprog);
3653 }
3654 }
3655 else
3656 {
3657 char_u *p = ise;
3658 cur_end = before_cursor + (int)STRLEN(before_cursor);
3659
3660 while (*p != NUL)
3661 {
3662 int len = copy_option_part(&p, part, MAXPATHL, ",");
3663
3664 if (len > 0 && len <= col)
3665 {
3666 if (STRNCMP(cur_end - len, part, len) == 0)
3667 {
3668 bytepos = col - len;
3669 if (add_match_to_list(rettv, part, len, bytepos) == FAIL)
3670 {
3671 vim_free(before_cursor);
3672 return;
3673 }
3674 }
3675 }
3676 }
3677 }
3678
3679 vim_free(before_cursor);
3680}
3681
3682/*
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003683 * Return Insert completion mode name string
3684 */
3685 static char_u *
3686ins_compl_mode(void)
3687{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003688 if (ctrl_x_mode_not_defined_yet() || ctrl_x_mode_scroll() || compl_started)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003689 return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
3690
3691 return (char_u *)"";
3692}
3693
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00003694/*
3695 * Assign the sequence number to all the completion matches which don't have
3696 * one assigned yet.
3697 */
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003698 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00003699ins_compl_update_sequence_numbers(void)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003700{
3701 int number = 0;
3702 compl_T *match;
3703
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003704 if (compl_dir_forward())
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003705 {
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003706 // Search backwards for the first valid (!= -1) number.
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003707 // This should normally succeed already at the first loop
3708 // cycle, so it's fast!
3709 for (match = compl_curr_match->cp_prev; match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003710 && !is_first_match(match); match = match->cp_prev)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003711 if (match->cp_number != -1)
3712 {
3713 number = match->cp_number;
3714 break;
3715 }
3716 if (match != NULL)
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003717 // go up and assign all numbers which are not assigned yet
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003718 for (match = match->cp_next;
3719 match != NULL && match->cp_number == -1;
3720 match = match->cp_next)
3721 match->cp_number = ++number;
3722 }
3723 else // BACKWARD
3724 {
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003725 // Search forwards (upwards) for the first valid (!= -1)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003726 // number. This should normally succeed already at the
3727 // first loop cycle, so it's fast!
3728 for (match = compl_curr_match->cp_next; match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003729 && !is_first_match(match); match = match->cp_next)
glepnir40891ba2025-02-10 22:18:00 +01003730 {
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003731 if (match->cp_number != -1)
3732 {
3733 number = match->cp_number;
3734 break;
3735 }
glepnir40891ba2025-02-10 22:18:00 +01003736 }
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003737 if (match != NULL)
glepnir40891ba2025-02-10 22:18:00 +01003738 {
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003739 // go down and assign all numbers which are not assigned yet
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003740 for (match = match->cp_prev; match
3741 && match->cp_number == -1;
3742 match = match->cp_prev)
3743 match->cp_number = ++number;
glepnir40891ba2025-02-10 22:18:00 +01003744 }
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003745 }
3746}
3747
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003748/*
glepnir037b0282025-01-16 14:37:44 +01003749 * Fill the dict of complete_info
3750 */
3751 static void
3752fill_complete_info_dict(dict_T *di, compl_T *match, int add_match)
3753{
3754 dict_add_string(di, "word", match->cp_str.string);
3755 dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]);
3756 dict_add_string(di, "menu", match->cp_text[CPT_MENU]);
3757 dict_add_string(di, "kind", match->cp_text[CPT_KIND]);
3758 dict_add_string(di, "info", match->cp_text[CPT_INFO]);
3759 if (add_match)
Hirohito Higashi355db992025-04-28 18:07:02 +02003760 dict_add_bool(di, "match", match->cp_in_match_array);
glepnir037b0282025-01-16 14:37:44 +01003761 if (match->cp_user_data.v_type == VAR_UNKNOWN)
Hirohito Higashi355db992025-04-28 18:07:02 +02003762 // Add an empty string for backwards compatibility
3763 dict_add_string(di, "user_data", (char_u *)"");
glepnir037b0282025-01-16 14:37:44 +01003764 else
Hirohito Higashi355db992025-04-28 18:07:02 +02003765 dict_add_tv(di, "user_data", &match->cp_user_data);
glepnir037b0282025-01-16 14:37:44 +01003766}
3767
3768/*
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003769 * Get complete information
3770 */
3771 static void
3772get_complete_info(list_T *what_list, dict_T *retdict)
3773{
3774 int ret = OK;
3775 listitem_T *item;
3776#define CI_WHAT_MODE 0x01
3777#define CI_WHAT_PUM_VISIBLE 0x02
3778#define CI_WHAT_ITEMS 0x04
3779#define CI_WHAT_SELECTED 0x08
glepnir037b0282025-01-16 14:37:44 +01003780#define CI_WHAT_COMPLETED 0x10
glepnird4088ed2024-12-31 10:55:22 +01003781#define CI_WHAT_MATCHES 0x20
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003782#define CI_WHAT_ALL 0xff
3783 int what_flag;
Girish Palya0ac1eb32025-04-16 20:18:33 +02003784 int compl_fuzzy_match = (get_cot_flags() & COT_FUZZY) != 0;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003785
3786 if (what_list == NULL)
glepnir037b0282025-01-16 14:37:44 +01003787 what_flag = CI_WHAT_ALL & ~(CI_WHAT_MATCHES | CI_WHAT_COMPLETED);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003788 else
3789 {
3790 what_flag = 0;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02003791 CHECK_LIST_MATERIALIZE(what_list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02003792 FOR_ALL_LIST_ITEMS(what_list, item)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003793 {
3794 char_u *what = tv_get_string(&item->li_tv);
3795
3796 if (STRCMP(what, "mode") == 0)
3797 what_flag |= CI_WHAT_MODE;
3798 else if (STRCMP(what, "pum_visible") == 0)
3799 what_flag |= CI_WHAT_PUM_VISIBLE;
3800 else if (STRCMP(what, "items") == 0)
3801 what_flag |= CI_WHAT_ITEMS;
3802 else if (STRCMP(what, "selected") == 0)
3803 what_flag |= CI_WHAT_SELECTED;
glepnir037b0282025-01-16 14:37:44 +01003804 else if (STRCMP(what, "completed") == 0)
3805 what_flag |= CI_WHAT_COMPLETED;
glepnird4088ed2024-12-31 10:55:22 +01003806 else if (STRCMP(what, "matches") == 0)
3807 what_flag |= CI_WHAT_MATCHES;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003808 }
3809 }
3810
3811 if (ret == OK && (what_flag & CI_WHAT_MODE))
3812 ret = dict_add_string(retdict, "mode", ins_compl_mode());
3813
3814 if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE))
3815 ret = dict_add_number(retdict, "pum_visible", pum_visible());
3816
glepnir037b0282025-01-16 14:37:44 +01003817 if (ret == OK && (what_flag & (CI_WHAT_ITEMS | CI_WHAT_SELECTED
3818 | CI_WHAT_MATCHES | CI_WHAT_COMPLETED)))
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003819 {
Christian Brabandt9eb236f2024-03-21 20:12:59 +01003820 list_T *li = NULL;
Girish Palya8950bf72024-03-20 20:07:29 +01003821 dict_T *di;
3822 compl_T *match;
3823 int selected_idx = -1;
glepnird4088ed2024-12-31 10:55:22 +01003824 int has_items = what_flag & CI_WHAT_ITEMS;
3825 int has_matches = what_flag & CI_WHAT_MATCHES;
glepnir037b0282025-01-16 14:37:44 +01003826 int has_completed = what_flag & CI_WHAT_COMPLETED;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003827
glepnird4088ed2024-12-31 10:55:22 +01003828 if (has_items || has_matches)
Girish Palya8950bf72024-03-20 20:07:29 +01003829 {
3830 li = list_alloc();
3831 if (li == NULL)
3832 return;
glepnird4088ed2024-12-31 10:55:22 +01003833 ret = dict_add_list(retdict, (has_matches && !has_items)
3834 ? "matches" : "items", li);
Girish Palya8950bf72024-03-20 20:07:29 +01003835 }
3836 if (ret == OK && what_flag & CI_WHAT_SELECTED)
3837 if (compl_curr_match != NULL && compl_curr_match->cp_number == -1)
3838 ins_compl_update_sequence_numbers();
3839 if (ret == OK && compl_first_match != NULL)
3840 {
3841 int list_idx = 0;
3842 match = compl_first_match;
3843 do
3844 {
3845 if (!match_at_original_text(match))
3846 {
glepnird4088ed2024-12-31 10:55:22 +01003847 if (has_items
3848 || (has_matches && match->cp_in_match_array))
Girish Palya8950bf72024-03-20 20:07:29 +01003849 {
3850 di = dict_alloc();
3851 if (di == NULL)
3852 return;
3853 ret = list_append_dict(li, di);
3854 if (ret != OK)
3855 return;
glepnir037b0282025-01-16 14:37:44 +01003856 fill_complete_info_dict(di, match, has_matches && has_items);
Girish Palya8950bf72024-03-20 20:07:29 +01003857 }
glepnird4088ed2024-12-31 10:55:22 +01003858 if (compl_curr_match != NULL
3859 && compl_curr_match->cp_number == match->cp_number)
Girish Palya8950bf72024-03-20 20:07:29 +01003860 selected_idx = list_idx;
Girish Palya0ac1eb32025-04-16 20:18:33 +02003861 if (compl_fuzzy_match || match->cp_in_match_array)
3862 list_idx += 1;
Girish Palya8950bf72024-03-20 20:07:29 +01003863 }
3864 match = match->cp_next;
3865 }
3866 while (match != NULL && !is_first_match(match));
3867 }
3868 if (ret == OK && (what_flag & CI_WHAT_SELECTED))
3869 ret = dict_add_number(retdict, "selected", selected_idx);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003870
glepnir037b0282025-01-16 14:37:44 +01003871 if (ret == OK && selected_idx != -1 && has_completed)
3872 {
3873 di = dict_alloc();
3874 if (di == NULL)
3875 return;
3876 fill_complete_info_dict(di, compl_curr_match, FALSE);
3877 ret = dict_add_dict(retdict, "completed", di);
3878 }
Yegappan Lakshmanan6b085b92022-09-04 12:47:21 +01003879 }
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003880}
3881
3882/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003883 * "complete_info()" function
3884 */
3885 void
3886f_complete_info(typval_T *argvars, typval_T *rettv)
3887{
3888 list_T *what_list = NULL;
3889
Bram Moolenaar93a10962022-06-16 11:42:09 +01003890 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003891 return;
3892
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003893 if (in_vim9script() && check_for_opt_list_arg(argvars, 0) == FAIL)
3894 return;
3895
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003896 if (argvars[0].v_type != VAR_UNKNOWN)
3897 {
Bram Moolenaard83392a2022-09-01 12:22:46 +01003898 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003899 return;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003900 what_list = argvars[0].vval.v_list;
3901 }
3902 get_complete_info(what_list, rettv->vval.v_dict);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003903}
3904#endif
3905
3906/*
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01003907 * Returns TRUE when using a user-defined function for thesaurus completion.
3908 */
3909 static int
3910thesaurus_func_complete(int type UNUSED)
3911{
3912#ifdef FEAT_COMPL_FUNC
Bram Moolenaarf4d8b762021-10-17 14:13:09 +01003913 return type == CTRL_X_THESAURUS
3914 && (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL);
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01003915#else
3916 return FALSE;
3917#endif
3918}
3919
3920/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003921 * Return value of process_next_cpt_value()
3922 */
3923enum
3924{
3925 INS_COMPL_CPT_OK = 1,
3926 INS_COMPL_CPT_CONT,
3927 INS_COMPL_CPT_END
3928};
3929
3930/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003931 * state information used for getting the next set of insert completion
3932 * matches.
3933 */
3934typedef struct
3935{
Bram Moolenaar0ff01832022-09-24 19:20:30 +01003936 char_u *e_cpt_copy; // copy of 'complete'
3937 char_u *e_cpt; // current entry in "e_cpt_copy"
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003938 buf_T *ins_buf; // buffer being scanned
Bram Moolenaar0ff01832022-09-24 19:20:30 +01003939 pos_T *cur_match_pos; // current match position
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003940 pos_T prev_match_pos; // previous match position
3941 int set_match_pos; // save first_match_pos/last_match_pos
3942 pos_T first_match_pos; // first match position
3943 pos_T last_match_pos; // last match position
3944 int found_all; // found all matches of a certain type.
3945 char_u *dict; // dictionary file to search
3946 int dict_f; // "dict" is an exact file name or not
Girish Palyacbe53192025-04-14 22:13:15 +02003947 callback_T *func_cb; // callback of function in 'cpt' option
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003948} ins_compl_next_state_T;
3949
3950/*
3951 * Process the next 'complete' option value in st->e_cpt.
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003952 *
3953 * If successful, the arguments are set as below:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003954 * st->cpt - pointer to the next option value in "st->cpt"
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003955 * compl_type_arg - type of insert mode completion to use
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003956 * st->found_all - all matches of this type are found
3957 * st->ins_buf - search for completions in this buffer
3958 * st->first_match_pos - position of the first completion match
3959 * st->last_match_pos - position of the last completion match
3960 * st->set_match_pos - TRUE if the first match position should be saved to
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01003961 * avoid loops after the search wraps around.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003962 * st->dict - name of the dictionary or thesaurus file to search
3963 * st->dict_f - flag specifying whether "dict" is an exact file name or not
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003964 *
3965 * Returns INS_COMPL_CPT_OK if the next value is processed successfully.
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003966 * Returns INS_COMPL_CPT_CONT to skip the current completion source matching
3967 * the "st->e_cpt" option value and process the next matching source.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003968 * Returns INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003969 */
3970 static int
3971process_next_cpt_value(
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003972 ins_compl_next_state_T *st,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003973 int *compl_type_arg,
glepnir8159fb12024-07-17 20:32:54 +02003974 pos_T *start_match_pos,
glepnir53b14572025-03-12 21:28:39 +01003975 int fuzzy_collect)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003976{
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003977 int compl_type = -1;
3978 int status = INS_COMPL_CPT_OK;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003979
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003980 st->found_all = FALSE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003981
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003982 while (*st->e_cpt == ',' || *st->e_cpt == ' ')
3983 st->e_cpt++;
3984
3985 if (*st->e_cpt == '.' && !curbuf->b_scanned)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003986 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003987 st->ins_buf = curbuf;
3988 st->first_match_pos = *start_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003989 // Move the cursor back one character so that ^N can match the
3990 // word immediately after the cursor.
glepnir53b14572025-03-12 21:28:39 +01003991 if (ctrl_x_mode_normal() && (!fuzzy_collect && dec(&st->first_match_pos) < 0))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003992 {
3993 // Move the cursor to after the last character in the
3994 // buffer, so that word at start of buffer is found
3995 // correctly.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003996 st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count;
zeertzjq94b7c322024-03-12 21:50:32 +01003997 st->first_match_pos.col = ml_get_len(st->first_match_pos.lnum);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003998 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003999 st->last_match_pos = st->first_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004000 compl_type = 0;
4001
4002 // Remember the first match so that the loop stops when we
4003 // wrap and come back there a second time.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004004 st->set_match_pos = TRUE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004005 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004006 else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004007 && (st->ins_buf = ins_compl_next_buf(
4008 st->ins_buf, *st->e_cpt)) != curbuf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004009 {
4010 // Scan a buffer, but not the current one.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004011 if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004012 {
4013 compl_started = TRUE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004014 st->first_match_pos.col = st->last_match_pos.col = 0;
4015 st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1;
4016 st->last_match_pos.lnum = 0;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004017 compl_type = 0;
4018 }
4019 else // unloaded buffer, scan like dictionary
4020 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004021 st->found_all = TRUE;
4022 if (st->ins_buf->b_fname == NULL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004023 {
4024 status = INS_COMPL_CPT_CONT;
4025 goto done;
4026 }
4027 compl_type = CTRL_X_DICTIONARY;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004028 st->dict = st->ins_buf->b_fname;
4029 st->dict_f = DICT_EXACT;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004030 }
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01004031 if (!shortmess(SHM_COMPLETIONSCAN))
4032 {
4033 msg_hist_off = TRUE; // reset in msg_trunc_attr()
4034 vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
4035 st->ins_buf->b_fname == NULL
4036 ? buf_spname(st->ins_buf)
4037 : st->ins_buf->b_sfname == NULL
4038 ? st->ins_buf->b_fname
4039 : st->ins_buf->b_sfname);
4040 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
4041 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004042 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004043 else if (*st->e_cpt == NUL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004044 status = INS_COMPL_CPT_END;
4045 else
4046 {
4047 if (ctrl_x_mode_line_or_eval())
4048 compl_type = -1;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004049 else if (*st->e_cpt == 'k' || *st->e_cpt == 's')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004050 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004051 if (*st->e_cpt == 'k')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004052 compl_type = CTRL_X_DICTIONARY;
4053 else
4054 compl_type = CTRL_X_THESAURUS;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004055 if (*++st->e_cpt != ',' && *st->e_cpt != NUL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004056 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004057 st->dict = st->e_cpt;
4058 st->dict_f = DICT_FIRST;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004059 }
4060 }
Girish Palyacbe53192025-04-14 22:13:15 +02004061#ifdef FEAT_COMPL_FUNC
4062 else if (*st->e_cpt == 'f' || *st->e_cpt == 'o')
4063 {
4064 compl_type = CTRL_X_FUNCTION;
4065 if (*st->e_cpt == 'o')
4066 st->func_cb = &curbuf->b_ofu_cb;
4067 else
4068 st->func_cb = (*++st->e_cpt != ',' && *st->e_cpt != NUL)
4069 ? get_cpt_func_callback(st->e_cpt) : &curbuf->b_cfu_cb;
4070 if (!st->func_cb)
4071 compl_type = -1;
4072 }
4073#endif
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004074#ifdef FEAT_FIND_ID
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004075 else if (*st->e_cpt == 'i')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004076 compl_type = CTRL_X_PATH_PATTERNS;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004077 else if (*st->e_cpt == 'd')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004078 compl_type = CTRL_X_PATH_DEFINES;
4079#endif
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004080 else if (*st->e_cpt == ']' || *st->e_cpt == 't')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004081 {
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004082 compl_type = CTRL_X_TAGS;
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01004083 if (!shortmess(SHM_COMPLETIONSCAN))
4084 {
4085 msg_hist_off = TRUE; // reset in msg_trunc_attr()
4086 vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags."));
4087 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
4088 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004089 }
4090 else
4091 compl_type = -1;
4092
4093 // in any case e_cpt is advanced to the next entry
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004094 (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ",");
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004095
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004096 st->found_all = TRUE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004097 if (compl_type == -1)
4098 status = INS_COMPL_CPT_CONT;
4099 }
4100
4101done:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004102 *compl_type_arg = compl_type;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004103 return status;
4104}
4105
4106#ifdef FEAT_FIND_ID
4107/*
4108 * Get the next set of identifiers or defines matching "compl_pattern" in
4109 * included files.
4110 */
4111 static void
4112get_next_include_file_completion(int compl_type)
4113{
John Marriott5e6ea922024-11-23 14:01:57 +01004114 find_pattern_in_path(compl_pattern.string, compl_direction,
4115 (int)compl_pattern.length, FALSE, FALSE,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004116 (compl_type == CTRL_X_PATH_DEFINES
4117 && !(compl_cont_status & CONT_SOL))
4118 ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND,
Colin Kennedy21570352024-03-03 16:16:47 +01004119 (linenr_T)1, (linenr_T)MAXLNUM, FALSE);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004120}
4121#endif
4122
4123/*
4124 * Get the next set of words matching "compl_pattern" in dictionary or
4125 * thesaurus files.
4126 */
4127 static void
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004128get_next_dict_tsr_completion(int compl_type, char_u *dict, int dict_f)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004129{
4130#ifdef FEAT_COMPL_FUNC
4131 if (thesaurus_func_complete(compl_type))
Girish Palyacbe53192025-04-14 22:13:15 +02004132 expand_by_function(compl_type, compl_pattern.string, NULL);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004133 else
4134#endif
4135 ins_compl_dictionaries(
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004136 dict != NULL ? dict
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004137 : (compl_type == CTRL_X_THESAURUS
4138 ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
4139 : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
John Marriott5e6ea922024-11-23 14:01:57 +01004140 compl_pattern.string,
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004141 dict != NULL ? dict_f : 0,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004142 compl_type == CTRL_X_THESAURUS);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004143}
4144
4145/*
4146 * Get the next set of tag names matching "compl_pattern".
4147 */
4148 static void
4149get_next_tag_completion(void)
4150{
4151 int save_p_ic;
4152 char_u **matches;
4153 int num_matches;
4154
4155 // set p_ic according to p_ic, p_scs and pat for find_tags().
4156 save_p_ic = p_ic;
John Marriott5e6ea922024-11-23 14:01:57 +01004157 p_ic = ignorecase(compl_pattern.string);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004158
4159 // Find up to TAG_MANY matches. Avoids that an enormous number
4160 // of matches is found when compl_pattern is empty
4161 g_tag_at_cursor = TRUE;
John Marriott5e6ea922024-11-23 14:01:57 +01004162 if (find_tags(compl_pattern.string, &num_matches, &matches,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004163 TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004164 | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004165 TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0)
4166 ins_compl_add_matches(num_matches, matches, p_ic);
4167 g_tag_at_cursor = FALSE;
4168 p_ic = save_p_ic;
4169}
4170
4171/*
glepnir8159fb12024-07-17 20:32:54 +02004172 * Compare function for qsort
4173 */
glepnirf31cfa22025-03-06 21:59:13 +01004174 static int
4175compare_scores(const void *a, const void *b)
glepnir8159fb12024-07-17 20:32:54 +02004176{
4177 int idx_a = *(const int *)a;
4178 int idx_b = *(const int *)b;
4179 int score_a = compl_fuzzy_scores[idx_a];
4180 int score_b = compl_fuzzy_scores[idx_b];
zeertzjq58d70522024-08-31 17:05:39 +02004181 return score_a == score_b ? (idx_a == idx_b ? 0 : (idx_a < idx_b ? -1 : 1))
4182 : (score_a > score_b ? -1 : 1);
glepnir8159fb12024-07-17 20:32:54 +02004183}
4184
4185/*
glepnirf31cfa22025-03-06 21:59:13 +01004186 * insert prefix with redraw
4187 */
4188 static void
4189ins_compl_longest_insert(char_u *prefix)
4190{
4191 ins_compl_delete();
4192 ins_compl_insert_bytes(prefix + get_compl_len(), -1);
4193 ins_redraw(FALSE);
4194}
4195
4196/*
4197 * Calculate the longest common prefix among the best fuzzy matches
4198 * stored in compl_best_matches, and insert it as the longest.
4199 */
4200 static void
4201fuzzy_longest_match(void)
4202{
4203 char_u *prefix = NULL;
4204 int prefix_len = 0;
4205 int i = 0;
4206 int j = 0;
4207 char_u *match_str = NULL;
4208 char_u *prefix_ptr = NULL;
4209 char_u *match_ptr = NULL;
4210 char_u *leader = NULL;
4211 size_t leader_len = 0;
4212 compl_T *compl = NULL;
4213 int more_candidates = FALSE;
4214 compl_T *nn_compl = NULL;
4215
4216 if (compl_num_bests == 0)
Hirohito Higashi355db992025-04-28 18:07:02 +02004217 return;
glepnirf31cfa22025-03-06 21:59:13 +01004218
4219 nn_compl = compl_first_match->cp_next->cp_next;
4220 if (nn_compl && nn_compl != compl_first_match)
4221 more_candidates = TRUE;
4222
4223 compl = ctrl_x_mode_whole_line() ? compl_first_match
4224 : compl_first_match->cp_next;
4225 if (compl_num_bests == 1)
4226 {
4227 // no more candidates insert the match str
4228 if (!more_candidates)
4229 {
4230 ins_compl_longest_insert(compl->cp_str.string);
4231 compl_num_bests = 0;
4232 }
4233 compl_num_bests = 0;
4234 return;
4235 }
4236
4237 compl_best_matches = (compl_T **)alloc(compl_num_bests * sizeof(compl_T *));
4238 if (compl_best_matches == NULL)
Hirohito Higashi355db992025-04-28 18:07:02 +02004239 return;
glepnirf31cfa22025-03-06 21:59:13 +01004240 while (compl != NULL && i < compl_num_bests)
4241 {
Hirohito Higashi355db992025-04-28 18:07:02 +02004242 compl_best_matches[i] = compl;
4243 compl = compl->cp_next;
4244 i++;
glepnirf31cfa22025-03-06 21:59:13 +01004245 }
4246
4247 prefix = compl_best_matches[0]->cp_str.string;
zeertzjq4422de62025-03-07 19:06:02 +01004248 prefix_len = (int)compl_best_matches[0]->cp_str.length;
glepnirf31cfa22025-03-06 21:59:13 +01004249
4250 for (i = 1; i < compl_num_bests; i++)
4251 {
4252 match_str = compl_best_matches[i]->cp_str.string;
Hirohito Higashi355db992025-04-28 18:07:02 +02004253 prefix_ptr = prefix;
4254 match_ptr = match_str;
4255 j = 0;
glepnirf31cfa22025-03-06 21:59:13 +01004256
4257 while (j < prefix_len && *match_ptr != NUL && *prefix_ptr != NUL)
4258 {
4259 if (STRNCMP(prefix_ptr, match_ptr, mb_ptr2len(prefix_ptr)) != 0)
4260 break;
4261
4262 MB_PTR_ADV(prefix_ptr);
4263 MB_PTR_ADV(match_ptr);
4264 j++;
4265 }
4266
4267 if (j > 0)
4268 prefix_len = j;
4269 }
4270
4271 leader = ins_compl_leader();
zeertzjq4422de62025-03-07 19:06:02 +01004272 leader_len = ins_compl_leader_len();
glepnirf31cfa22025-03-06 21:59:13 +01004273
4274 // skip non-consecutive prefixes
zeertzjq4422de62025-03-07 19:06:02 +01004275 if (leader_len > 0 && STRNCMP(prefix, leader, leader_len) != 0)
glepnirf31cfa22025-03-06 21:59:13 +01004276 goto end;
4277
zeertzjq4422de62025-03-07 19:06:02 +01004278 prefix = vim_strnsave(prefix, prefix_len);
glepnirf31cfa22025-03-06 21:59:13 +01004279 if (prefix != NULL)
4280 {
4281 ins_compl_longest_insert(prefix);
4282 compl_cfc_longest_ins = TRUE;
4283 vim_free(prefix);
4284 }
4285
4286end:
4287 vim_free(compl_best_matches);
4288 compl_best_matches = NULL;
4289 compl_num_bests = 0;
4290}
4291
4292/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004293 * Get the next set of filename matching "compl_pattern".
4294 */
4295 static void
4296get_next_filename_completion(void)
4297{
4298 char_u **matches;
4299 int num_matches;
glepnir8159fb12024-07-17 20:32:54 +02004300 char_u *ptr;
4301 garray_T fuzzy_indices;
4302 int i;
4303 int score;
4304 char_u *leader = ins_compl_leader();
John Marriott5e6ea922024-11-23 14:01:57 +01004305 size_t leader_len = ins_compl_leader_len();;
glepnirf31cfa22025-03-06 21:59:13 +01004306 int in_fuzzy_collect = (cfc_has_mode() && leader_len > 0);
glepnir8159fb12024-07-17 20:32:54 +02004307 int *fuzzy_indices_data;
glepnir0be03e12024-07-19 16:45:05 +02004308 char_u *last_sep = NULL;
glepnirf31cfa22025-03-06 21:59:13 +01004309 int need_collect_bests = in_fuzzy_collect && compl_get_longest;
4310 int max_score = 0;
4311 int current_score = 0;
4312 int dir = compl_direction;
glepnir8159fb12024-07-17 20:32:54 +02004313
glepnirb9de1a02024-08-02 19:14:38 +02004314#ifdef BACKSLASH_IN_FILENAME
4315 char pathsep = (curbuf->b_p_csl[0] == 's') ?
4316 '/' : (curbuf->b_p_csl[0] == 'b') ? '\\' : PATHSEP;
4317#else
4318 char pathsep = PATHSEP;
4319#endif
4320
glepnirf31cfa22025-03-06 21:59:13 +01004321 if (in_fuzzy_collect)
glepnir8159fb12024-07-17 20:32:54 +02004322 {
glepnirb9de1a02024-08-02 19:14:38 +02004323#ifdef BACKSLASH_IN_FILENAME
4324 if (curbuf->b_p_csl[0] == 's')
4325 {
John Marriott5e6ea922024-11-23 14:01:57 +01004326 for (i = 0; i < (int)leader_len; i++)
glepnirb9de1a02024-08-02 19:14:38 +02004327 {
4328 if (leader[i] == '\\')
4329 leader[i] = '/';
4330 }
4331 }
4332 else if (curbuf->b_p_csl[0] == 'b')
4333 {
John Marriott5e6ea922024-11-23 14:01:57 +01004334 for (i = 0; i < (int)leader_len; i++)
glepnirb9de1a02024-08-02 19:14:38 +02004335 {
4336 if (leader[i] == '/')
4337 leader[i] = '\\';
4338 }
4339 }
4340#endif
4341 last_sep = vim_strrchr(leader, pathsep);
glepnir0be03e12024-07-19 16:45:05 +02004342 if (last_sep == NULL)
4343 {
4344 // No path separator or separator is the last character,
4345 // fuzzy match the whole leader
John Marriott5e6ea922024-11-23 14:01:57 +01004346 VIM_CLEAR_STRING(compl_pattern);
4347 compl_pattern.string = vim_strnsave((char_u *)"*", 1);
4348 if (compl_pattern.string == NULL)
4349 return;
4350 compl_pattern.length = 1;
glepnir0be03e12024-07-19 16:45:05 +02004351 }
4352 else if (*(last_sep + 1) == '\0')
glepnirf31cfa22025-03-06 21:59:13 +01004353 in_fuzzy_collect = FALSE;
glepnir0be03e12024-07-19 16:45:05 +02004354 else
4355 {
4356 // Split leader into path and file parts
4357 int path_len = last_sep - leader + 1;
John Marriott5e6ea922024-11-23 14:01:57 +01004358 char_u *path_with_wildcard;
4359
4360 path_with_wildcard = alloc(path_len + 2);
glepnir0be03e12024-07-19 16:45:05 +02004361 if (path_with_wildcard != NULL)
4362 {
John Marriott5e6ea922024-11-23 14:01:57 +01004363 vim_snprintf((char *)path_with_wildcard, path_len + 2, "%*.*s*", path_len, path_len, leader);
4364 VIM_CLEAR_STRING(compl_pattern);
4365 compl_pattern.string = path_with_wildcard;
4366 compl_pattern.length = path_len + 1;
glepnir0be03e12024-07-19 16:45:05 +02004367
4368 // Move leader to the file part
4369 leader = last_sep + 1;
John Marriott5e6ea922024-11-23 14:01:57 +01004370 leader_len -= path_len;
glepnir0be03e12024-07-19 16:45:05 +02004371 }
4372 }
glepnir8159fb12024-07-17 20:32:54 +02004373 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004374
John Marriott5e6ea922024-11-23 14:01:57 +01004375 if (expand_wildcards(1, &compl_pattern.string, &num_matches, &matches,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004376 EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK)
4377 return;
4378
4379 // May change home directory back to "~".
John Marriott5e6ea922024-11-23 14:01:57 +01004380 tilde_replace(compl_pattern.string, num_matches, matches);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004381#ifdef BACKSLASH_IN_FILENAME
4382 if (curbuf->b_p_csl[0] != NUL)
4383 {
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004384 for (i = 0; i < num_matches; ++i)
4385 {
glepnir8159fb12024-07-17 20:32:54 +02004386 ptr = matches[i];
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004387 while (*ptr != NUL)
4388 {
4389 if (curbuf->b_p_csl[0] == 's' && *ptr == '\\')
4390 *ptr = '/';
4391 else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/')
4392 *ptr = '\\';
4393 ptr += (*mb_ptr2len)(ptr);
4394 }
4395 }
4396 }
4397#endif
glepnir8159fb12024-07-17 20:32:54 +02004398
glepnirf31cfa22025-03-06 21:59:13 +01004399 if (in_fuzzy_collect)
glepnir8159fb12024-07-17 20:32:54 +02004400 {
4401 ga_init2(&fuzzy_indices, sizeof(int), 10);
4402 compl_fuzzy_scores = (int *)alloc(sizeof(int) * num_matches);
4403
4404 for (i = 0; i < num_matches; i++)
4405 {
4406 ptr = matches[i];
4407 score = fuzzy_match_str(ptr, leader);
4408 if (score > 0)
4409 {
4410 if (ga_grow(&fuzzy_indices, 1) == OK)
4411 {
4412 ((int *)fuzzy_indices.ga_data)[fuzzy_indices.ga_len] = i;
4413 compl_fuzzy_scores[i] = score;
4414 fuzzy_indices.ga_len++;
4415 }
4416 }
4417 }
4418
Christian Brabandt220474d2024-07-20 13:26:44 +02004419 // prevent qsort from deref NULL pointer
4420 if (fuzzy_indices.ga_len > 0)
4421 {
glepnirf31cfa22025-03-06 21:59:13 +01004422 char_u *match = NULL;
Christian Brabandt220474d2024-07-20 13:26:44 +02004423 fuzzy_indices_data = (int *)fuzzy_indices.ga_data;
4424 qsort(fuzzy_indices_data, fuzzy_indices.ga_len, sizeof(int), compare_scores);
glepnir8159fb12024-07-17 20:32:54 +02004425
Christian Brabandt220474d2024-07-20 13:26:44 +02004426 for (i = 0; i < fuzzy_indices.ga_len; ++i)
glepnirf31cfa22025-03-06 21:59:13 +01004427 {
Hirohito Higashi355db992025-04-28 18:07:02 +02004428 match = matches[fuzzy_indices_data[i]];
glepnirf31cfa22025-03-06 21:59:13 +01004429 current_score = compl_fuzzy_scores[fuzzy_indices_data[i]];
4430 if (ins_compl_add(match, -1, NULL, NULL, NULL, dir,
Hirohito Higashi355db992025-04-28 18:07:02 +02004431 CP_FAST | ((p_fic || p_wic) ? CP_ICASE : 0),
4432 FALSE, NULL, current_score) == OK)
glepnirf31cfa22025-03-06 21:59:13 +01004433 dir = FORWARD;
4434
4435 if (need_collect_bests)
4436 {
4437 if (i == 0 || current_score == max_score)
4438 {
4439 compl_num_bests++;
4440 max_score = current_score;
4441 }
4442 }
4443 }
glepnir8159fb12024-07-17 20:32:54 +02004444
Christian Brabandt220474d2024-07-20 13:26:44 +02004445 FreeWild(num_matches, matches);
Christian Brabandt220474d2024-07-20 13:26:44 +02004446 }
glepnir6b6280c2024-07-27 16:25:45 +02004447 else if (leader_len > 0)
4448 {
4449 FreeWild(num_matches, matches);
4450 num_matches = 0;
4451 }
Christian Brabandt220474d2024-07-20 13:26:44 +02004452
glepnir8159fb12024-07-17 20:32:54 +02004453 vim_free(compl_fuzzy_scores);
4454 ga_clear(&fuzzy_indices);
glepnirf31cfa22025-03-06 21:59:13 +01004455
4456 if (compl_num_bests > 0 && compl_get_longest)
4457 fuzzy_longest_match();
4458 return;
glepnir8159fb12024-07-17 20:32:54 +02004459 }
4460
glepnir6b6280c2024-07-27 16:25:45 +02004461 if (num_matches > 0)
4462 ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004463}
4464
4465/*
4466 * Get the next set of command-line completions matching "compl_pattern".
4467 */
4468 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00004469get_next_cmdline_completion(void)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004470{
4471 char_u **matches;
4472 int num_matches;
4473
John Marriott5e6ea922024-11-23 14:01:57 +01004474 if (expand_cmdline(&compl_xp, compl_pattern.string,
4475 (int)compl_pattern.length, &num_matches, &matches) == EXPAND_OK)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004476 ins_compl_add_matches(num_matches, matches, FALSE);
4477}
4478
4479/*
4480 * Get the next set of spell suggestions matching "compl_pattern".
4481 */
4482 static void
4483get_next_spell_completion(linenr_T lnum UNUSED)
4484{
4485#ifdef FEAT_SPELL
4486 char_u **matches;
4487 int num_matches;
4488
John Marriott5e6ea922024-11-23 14:01:57 +01004489 num_matches = expand_spelling(lnum, compl_pattern.string, &matches);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004490 if (num_matches > 0)
4491 ins_compl_add_matches(num_matches, matches, p_ic);
Bram Moolenaar8e7cc6b2021-12-30 10:32:25 +00004492 else
4493 vim_free(matches);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004494#endif
4495}
4496
4497/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004498 * Return the next word or line from buffer "ins_buf" at position
4499 * "cur_match_pos" for completion. The length of the match is set in "len".
4500 */
4501 static char_u *
zeertzjq440d4cb2023-03-02 17:51:32 +00004502ins_compl_get_next_word_or_line(
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004503 buf_T *ins_buf, // buffer being scanned
4504 pos_T *cur_match_pos, // current match position
4505 int *match_len,
4506 int *cont_s_ipos) // next ^X<> will set initial_pos
4507{
4508 char_u *ptr;
4509 int len;
4510
4511 *match_len = 0;
4512 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) +
4513 cur_match_pos->col;
John Marriott5e6ea922024-11-23 14:01:57 +01004514 len = (int)ml_get_buf_len(ins_buf, cur_match_pos->lnum) - cur_match_pos->col;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004515 if (ctrl_x_mode_line_or_eval())
4516 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004517 if (compl_status_adding())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004518 {
4519 if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count)
4520 return NULL;
4521 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
John Marriott5e6ea922024-11-23 14:01:57 +01004522 len = ml_get_buf_len(ins_buf, cur_match_pos->lnum + 1);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004523 if (!p_paste)
John Marriott5e6ea922024-11-23 14:01:57 +01004524 {
4525 char_u *tmp_ptr = ptr;
4526
4527 ptr = skipwhite(tmp_ptr);
4528 len -= (int)(ptr - tmp_ptr);
4529 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004530 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004531 }
4532 else
4533 {
4534 char_u *tmp_ptr = ptr;
4535
John Marriott5e6ea922024-11-23 14:01:57 +01004536 if (compl_status_adding() && compl_length <= len)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004537 {
4538 tmp_ptr += compl_length;
4539 // Skip if already inside a word.
4540 if (vim_iswordp(tmp_ptr))
4541 return NULL;
4542 // Find start of next word.
4543 tmp_ptr = find_word_start(tmp_ptr);
4544 }
4545 // Find end of this word.
4546 tmp_ptr = find_word_end(tmp_ptr);
4547 len = (int)(tmp_ptr - ptr);
4548
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004549 if (compl_status_adding() && len == compl_length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004550 {
4551 if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count)
4552 {
4553 // Try next line, if any. the new word will be
4554 // "join" as if the normal command "J" was used.
4555 // IOSIZE is always greater than
4556 // compl_length, so the next STRNCPY always
4557 // works -- Acevedo
4558 STRNCPY(IObuff, ptr, len);
4559 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
4560 tmp_ptr = ptr = skipwhite(ptr);
4561 // Find start of next word.
4562 tmp_ptr = find_word_start(tmp_ptr);
4563 // Find end of next word.
4564 tmp_ptr = find_word_end(tmp_ptr);
4565 if (tmp_ptr > ptr)
4566 {
4567 if (*ptr != ')' && IObuff[len - 1] != TAB)
4568 {
4569 if (IObuff[len - 1] != ' ')
4570 IObuff[len++] = ' ';
4571 // IObuf =~ "\k.* ", thus len >= 2
4572 if (p_js
4573 && (IObuff[len - 2] == '.'
4574 || (vim_strchr(p_cpo, CPO_JOINSP)
4575 == NULL
4576 && (IObuff[len - 2] == '?'
4577 || IObuff[len - 2] == '!'))))
4578 IObuff[len++] = ' ';
4579 }
4580 // copy as much as possible of the new word
4581 if (tmp_ptr - ptr >= IOSIZE - len)
4582 tmp_ptr = ptr + IOSIZE - len - 1;
4583 STRNCPY(IObuff + len, ptr, tmp_ptr - ptr);
4584 len += (int)(tmp_ptr - ptr);
4585 *cont_s_ipos = TRUE;
4586 }
4587 IObuff[len] = NUL;
4588 ptr = IObuff;
4589 }
4590 if (len == compl_length)
4591 return NULL;
4592 }
4593 }
4594
4595 *match_len = len;
4596 return ptr;
4597}
4598
4599/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004600 * Get the next set of words matching "compl_pattern" for default completion(s)
4601 * (normal ^P/^N and ^X^L).
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004602 * Search for "compl_pattern" in the buffer "st->ins_buf" starting from the
4603 * position "st->start_pos" in the "compl_direction" direction. If
4604 * "st->set_match_pos" is TRUE, then set the "st->first_match_pos" and
4605 * "st->last_match_pos".
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004606 * Returns OK if a new next match is found, otherwise returns FAIL.
4607 */
4608 static int
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004609get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004610{
4611 int found_new_match = FAIL;
4612 int save_p_scs;
4613 int save_p_ws;
4614 int looped_around = FALSE;
glepnir8159fb12024-07-17 20:32:54 +02004615 char_u *ptr = NULL;
4616 int len = 0;
glepnirf31cfa22025-03-06 21:59:13 +01004617 int in_collect = (cfc_has_mode() && compl_length > 0);
glepnir8159fb12024-07-17 20:32:54 +02004618 char_u *leader = ins_compl_leader();
glepnirf31cfa22025-03-06 21:59:13 +01004619 int score = 0;
Girish Palyab1565882025-04-15 20:16:00 +02004620 int in_curbuf = st->ins_buf == curbuf;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004621
4622 // If 'infercase' is set, don't use 'smartcase' here
4623 save_p_scs = p_scs;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004624 if (st->ins_buf->b_p_inf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004625 p_scs = FALSE;
4626
4627 // Buffers other than curbuf are scanned from the beginning or the
4628 // end but never from the middle, thus setting nowrapscan in this
4629 // buffer is a good idea, on the other hand, we always set
4630 // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
4631 save_p_ws = p_ws;
Girish Palyab1565882025-04-15 20:16:00 +02004632 if (!in_curbuf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004633 p_ws = FALSE;
glepnirf31cfa22025-03-06 21:59:13 +01004634 else if (*st->e_cpt == '.')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004635 p_ws = TRUE;
4636 looped_around = FALSE;
4637 for (;;)
4638 {
4639 int cont_s_ipos = FALSE;
4640
4641 ++msg_silent; // Don't want messages for wrapscan.
4642
glepnirf31cfa22025-03-06 21:59:13 +01004643 if (in_collect)
4644 {
Hirohito Higashi355db992025-04-28 18:07:02 +02004645 found_new_match = search_for_fuzzy_match(st->ins_buf,
glepnir8159fb12024-07-17 20:32:54 +02004646 st->cur_match_pos, leader, compl_direction,
glepnirf31cfa22025-03-06 21:59:13 +01004647 start_pos, &len, &ptr, &score);
4648 }
4649 // ctrl_x_mode_line_or_eval() || word-wise search that
4650 // has added a word that was at the beginning of the line
4651 else if (ctrl_x_mode_whole_line() || ctrl_x_mode_eval() || (compl_cont_status & CONT_SOL))
4652 found_new_match = search_for_exact_line(st->ins_buf,
4653 st->cur_match_pos, compl_direction, compl_pattern.string);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004654 else
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004655 found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
John Marriott5e6ea922024-11-23 14:01:57 +01004656 NULL, compl_direction, compl_pattern.string, (int)compl_pattern.length,
John Marriott8c85a2a2024-05-20 19:18:26 +02004657 1L, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004658 --msg_silent;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004659 if (!compl_started || st->set_match_pos)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004660 {
4661 // set "compl_started" even on fail
4662 compl_started = TRUE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004663 st->first_match_pos = *st->cur_match_pos;
4664 st->last_match_pos = *st->cur_match_pos;
4665 st->set_match_pos = FALSE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004666 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004667 else if (st->first_match_pos.lnum == st->last_match_pos.lnum
4668 && st->first_match_pos.col == st->last_match_pos.col)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004669 {
4670 found_new_match = FAIL;
4671 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004672 else if (compl_dir_forward()
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004673 && (st->prev_match_pos.lnum > st->cur_match_pos->lnum
4674 || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
4675 && st->prev_match_pos.col >= st->cur_match_pos->col)))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004676 {
4677 if (looped_around)
4678 found_new_match = FAIL;
4679 else
4680 looped_around = TRUE;
4681 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004682 else if (!compl_dir_forward()
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004683 && (st->prev_match_pos.lnum < st->cur_match_pos->lnum
4684 || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
4685 && st->prev_match_pos.col <= st->cur_match_pos->col)))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004686 {
4687 if (looped_around)
4688 found_new_match = FAIL;
4689 else
4690 looped_around = TRUE;
4691 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004692 st->prev_match_pos = *st->cur_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004693 if (found_new_match == FAIL)
4694 break;
4695
4696 // when ADDING, the text before the cursor matches, skip it
Girish Palyab1565882025-04-15 20:16:00 +02004697 if (compl_status_adding() && in_curbuf
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004698 && start_pos->lnum == st->cur_match_pos->lnum
4699 && start_pos->col == st->cur_match_pos->col)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004700 continue;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004701
glepnirf31cfa22025-03-06 21:59:13 +01004702 if (!in_collect)
glepnir8159fb12024-07-17 20:32:54 +02004703 ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
4704 &len, &cont_s_ipos);
glepnirbfb4eea2025-01-31 15:28:29 +01004705 if (ptr == NULL || (ins_compl_has_preinsert() && STRCMP(ptr, compl_pattern.string) == 0))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004706 continue;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004707
Girish Palyab1565882025-04-15 20:16:00 +02004708 if (is_nearest_active() && in_curbuf)
4709 {
4710 score = st->cur_match_pos->lnum - curwin->w_cursor.lnum;
4711 if (score < 0)
4712 score = -score;
4713 score++;
4714 }
4715
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004716 if (ins_compl_add_infercase(ptr, len, p_ic,
Girish Palyab1565882025-04-15 20:16:00 +02004717 in_curbuf ? NULL : st->ins_buf->b_sfname,
glepnirf31cfa22025-03-06 21:59:13 +01004718 0, cont_s_ipos, score) != NOTDONE)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004719 {
glepnirf31cfa22025-03-06 21:59:13 +01004720 if (in_collect && score == compl_first_match->cp_next->cp_score)
4721 compl_num_bests++;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004722 found_new_match = OK;
4723 break;
4724 }
4725 }
4726 p_scs = save_p_scs;
4727 p_ws = save_p_ws;
4728
4729 return found_new_match;
4730}
4731
4732/*
Girish Palyacbe53192025-04-14 22:13:15 +02004733 * Return the callback function associated with "funcname".
4734 */
4735#ifdef FEAT_COMPL_FUNC
4736 static callback_T *
4737get_cpt_func_callback(char_u *funcname)
4738{
4739 static callback_T cb;
4740 char_u buf[LSIZE];
4741 int slen;
4742
4743 slen = copy_option_part(&funcname, buf, LSIZE, ",");
4744 if (slen > 0 && option_set_callback_func(buf, &cb))
4745 return &cb;
4746 return NULL;
4747}
4748
4749/*
4750 * Retrieve new completion matches by invoking callback "cb".
4751 */
4752 static void
4753expand_cpt_function(callback_T *cb)
4754{
4755 // Re-insert the text removed by ins_compl_delete().
4756 ins_compl_insert_bytes(compl_orig_text.string + get_compl_len(), -1);
4757 // Get matches
4758 get_cpt_func_completion_matches(cb);
4759 // Undo insertion
4760 ins_compl_delete();
4761}
4762#endif
4763
4764/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004765 * get the next set of completion matches for "type".
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004766 * Returns TRUE if a new match is found. Otherwise returns FALSE.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004767 */
4768 static int
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004769get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004770{
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004771 int found_new_match = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004772
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004773 switch (type)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004774 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004775 case -1:
4776 break;
4777#ifdef FEAT_FIND_ID
4778 case CTRL_X_PATH_PATTERNS:
4779 case CTRL_X_PATH_DEFINES:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004780 get_next_include_file_completion(type);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004781 break;
4782#endif
4783
4784 case CTRL_X_DICTIONARY:
4785 case CTRL_X_THESAURUS:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004786 get_next_dict_tsr_completion(type, st->dict, st->dict_f);
4787 st->dict = NULL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004788 break;
4789
4790 case CTRL_X_TAGS:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004791 get_next_tag_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004792 break;
4793
4794 case CTRL_X_FILES:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004795 get_next_filename_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004796 break;
4797
4798 case CTRL_X_CMDLINE:
zeertzjqdca29d92021-08-31 19:12:51 +02004799 case CTRL_X_CMDLINE_CTRL_X:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00004800 get_next_cmdline_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004801 break;
4802
4803#ifdef FEAT_COMPL_FUNC
4804 case CTRL_X_FUNCTION:
Girish Palyacbe53192025-04-14 22:13:15 +02004805 if (ctrl_x_mode_normal()) // Invoked by a func in 'cpt' option
4806 expand_cpt_function(st->func_cb);
4807 else
4808 expand_by_function(type, compl_pattern.string, NULL);
4809 break;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004810 case CTRL_X_OMNI:
Girish Palyacbe53192025-04-14 22:13:15 +02004811 expand_by_function(type, compl_pattern.string, NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004812 break;
4813#endif
4814
4815 case CTRL_X_SPELL:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004816 get_next_spell_completion(st->first_match_pos.lnum);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004817 break;
4818
4819 default: // normal ^P/^N and ^X^L
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004820 found_new_match = get_next_default_completion(st, ini);
4821 if (found_new_match == FAIL && st->ins_buf == curbuf)
4822 st->found_all = TRUE;
4823 }
4824
4825 // check if compl_curr_match has changed, (e.g. other type of
4826 // expansion added something)
4827 if (type != 0 && compl_curr_match != compl_old_match)
4828 found_new_match = OK;
4829
4830 return found_new_match;
4831}
4832
4833/*
Girish Palya0ac1eb32025-04-16 20:18:33 +02004834 * Strips carets followed by numbers. This suffix typically represents the
4835 * max_matches setting.
4836 */
4837 static void
4838strip_caret_numbers_in_place(char_u *str)
4839{
4840 char_u *read = str, *write = str, *p;
4841
4842 if (str == NULL)
4843 return;
4844
4845 while (*read)
4846 {
4847 if (*read == '^')
4848 {
4849 p = read + 1;
4850 while (vim_isdigit(*p))
4851 p++;
4852 if ((*p == ',' || *p == '\0') && p != read + 1)
4853 {
4854 read = p;
4855 continue;
4856 }
4857 else
4858 *write++ = *read++;
4859 }
4860 else
4861 *write++ = *read++;
4862 }
4863 *write = '\0';
4864}
4865
4866/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004867 * Get the next expansion(s), using "compl_pattern".
4868 * The search starts at position "ini" in curbuf and in the direction
4869 * compl_direction.
4870 * When "compl_started" is FALSE start at that position, otherwise continue
4871 * where we stopped searching before.
4872 * This may return before finding all the matches.
4873 * Return the total number of matches or -1 if still unknown -- Acevedo
4874 */
4875 static int
4876ins_compl_get_exp(pos_T *ini)
4877{
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004878 static ins_compl_next_state_T st;
4879 static int st_cleared = FALSE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004880 int i;
4881 int found_new_match;
4882 int type = ctrl_x_mode;
4883
4884 if (!compl_started)
4885 {
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004886 buf_T *buf;
4887
4888 FOR_ALL_BUFFERS(buf)
4889 buf->b_scanned = 0;
4890 if (!st_cleared)
4891 {
4892 CLEAR_FIELD(st);
4893 st_cleared = TRUE;
4894 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004895 st.found_all = FALSE;
4896 st.ins_buf = curbuf;
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004897 vim_free(st.e_cpt_copy);
Christian Brabandtee17b6f2023-09-09 11:23:50 +02004898 // Make a copy of 'complete', in case the buffer is wiped out.
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004899 st.e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL)
4900 ? (char_u *)"." : curbuf->b_p_cpt);
Girish Palya0ac1eb32025-04-16 20:18:33 +02004901 strip_caret_numbers_in_place(st.e_cpt_copy);
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004902 st.e_cpt = st.e_cpt_copy == NULL ? (char_u *)"" : st.e_cpt_copy;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004903 st.last_match_pos = st.first_match_pos = *ini;
Girish Palyacbe53192025-04-14 22:13:15 +02004904
4905 if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
Girish Palya0ac1eb32025-04-16 20:18:33 +02004906 && !cpt_sources_init())
Girish Palyacbe53192025-04-14 22:13:15 +02004907 return FAIL;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004908 }
4909 else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
4910 st.ins_buf = curbuf; // In case the buffer was wiped out.
4911
4912 compl_old_match = compl_curr_match; // remember the last current match
Christian Brabandt13032a42024-07-28 21:16:48 +02004913 st.cur_match_pos = (compl_dir_forward())
glepnir8159fb12024-07-17 20:32:54 +02004914 ? &st.last_match_pos : &st.first_match_pos;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004915
4916 // For ^N/^P loop over all the flags/windows/buffers in 'complete'.
Girish Palya0ac1eb32025-04-16 20:18:33 +02004917 for (cpt_sources_index = 0;;)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004918 {
4919 found_new_match = FAIL;
4920 st.set_match_pos = FALSE;
4921
4922 // For ^N/^P pick a new entry from e_cpt if compl_started is off,
4923 // or if found_all says this entry is done. For ^X^L only use the
4924 // entries from 'complete' that look in loaded buffers.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004925 if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004926 && (!compl_started || st.found_all))
4927 {
glepnir53b14572025-03-12 21:28:39 +01004928 int status = process_next_cpt_value(&st, &type, ini, cfc_has_mode());
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004929
4930 if (status == INS_COMPL_CPT_END)
4931 break;
4932 if (status == INS_COMPL_CPT_CONT)
Girish Palyacbe53192025-04-14 22:13:15 +02004933 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02004934 cpt_sources_index++;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004935 continue;
Girish Palyacbe53192025-04-14 22:13:15 +02004936 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004937 }
4938
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004939 // If complete() was called then compl_pattern has been reset. The
4940 // following won't work then, bail out.
John Marriott5e6ea922024-11-23 14:01:57 +01004941 if (compl_pattern.string == NULL)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004942 break;
4943
4944 // get the next set of completion matches
4945 found_new_match = get_next_completion_match(type, &st, ini);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004946
Girish Palyacbe53192025-04-14 22:13:15 +02004947 if (type > 0)
Girish Palya0ac1eb32025-04-16 20:18:33 +02004948 cpt_sources_index++;
Girish Palyacbe53192025-04-14 22:13:15 +02004949
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004950 // break the loop for specialized modes (use 'complete' just for the
4951 // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new
4952 // match
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004953 if ((ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())
4954 || found_new_match != FAIL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004955 {
4956 if (got_int)
4957 break;
4958 // Fill the popup menu as soon as possible.
4959 if (type != -1)
4960 ins_compl_check_keys(0, FALSE);
4961
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004962 if ((ctrl_x_mode_not_default()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004963 && !ctrl_x_mode_line_or_eval()) || compl_interrupted)
4964 break;
4965 compl_started = TRUE;
4966 }
4967 else
4968 {
4969 // Mark a buffer scanned when it has been scanned completely
Christian Brabandtee9166e2023-09-03 21:24:33 +02004970 if (buf_valid(st.ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004971 st.ins_buf->b_scanned = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004972
4973 compl_started = FALSE;
4974 }
4975 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02004976 cpt_sources_index = -1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004977 compl_started = TRUE;
4978
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004979 if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004980 && *st.e_cpt == NUL) // Got to end of 'complete'
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004981 found_new_match = FAIL;
4982
4983 i = -1; // total of matches, unknown
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004984 if (found_new_match == FAIL || (ctrl_x_mode_not_default()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004985 && !ctrl_x_mode_line_or_eval()))
4986 i = ins_compl_make_cyclic();
4987
glepnirf31cfa22025-03-06 21:59:13 +01004988 if (cfc_has_mode() && compl_get_longest && compl_num_bests > 0)
4989 fuzzy_longest_match();
4990
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004991 if (compl_old_match != NULL)
4992 {
4993 // If several matches were added (FORWARD) or the search failed and has
4994 // just been made cyclic then we have to move compl_curr_match to the
4995 // next or previous entry (if any) -- Acevedo
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004996 compl_curr_match = compl_dir_forward() ? compl_old_match->cp_next
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004997 : compl_old_match->cp_prev;
4998 if (compl_curr_match == NULL)
4999 compl_curr_match = compl_old_match;
5000 }
LemonBoy2bf52dd2022-04-09 18:17:34 +01005001 may_trigger_modechanged();
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01005002
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005003 return i;
5004}
5005
5006/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005007 * Update "compl_shown_match" to the actually shown match, it may differ when
5008 * "compl_leader" is used to omit some of the matches.
5009 */
5010 static void
5011ins_compl_update_shown_match(void)
5012{
5013 while (!ins_compl_equal(compl_shown_match,
John Marriott5e6ea922024-11-23 14:01:57 +01005014 compl_leader.string, (int)compl_leader.length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005015 && compl_shown_match->cp_next != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005016 && !is_first_match(compl_shown_match->cp_next))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005017 compl_shown_match = compl_shown_match->cp_next;
5018
5019 // If we didn't find it searching forward, and compl_shows_dir is
5020 // backward, find the last match.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005021 if (compl_shows_dir_backward()
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005022 && !ins_compl_equal(compl_shown_match,
John Marriott5e6ea922024-11-23 14:01:57 +01005023 compl_leader.string, (int)compl_leader.length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005024 && (compl_shown_match->cp_next == NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005025 || is_first_match(compl_shown_match->cp_next)))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005026 {
5027 while (!ins_compl_equal(compl_shown_match,
John Marriott5e6ea922024-11-23 14:01:57 +01005028 compl_leader.string, (int)compl_leader.length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005029 && compl_shown_match->cp_prev != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005030 && !is_first_match(compl_shown_match->cp_prev))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005031 compl_shown_match = compl_shown_match->cp_prev;
5032 }
5033}
5034
5035/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005036 * Delete the old text being completed.
5037 */
5038 void
5039ins_compl_delete(void)
5040{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005041 // In insert mode: Delete the typed part.
5042 // In replace mode: Put the old characters back, if any.
glepniredd4ac32025-01-29 18:53:51 +01005043 int col = compl_col + (compl_status_adding() ? compl_length : 0);
John Marriottf4b36412025-02-23 09:09:59 +01005044 string_T remaining = {NULL, 0};
glepnir76bdb822025-02-08 19:04:51 +01005045 int orig_col;
glepniredd4ac32025-01-29 18:53:51 +01005046 int has_preinsert = ins_compl_preinsert_effect();
5047 if (has_preinsert)
5048 {
Yegappan Lakshmanan7b6add02025-04-01 20:38:37 +02005049 col += (int)ins_compl_leader_len();
glepniredd4ac32025-01-29 18:53:51 +01005050 curwin->w_cursor.col = compl_ins_end_col;
5051 }
5052
glepnir76bdb822025-02-08 19:04:51 +01005053 if (curwin->w_cursor.lnum > compl_lnum)
5054 {
5055 if (curwin->w_cursor.col < ml_get_curline_len())
5056 {
John Marriottf4b36412025-02-23 09:09:59 +01005057 char_u *line = ml_get_cursor();
5058 remaining.length = ml_get_cursor_len();
5059 remaining.string = vim_strnsave(line, remaining.length);
5060 if (remaining.string == NULL)
glepnir76bdb822025-02-08 19:04:51 +01005061 return;
5062 }
5063 while (curwin->w_cursor.lnum > compl_lnum)
5064 {
5065 if (ml_delete(curwin->w_cursor.lnum) == FAIL)
5066 {
John Marriottf4b36412025-02-23 09:09:59 +01005067 if (remaining.string)
5068 vim_free(remaining.string);
glepnir76bdb822025-02-08 19:04:51 +01005069 return;
5070 }
zeertzjq060e6552025-02-21 20:06:26 +01005071 deleted_lines_mark(curwin->w_cursor.lnum, 1L);
glepnir76bdb822025-02-08 19:04:51 +01005072 curwin->w_cursor.lnum--;
5073 }
5074 // move cursor to end of line
5075 curwin->w_cursor.col = ml_get_curline_len();
5076 }
5077
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005078 if ((int)curwin->w_cursor.col > col)
5079 {
5080 if (stop_arrow() == FAIL)
glepnire3647c82025-02-10 21:16:32 +01005081 {
John Marriottf4b36412025-02-23 09:09:59 +01005082 if (remaining.string)
5083 vim_free(remaining.string);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005084 return;
glepnire3647c82025-02-10 21:16:32 +01005085 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005086 backspace_until_column(col);
zeertzjqf25d8f92024-12-18 21:12:25 +01005087 compl_ins_end_col = curwin->w_cursor.col;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005088 }
5089
John Marriottf4b36412025-02-23 09:09:59 +01005090 if (remaining.string != NULL)
glepnir76bdb822025-02-08 19:04:51 +01005091 {
5092 orig_col = curwin->w_cursor.col;
John Marriottf4b36412025-02-23 09:09:59 +01005093 ins_str(remaining.string, remaining.length);
glepnir76bdb822025-02-08 19:04:51 +01005094 curwin->w_cursor.col = orig_col;
John Marriottf4b36412025-02-23 09:09:59 +01005095 vim_free(remaining.string);
glepnir76bdb822025-02-08 19:04:51 +01005096 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005097 // TODO: is this sufficient for redrawing? Redrawing everything causes
5098 // flicker, thus we can't do that.
5099 changed_cline_bef_curs();
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02005100#ifdef FEAT_EVAL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005101 // clear v:completed_item
5102 set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02005103#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005104}
5105
5106/*
glepnir76bdb822025-02-08 19:04:51 +01005107 * Insert a completion string that contains newlines.
5108 * The string is split and inserted line by line.
5109 */
5110 static void
5111ins_compl_expand_multiple(char_u *str)
5112{
5113 char_u *start = str;
5114 char_u *curr = str;
glepnir5090a1f2025-02-24 19:10:37 +01005115 int base_indent = get_indent();
glepnir76bdb822025-02-08 19:04:51 +01005116
5117 while (*curr != NUL)
5118 {
5119 if (*curr == '\n')
5120 {
5121 // Insert the text chunk before newline
5122 if (curr > start)
5123 ins_char_bytes(start, (int)(curr - start));
5124
5125 // Handle newline
glepnir5090a1f2025-02-24 19:10:37 +01005126 open_line(FORWARD, OPENLINE_KEEPTRAIL | OPENLINE_FORCE_INDENT, base_indent, NULL);
glepnir76bdb822025-02-08 19:04:51 +01005127 start = curr + 1;
5128 }
5129 curr++;
5130 }
5131
5132 // Handle remaining text after last newline (if any)
5133 if (curr > start)
5134 ins_char_bytes(start, (int)(curr - start));
5135
5136 compl_ins_end_col = curwin->w_cursor.col;
5137}
5138
5139/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005140 * Insert the new text being completed.
5141 * "in_compl_func" is TRUE when called from complete_check().
glepniredd4ac32025-01-29 18:53:51 +01005142 * "move_cursor" is used when 'completeopt' includes "preinsert" and when TRUE
5143 * cursor needs to move back from the inserted text to the compl_leader.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005144 */
5145 void
glepniredd4ac32025-01-29 18:53:51 +01005146ins_compl_insert(int in_compl_func, int move_cursor)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005147{
glepnir76bdb822025-02-08 19:04:51 +01005148 int compl_len = get_compl_len();
5149 int preinsert = ins_compl_has_preinsert();
5150 char_u *cp_str = compl_shown_match->cp_str.string;
5151 size_t cp_str_len = compl_shown_match->cp_str.length;
5152 size_t leader_len = ins_compl_leader_len();
5153 char_u *has_multiple = vim_strchr(cp_str, '\n');
Bram Moolenaar4b28ba32021-12-27 19:28:37 +00005154
5155 // Make sure we don't go over the end of the string, this can happen with
5156 // illegal bytes.
zeertzjq8297e2c2025-01-30 11:04:47 +01005157 if (compl_len < (int)cp_str_len)
glepniredd4ac32025-01-29 18:53:51 +01005158 {
glepnir76bdb822025-02-08 19:04:51 +01005159 if (has_multiple)
5160 ins_compl_expand_multiple(cp_str + compl_len);
5161 else
5162 {
5163 ins_compl_insert_bytes(cp_str + compl_len, -1);
5164 if (preinsert && move_cursor)
5165 curwin->w_cursor.col -= (colnr_T)(cp_str_len - leader_len);
5166 }
glepniredd4ac32025-01-29 18:53:51 +01005167 }
5168 if (match_at_original_text(compl_shown_match) || preinsert)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005169 compl_used_match = FALSE;
5170 else
5171 compl_used_match = TRUE;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02005172#ifdef FEAT_EVAL
5173 {
5174 dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
5175
5176 set_vim_var_dict(VV_COMPLETED_ITEM, dict);
5177 }
5178#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005179 if (!in_compl_func)
5180 compl_curr_match = compl_shown_match;
5181}
5182
5183/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005184 * show the file name for the completion match (if any). Truncate the file
5185 * name to avoid a wait for return.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005186 */
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005187 static void
5188ins_compl_show_filename(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005189{
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005190 char *lead = _("match in file");
5191 int space = sc_col - vim_strsize((char_u *)lead) - 2;
5192 char_u *s;
5193 char_u *e;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005194
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005195 if (space <= 0)
5196 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005197
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005198 // We need the tail that fits. With double-byte encoding going
5199 // back from the end is very slow, thus go from the start and keep
5200 // the text that fits in "space" between "s" and "e".
5201 for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005202 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005203 space -= ptr2cells(e);
5204 while (space < 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005205 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005206 space += ptr2cells(s);
5207 MB_PTR_ADV(s);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005208 }
5209 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005210 msg_hist_off = TRUE;
5211 vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
5212 s > compl_shown_match->cp_fname ? "<" : "", s);
5213 msg((char *)IObuff);
5214 msg_hist_off = FALSE;
5215 redraw_cmdline = FALSE; // don't overwrite!
5216}
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005217
glepnirdca57fb2024-06-04 22:01:21 +02005218/*
zeertzjq551d8c32024-06-05 19:53:32 +02005219 * Find a completion item when 'completeopt' contains "fuzzy".
glepnirdca57fb2024-06-04 22:01:21 +02005220 */
glepnira218cc62024-06-03 19:32:39 +02005221 static compl_T *
5222find_comp_when_fuzzy(void)
5223{
5224 int score;
5225 char_u* str;
5226 int target_idx = -1;
5227 int is_forward = compl_shows_dir_forward();
5228 int is_backward = compl_shows_dir_backward();
5229 compl_T *comp = NULL;
5230
glepnir43eef882024-06-19 20:20:48 +02005231 if ((is_forward && compl_selected_item == compl_match_arraysize - 1)
glepnira218cc62024-06-03 19:32:39 +02005232 || (is_backward && compl_selected_item == 0))
glepnir3e50a282025-04-03 21:17:06 +02005233 return compl_first_match != compl_shown_match ?
5234 (is_forward ? compl_shown_match->cp_next : compl_first_match) :
glepnir65407ce2024-07-06 16:09:19 +02005235 (compl_first_match->cp_prev ? compl_first_match->cp_prev : NULL);
glepnira218cc62024-06-03 19:32:39 +02005236
5237 if (is_forward)
5238 target_idx = compl_selected_item + 1;
5239 else if (is_backward)
Hirohito Higashi355db992025-04-28 18:07:02 +02005240 target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1
glepnirdca57fb2024-06-04 22:01:21 +02005241 : compl_selected_item - 1;
glepnira218cc62024-06-03 19:32:39 +02005242
5243 score = compl_match_array[target_idx].pum_score;
5244 str = compl_match_array[target_idx].pum_text;
5245
5246 comp = compl_first_match;
Hirohito Higashia4a00a72025-05-08 22:58:31 +02005247 do
5248 {
Hirohito Higashi355db992025-04-28 18:07:02 +02005249 if (comp->cp_score == score && (str == comp->cp_str.string || str == comp->cp_text[CPT_ABBR]))
5250 return comp;
5251 comp = comp->cp_next;
glepnira218cc62024-06-03 19:32:39 +02005252 } while (comp != NULL && !is_first_match(comp));
5253
5254 return NULL;
5255}
5256
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005257/*
Girish Palya0ac1eb32025-04-16 20:18:33 +02005258 * Find the appropriate completion item when 'complete' ('cpt') includes
5259 * a 'max_matches' postfix. In this case, we search for a match where
5260 * 'cp_in_match_array' is set, indicating that the match is also present
5261 * in 'compl_match_array'.
5262 */
5263 static compl_T *
5264find_comp_when_cpt_sources(void)
5265{
5266 int is_forward = compl_shows_dir_forward();
5267 compl_T *match = compl_shown_match;
5268
5269 do
5270 match = is_forward ? match->cp_next : match->cp_prev;
5271 while (match->cp_next && !match->cp_in_match_array
5272 && !match_at_original_text(match));
5273 return match;
5274}
5275
5276/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005277 * Find the next set of matches for completion. Repeat the completion "todo"
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005278 * times. The number of matches found is returned in 'num_matches'.
5279 *
5280 * If "allow_get_expansion" is TRUE, then ins_compl_get_exp() may be called to
5281 * get more completions. If it is FALSE, then do nothing when there are no more
5282 * completions in the given direction.
5283 *
5284 * If "advance" is TRUE, then completion will move to the first match.
5285 * Otherwise, the original text will be shown.
5286 *
5287 * Returns OK on success and -1 if the number of matches are unknown.
5288 */
5289 static int
5290find_next_completion_match(
5291 int allow_get_expansion,
5292 int todo, // repeat completion this many times
5293 int advance,
5294 int *num_matches)
5295{
5296 int found_end = FALSE;
5297 compl_T *found_compl = NULL;
zeertzjqaa925ee2024-06-09 18:24:05 +02005298 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02005299 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
5300 int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
Girish Palya0ac1eb32025-04-16 20:18:33 +02005301 int cpt_sources_active = compl_match_array && cpt_sources_array;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005302
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005303 while (--todo >= 0)
5304 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005305 if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005306 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02005307 if (compl_match_array != NULL && compl_fuzzy_match)
5308 compl_shown_match = find_comp_when_fuzzy();
5309 else if (cpt_sources_active)
5310 compl_shown_match = find_comp_when_cpt_sources();
5311 else
5312 compl_shown_match = compl_shown_match->cp_next;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005313 found_end = (compl_first_match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005314 && (is_first_match(compl_shown_match->cp_next)
5315 || is_first_match(compl_shown_match)));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005316 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005317 else if (compl_shows_dir_backward()
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005318 && compl_shown_match->cp_prev != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005319 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005320 found_end = is_first_match(compl_shown_match);
Girish Palya0ac1eb32025-04-16 20:18:33 +02005321 if (compl_match_array != NULL && compl_fuzzy_match)
5322 compl_shown_match = find_comp_when_fuzzy();
5323 else if (cpt_sources_active)
5324 compl_shown_match = find_comp_when_cpt_sources();
5325 else
5326 compl_shown_match = compl_shown_match->cp_prev;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005327 found_end |= is_first_match(compl_shown_match);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005328 }
5329 else
5330 {
5331 if (!allow_get_expansion)
5332 {
5333 if (advance)
5334 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005335 if (compl_shows_dir_backward())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005336 compl_pending -= todo + 1;
5337 else
5338 compl_pending += todo + 1;
5339 }
5340 return -1;
5341 }
5342
5343 if (!compl_no_select && advance)
5344 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005345 if (compl_shows_dir_backward())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005346 --compl_pending;
5347 else
5348 ++compl_pending;
5349 }
5350
5351 // Find matches.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005352 *num_matches = ins_compl_get_exp(&compl_startpos);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005353
5354 // handle any pending completions
5355 while (compl_pending != 0 && compl_direction == compl_shows_dir
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005356 && advance)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005357 {
5358 if (compl_pending > 0 && compl_shown_match->cp_next != NULL)
5359 {
5360 compl_shown_match = compl_shown_match->cp_next;
5361 --compl_pending;
5362 }
5363 if (compl_pending < 0 && compl_shown_match->cp_prev != NULL)
5364 {
5365 compl_shown_match = compl_shown_match->cp_prev;
5366 ++compl_pending;
5367 }
5368 else
5369 break;
5370 }
5371 found_end = FALSE;
5372 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005373 if (!match_at_original_text(compl_shown_match)
John Marriott5e6ea922024-11-23 14:01:57 +01005374 && compl_leader.string != NULL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005375 && !ins_compl_equal(compl_shown_match,
John Marriott5e6ea922024-11-23 14:01:57 +01005376 compl_leader.string, (int)compl_leader.length)
glepnira218cc62024-06-03 19:32:39 +02005377 && !(compl_fuzzy_match && compl_shown_match->cp_score > 0))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005378 ++todo;
5379 else
5380 // Remember a matching item.
5381 found_compl = compl_shown_match;
5382
5383 // Stop at the end of the list when we found a usable match.
5384 if (found_end)
5385 {
5386 if (found_compl != NULL)
5387 {
5388 compl_shown_match = found_compl;
5389 break;
5390 }
5391 todo = 1; // use first usable match after wrapping around
5392 }
5393 }
5394
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005395 return OK;
5396}
5397
5398/*
5399 * Fill in the next completion in the current direction.
5400 * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to
5401 * get more completions. If it is FALSE, then we just do nothing when there
5402 * are no more completions in a given direction. The latter case is used when
5403 * we are still in the middle of finding completions, to allow browsing
5404 * through the ones found so far.
5405 * Return the total number of matches, or -1 if still unknown -- webb.
5406 *
5407 * compl_curr_match is currently being used by ins_compl_get_exp(), so we use
5408 * compl_shown_match here.
5409 *
5410 * Note that this function may be called recursively once only. First with
5411 * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn
5412 * calls this function with "allow_get_expansion" FALSE.
5413 */
5414 static int
5415ins_compl_next(
5416 int allow_get_expansion,
5417 int count, // repeat completion this many times; should
5418 // be at least 1
5419 int insert_match, // Insert the newly selected match
5420 int in_compl_func) // called from complete_check()
5421{
5422 int num_matches = -1;
5423 int todo = count;
5424 int advance;
5425 int started = compl_started;
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005426 buf_T *orig_curbuf = curbuf;
zeertzjqaa925ee2024-06-09 18:24:05 +02005427 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02005428 int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
5429 int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
glepniredd4ac32025-01-29 18:53:51 +01005430 int compl_preinsert = ins_compl_has_preinsert();
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005431
5432 // When user complete function return -1 for findstart which is next
5433 // time of 'always', compl_shown_match become NULL.
5434 if (compl_shown_match == NULL)
5435 return -1;
5436
John Marriott5e6ea922024-11-23 14:01:57 +01005437 if (compl_leader.string != NULL
glepnira218cc62024-06-03 19:32:39 +02005438 && !match_at_original_text(compl_shown_match)
5439 && !compl_fuzzy_match)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005440 // Update "compl_shown_match" to the actually shown match
5441 ins_compl_update_shown_match();
5442
5443 if (allow_get_expansion && insert_match
nwounkn2e3cd522023-10-17 11:05:38 +02005444 && (!compl_get_longest || compl_used_match))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005445 // Delete old text to be replaced
5446 ins_compl_delete();
5447
5448 // When finding the longest common text we stick at the original text,
5449 // don't let CTRL-N or CTRL-P move to the first match.
5450 advance = count != 1 || !allow_get_expansion || !compl_get_longest;
5451
5452 // When restarting the search don't insert the first match either.
5453 if (compl_restarting)
5454 {
5455 advance = FALSE;
5456 compl_restarting = FALSE;
5457 }
5458
5459 // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
5460 // around.
5461 if (find_next_completion_match(allow_get_expansion, todo, advance,
5462 &num_matches) == -1)
5463 return -1;
5464
Bram Moolenaar0ff01832022-09-24 19:20:30 +01005465 if (curbuf != orig_curbuf)
5466 {
5467 // In case some completion function switched buffer, don't want to
5468 // insert the completion elsewhere.
5469 return -1;
5470 }
5471
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005472 // Insert the text of the new completion, or the compl_leader.
glepniredd4ac32025-01-29 18:53:51 +01005473 if (compl_no_insert && !started && !compl_preinsert)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005474 {
glepnir6a38aff2024-12-16 21:56:16 +01005475 ins_compl_insert_bytes(compl_orig_text.string + get_compl_len(), -1);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005476 compl_used_match = FALSE;
5477 }
5478 else if (insert_match)
5479 {
5480 if (!compl_get_longest || compl_used_match)
glepniredd4ac32025-01-29 18:53:51 +01005481 ins_compl_insert(in_compl_func, TRUE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005482 else
glepnir6a38aff2024-12-16 21:56:16 +01005483 ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005484 }
5485 else
5486 compl_used_match = FALSE;
5487
5488 if (!allow_get_expansion)
5489 {
5490 // may undisplay the popup menu first
5491 ins_compl_upd_pum();
5492
5493 if (pum_enough_matches())
5494 // Will display the popup menu, don't redraw yet to avoid flicker.
5495 pum_call_update_screen();
5496 else
5497 // Not showing the popup menu yet, redraw to show the user what was
5498 // inserted.
5499 update_screen(0);
5500
5501 // display the updated popup menu
5502 ins_compl_show_pum();
5503#ifdef FEAT_GUI
5504 if (gui.in_use)
5505 {
5506 // Show the cursor after the match, not after the redrawn text.
5507 setcursor();
5508 out_flush_cursor(FALSE, FALSE);
5509 }
5510#endif
5511
5512 // Delete old text to be replaced, since we're still searching and
5513 // don't want to match ourselves!
5514 ins_compl_delete();
5515 }
5516
5517 // Enter will select a match when the match wasn't inserted and the popup
5518 // menu is visible.
5519 if (compl_no_insert && !started)
5520 compl_enter_selects = TRUE;
5521 else
5522 compl_enter_selects = !insert_match && compl_match_array != NULL;
5523
5524 // Show the file name for the match (if any)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005525 if (compl_shown_match->cp_fname != NULL)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005526 ins_compl_show_filename();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005527
5528 return num_matches;
5529}
5530
5531/*
5532 * Call this while finding completions, to check whether the user has hit a key
5533 * that should change the currently displayed completion, or exit completion
5534 * mode. Also, when compl_pending is not zero, show a completion as soon as
5535 * possible. -- webb
5536 * "frequency" specifies out of how many calls we actually check.
5537 * "in_compl_func" is TRUE when called from complete_check(), don't set
5538 * compl_curr_match.
5539 */
5540 void
5541ins_compl_check_keys(int frequency, int in_compl_func)
5542{
5543 static int count = 0;
5544 int c;
5545
5546 // Don't check when reading keys from a script, :normal or feedkeys().
5547 // That would break the test scripts. But do check for keys when called
5548 // from complete_check().
5549 if (!in_compl_func && (using_script() || ex_normal_busy))
5550 return;
5551
5552 // Only do this at regular intervals
5553 if (++count < frequency)
5554 return;
5555 count = 0;
5556
5557 // Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key()
5558 // can't do its work correctly.
5559 c = vpeekc_any();
5560 if (c != NUL)
5561 {
5562 if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R)
5563 {
5564 c = safe_vgetc(); // Eat the character
5565 compl_shows_dir = ins_compl_key2dir(c);
5566 (void)ins_compl_next(FALSE, ins_compl_key2count(c),
5567 c != K_UP && c != K_DOWN, in_compl_func);
5568 }
5569 else
5570 {
5571 // Need to get the character to have KeyTyped set. We'll put it
5572 // back with vungetc() below. But skip K_IGNORE.
5573 c = safe_vgetc();
5574 if (c != K_IGNORE)
5575 {
5576 // Don't interrupt completion when the character wasn't typed,
5577 // e.g., when doing @q to replay keys.
5578 if (c != Ctrl_R && KeyTyped)
5579 compl_interrupted = TRUE;
5580
5581 vungetc(c);
5582 }
5583 }
5584 }
zeertzjq529b9ad2024-06-05 20:27:06 +02005585 if (compl_pending != 0 && !got_int && !(cot_flags & COT_NOINSERT))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005586 {
5587 int todo = compl_pending > 0 ? compl_pending : -compl_pending;
5588
5589 compl_pending = 0;
5590 (void)ins_compl_next(FALSE, todo, TRUE, in_compl_func);
5591 }
5592}
5593
5594/*
5595 * Decide the direction of Insert mode complete from the key typed.
5596 * Returns BACKWARD or FORWARD.
5597 */
5598 static int
5599ins_compl_key2dir(int c)
5600{
5601 if (c == Ctrl_P || c == Ctrl_L
5602 || c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP || c == K_UP)
5603 return BACKWARD;
5604 return FORWARD;
5605}
5606
5607/*
5608 * Return TRUE for keys that are used for completion only when the popup menu
5609 * is visible.
5610 */
5611 static int
5612ins_compl_pum_key(int c)
5613{
5614 return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP
5615 || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == K_S_DOWN
5616 || c == K_UP || c == K_DOWN);
5617}
5618
5619/*
5620 * Decide the number of completions to move forward.
5621 * Returns 1 for most keys, height of the popup menu for page-up/down keys.
5622 */
5623 static int
5624ins_compl_key2count(int c)
5625{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005626 if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN)
5627 {
glepnir19e1dd62025-05-08 22:50:38 +02005628 int h = pum_get_height();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005629 if (h > 3)
5630 h -= 2; // keep some context
5631 return h;
5632 }
5633 return 1;
5634}
5635
5636/*
5637 * Return TRUE if completion with "c" should insert the match, FALSE if only
5638 * to change the currently selected completion.
5639 */
5640 static int
5641ins_compl_use_match(int c)
5642{
5643 switch (c)
5644 {
5645 case K_UP:
5646 case K_DOWN:
5647 case K_PAGEDOWN:
5648 case K_KPAGEDOWN:
5649 case K_S_DOWN:
5650 case K_PAGEUP:
5651 case K_KPAGEUP:
5652 case K_S_UP:
5653 return FALSE;
5654 }
5655 return TRUE;
5656}
5657
5658/*
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005659 * Get the pattern, column and length for normal completion (CTRL-N CTRL-P
5660 * completion)
John Marriott5e6ea922024-11-23 14:01:57 +01005661 * Sets the global variables: compl_col, compl_length and compl_pattern.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005662 * Uses the global variables: compl_cont_status and ctrl_x_mode
5663 */
5664 static int
5665get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
5666{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005667 if ((compl_cont_status & CONT_SOL) || ctrl_x_mode_path_defines())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005668 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005669 if (!compl_status_adding())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005670 {
5671 while (--startcol >= 0 && vim_isIDc(line[startcol]))
5672 ;
5673 compl_col += ++startcol;
5674 compl_length = curs_col - startcol;
5675 }
5676 if (p_ic)
John Marriott8c85a2a2024-05-20 19:18:26 +02005677 {
John Marriott5e6ea922024-11-23 14:01:57 +01005678 compl_pattern.string = str_foldcase(line + compl_col,
5679 compl_length, NULL, 0);
5680 if (compl_pattern.string == NULL)
5681 {
5682 compl_pattern.length = 0;
5683 return FAIL;
5684 }
5685 compl_pattern.length = STRLEN(compl_pattern.string);
5686 }
5687 else
5688 {
5689 compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
5690 if (compl_pattern.string == NULL)
5691 {
5692 compl_pattern.length = 0;
5693 return FAIL;
5694 }
5695 compl_pattern.length = (size_t)compl_length;
John Marriott8c85a2a2024-05-20 19:18:26 +02005696 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005697 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005698 else if (compl_status_adding())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005699 {
5700 char_u *prefix = (char_u *)"\\<";
John Marriott8c85a2a2024-05-20 19:18:26 +02005701 size_t prefixlen = STRLEN_LITERAL("\\<");
John Marriott5e6ea922024-11-23 14:01:57 +01005702 size_t n;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005703
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005704 if (!vim_iswordp(line + compl_col)
5705 || (compl_col > 0
5706 && (vim_iswordp(mb_prevptr(line, line + compl_col)))))
John Marriott8c85a2a2024-05-20 19:18:26 +02005707 {
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005708 prefix = (char_u *)"";
John Marriott8c85a2a2024-05-20 19:18:26 +02005709 prefixlen = 0;
5710 }
John Marriott5e6ea922024-11-23 14:01:57 +01005711
5712 // we need up to 2 extra chars for the prefix
5713 n = quote_meta(NULL, line + compl_col, compl_length) + prefixlen;
5714 compl_pattern.string = alloc(n);
5715 if (compl_pattern.string == NULL)
5716 {
5717 compl_pattern.length = 0;
5718 return FAIL;
5719 }
5720 STRCPY((char *)compl_pattern.string, prefix);
5721 (void)quote_meta(compl_pattern.string + prefixlen,
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005722 line + compl_col, compl_length);
John Marriott5e6ea922024-11-23 14:01:57 +01005723 compl_pattern.length = n - 1;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005724 }
5725 else if (--startcol < 0
5726 || !vim_iswordp(mb_prevptr(line, line + startcol + 1)))
5727 {
John Marriott5e6ea922024-11-23 14:01:57 +01005728 size_t len = STRLEN_LITERAL("\\<\\k\\k");
5729
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005730 // Match any word of at least two chars
John Marriott5e6ea922024-11-23 14:01:57 +01005731 compl_pattern.string = vim_strnsave((char_u *)"\\<\\k\\k", len);
5732 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02005733 {
John Marriott5e6ea922024-11-23 14:01:57 +01005734 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005735 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02005736 }
John Marriott5e6ea922024-11-23 14:01:57 +01005737 compl_pattern.length = len;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005738 compl_col += curs_col;
5739 compl_length = 0;
5740 }
5741 else
5742 {
5743 // Search the point of change class of multibyte character
5744 // or not a word single byte character backward.
5745 if (has_mbyte)
5746 {
5747 int base_class;
5748 int head_off;
5749
5750 startcol -= (*mb_head_off)(line, line + startcol);
5751 base_class = mb_get_class(line + startcol);
5752 while (--startcol >= 0)
5753 {
5754 head_off = (*mb_head_off)(line, line + startcol);
5755 if (base_class != mb_get_class(line + startcol
5756 - head_off))
5757 break;
5758 startcol -= head_off;
5759 }
5760 }
5761 else
glepnir19e1dd62025-05-08 22:50:38 +02005762 {
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005763 while (--startcol >= 0 && vim_iswordc(line[startcol]))
5764 ;
glepnir19e1dd62025-05-08 22:50:38 +02005765 }
5766
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005767 compl_col += ++startcol;
5768 compl_length = (int)curs_col - startcol;
5769 if (compl_length == 1)
5770 {
5771 // Only match word with at least two chars -- webb
5772 // there's no need to call quote_meta,
5773 // alloc(7) is enough -- Acevedo
John Marriott5e6ea922024-11-23 14:01:57 +01005774 compl_pattern.string = alloc(7);
5775 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02005776 {
John Marriott5e6ea922024-11-23 14:01:57 +01005777 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005778 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02005779 }
John Marriott5e6ea922024-11-23 14:01:57 +01005780 STRCPY((char *)compl_pattern.string, "\\<");
5781 (void)quote_meta(compl_pattern.string + 2, line + compl_col, 1);
5782 STRCAT((char *)compl_pattern.string, "\\k");
5783 compl_pattern.length = STRLEN(compl_pattern.string);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005784 }
5785 else
5786 {
John Marriott5e6ea922024-11-23 14:01:57 +01005787 size_t n = quote_meta(NULL, line + compl_col, compl_length) + 2;
5788
5789 compl_pattern.string = alloc(n);
5790 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02005791 {
John Marriott5e6ea922024-11-23 14:01:57 +01005792 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005793 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02005794 }
John Marriott5e6ea922024-11-23 14:01:57 +01005795 STRCPY((char *)compl_pattern.string, "\\<");
5796 (void)quote_meta(compl_pattern.string + 2, line + compl_col,
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005797 compl_length);
John Marriott5e6ea922024-11-23 14:01:57 +01005798 compl_pattern.length = n - 1;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005799 }
5800 }
5801
5802 return OK;
5803}
5804
5805/*
5806 * Get the pattern, column and length for whole line completion or for the
5807 * complete() function.
5808 * Sets the global variables: compl_col, compl_length and compl_pattern.
5809 */
5810 static int
5811get_wholeline_compl_info(char_u *line, colnr_T curs_col)
5812{
5813 compl_col = (colnr_T)getwhitecols(line);
5814 compl_length = (int)curs_col - (int)compl_col;
5815 if (compl_length < 0) // cursor in indent: empty pattern
5816 compl_length = 0;
5817 if (p_ic)
John Marriott8c85a2a2024-05-20 19:18:26 +02005818 {
John Marriott5e6ea922024-11-23 14:01:57 +01005819 compl_pattern.string = str_foldcase(line + compl_col, compl_length,
5820 NULL, 0);
5821 if (compl_pattern.string == NULL)
5822 {
5823 compl_pattern.length = 0;
5824 return FAIL;
5825 }
5826 compl_pattern.length = STRLEN(compl_pattern.string);
John Marriott8c85a2a2024-05-20 19:18:26 +02005827 }
John Marriott5e6ea922024-11-23 14:01:57 +01005828 else
5829 {
5830 compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
5831 if (compl_pattern.string == NULL)
5832 {
5833 compl_pattern.length = 0;
5834 return FAIL;
5835 }
5836 compl_pattern.length = (size_t)compl_length;
5837 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005838
5839 return OK;
5840}
5841
5842/*
5843 * Get the pattern, column and length for filename completion.
5844 * Sets the global variables: compl_col, compl_length and compl_pattern.
5845 */
5846 static int
5847get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col)
5848{
5849 // Go back to just before the first filename character.
5850 if (startcol > 0)
5851 {
5852 char_u *p = line + startcol;
5853
5854 MB_PTR_BACK(line, p);
5855 while (p > line && vim_isfilec(PTR2CHAR(p)))
5856 MB_PTR_BACK(line, p);
5857 if (p == line && vim_isfilec(PTR2CHAR(p)))
5858 startcol = 0;
5859 else
5860 startcol = (int)(p - line) + 1;
5861 }
5862
5863 compl_col += startcol;
5864 compl_length = (int)curs_col - startcol;
John Marriott5e6ea922024-11-23 14:01:57 +01005865 compl_pattern.string = addstar(line + compl_col, compl_length, EXPAND_FILES);
5866 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02005867 {
John Marriott5e6ea922024-11-23 14:01:57 +01005868 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005869 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02005870 }
5871
John Marriott5e6ea922024-11-23 14:01:57 +01005872 compl_pattern.length = STRLEN(compl_pattern.string);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005873
5874 return OK;
5875}
5876
5877/*
5878 * Get the pattern, column and length for command-line completion.
5879 * Sets the global variables: compl_col, compl_length and compl_pattern.
5880 */
5881 static int
5882get_cmdline_compl_info(char_u *line, colnr_T curs_col)
5883{
John Marriott5e6ea922024-11-23 14:01:57 +01005884 compl_pattern.string = vim_strnsave(line, curs_col);
5885 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02005886 {
John Marriott5e6ea922024-11-23 14:01:57 +01005887 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005888 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02005889 }
John Marriott5e6ea922024-11-23 14:01:57 +01005890 compl_pattern.length = curs_col;
5891 set_cmd_context(&compl_xp, compl_pattern.string,
5892 (int)compl_pattern.length, curs_col, FALSE);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005893 if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
5894 || compl_xp.xp_context == EXPAND_NOTHING)
5895 // No completion possible, use an empty pattern to get a
5896 // "pattern not found" message.
5897 compl_col = curs_col;
5898 else
John Marriott5e6ea922024-11-23 14:01:57 +01005899 compl_col = (int)(compl_xp.xp_pattern - compl_pattern.string);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005900 compl_length = curs_col - compl_col;
5901
5902 return OK;
5903}
5904
5905/*
5906 * Get the pattern, column and length for user defined completion ('omnifunc',
5907 * 'completefunc' and 'thesaurusfunc')
5908 * Sets the global variables: compl_col, compl_length and compl_pattern.
5909 * Uses the global variable: spell_bad_len
Girish Palyacbe53192025-04-14 22:13:15 +02005910 * Callback function "cb" is set if triggered by a function in the 'cpt'
5911 * option; otherwise, it is NULL.
5912 * "startcol", when not NULL, contains the column returned by function.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005913 */
5914 static int
glepnir19e1dd62025-05-08 22:50:38 +02005915get_userdefined_compl_info(
5916 colnr_T curs_col UNUSED,
5917 callback_T *cb UNUSED,
5918 int *startcol UNUSED)
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005919{
5920 int ret = FAIL;
5921
5922#ifdef FEAT_COMPL_FUNC
5923 // Call user defined function 'completefunc' with "a:findstart"
5924 // set to 1 to obtain the length of text to use for completion.
glepnir19e1dd62025-05-08 22:50:38 +02005925 char_u *line = NULL;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005926 typval_T args[3];
5927 int col;
glepnir19e1dd62025-05-08 22:50:38 +02005928 char_u *funcname = NULL;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005929 pos_T pos;
5930 int save_State = State;
Girish Palyacbe53192025-04-14 22:13:15 +02005931 int len;
glepnir19e1dd62025-05-08 22:50:38 +02005932 string_T *compl_pat = NULL;
Girish Palyacbe53192025-04-14 22:13:15 +02005933 int is_cpt_function = (cb != NULL);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005934
Girish Palyacbe53192025-04-14 22:13:15 +02005935 if (!is_cpt_function)
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005936 {
Girish Palyacbe53192025-04-14 22:13:15 +02005937 // Call 'completefunc' or 'omnifunc' or 'thesaurusfunc' and get pattern
5938 // length as a string
5939 funcname = get_complete_funcname(ctrl_x_mode);
5940 if (*funcname == NUL)
5941 {
5942 semsg(_(e_option_str_is_not_set), ctrl_x_mode_function()
5943 ? "completefunc" : "omnifunc");
5944 return FAIL;
5945 }
5946 cb = get_insert_callback(ctrl_x_mode);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005947 }
5948
5949 args[0].v_type = VAR_NUMBER;
5950 args[0].vval.v_number = 1;
5951 args[1].v_type = VAR_STRING;
5952 args[1].vval.v_string = (char_u *)"";
5953 args[2].v_type = VAR_UNKNOWN;
5954 pos = curwin->w_cursor;
zeertzjqcfe45652022-05-27 17:26:55 +01005955 ++textlock;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005956 col = call_callback_retnr(cb, 2, args);
zeertzjqcfe45652022-05-27 17:26:55 +01005957 --textlock;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005958
5959 State = save_State;
5960 curwin->w_cursor = pos; // restore the cursor position
zeertzjq0a419e02024-04-02 19:01:14 +02005961 check_cursor(); // make sure cursor position is valid, just in case
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005962 validate_cursor();
5963 if (!EQUAL_POS(curwin->w_cursor, pos))
5964 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00005965 emsg(_(e_complete_function_deleted_text));
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005966 return FAIL;
5967 }
5968
Girish Palyacbe53192025-04-14 22:13:15 +02005969 if (startcol != NULL)
5970 *startcol = col;
5971
LemonBoy9bcb9ca2022-05-26 15:23:26 +01005972 // Return value -2 means the user complete function wants to cancel the
5973 // complete without an error, do the same if the function did not execute
5974 // successfully.
5975 if (col == -2 || aborting())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005976 return FAIL;
glepnir19e1dd62025-05-08 22:50:38 +02005977
LemonBoy9bcb9ca2022-05-26 15:23:26 +01005978 // Return value -3 does the same as -2 and leaves CTRL-X mode.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005979 if (col == -3)
5980 {
Girish Palyacbe53192025-04-14 22:13:15 +02005981 if (is_cpt_function)
5982 return FAIL;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005983 ctrl_x_mode = CTRL_X_NORMAL;
5984 edit_submode = NULL;
5985 if (!shortmess(SHM_COMPLETIONMENU))
5986 msg_clr_cmdline();
5987 return FAIL;
5988 }
5989
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00005990 // Reset extended parameters of completion, when starting new
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005991 // completion.
5992 compl_opt_refresh_always = FALSE;
5993 compl_opt_suppress_empty = FALSE;
5994
Girish Palyacbe53192025-04-14 22:13:15 +02005995 if (col < 0 || col > curs_col)
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005996 col = curs_col;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005997
5998 // Setup variables for completion. Need to obtain "line" again,
5999 // it may have become invalid.
6000 line = ml_get(curwin->w_cursor.lnum);
Girish Palyacbe53192025-04-14 22:13:15 +02006001 len = curs_col - col;
6002 compl_pat = is_cpt_function ? &cpt_compl_pattern : &compl_pattern;
6003 compl_pat->string = vim_strnsave(line + col, (size_t)len);
6004 if (compl_pat->string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02006005 {
Girish Palyacbe53192025-04-14 22:13:15 +02006006 compl_pat->length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006007 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02006008 }
Girish Palyacbe53192025-04-14 22:13:15 +02006009 compl_pat->length = (size_t)compl_length;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006010
Girish Palyacbe53192025-04-14 22:13:15 +02006011 if (!is_cpt_function)
6012 {
6013 compl_col = col;
6014 compl_length = len;
6015 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006016 ret = OK;
6017#endif
6018
6019 return ret;
6020}
6021
6022/*
6023 * Get the pattern, column and length for spell completion.
6024 * Sets the global variables: compl_col, compl_length and compl_pattern.
6025 * Uses the global variable: spell_bad_len
6026 */
6027 static int
6028get_spell_compl_info(int startcol UNUSED, colnr_T curs_col UNUSED)
6029{
6030 int ret = FAIL;
6031#ifdef FEAT_SPELL
glepnir19e1dd62025-05-08 22:50:38 +02006032 char_u *line = NULL;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006033
6034 if (spell_bad_len > 0)
6035 compl_col = curs_col - spell_bad_len;
6036 else
6037 compl_col = spell_word_start(startcol);
glepnir19e1dd62025-05-08 22:50:38 +02006038
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006039 if (compl_col >= (colnr_T)startcol)
6040 {
6041 compl_length = 0;
6042 compl_col = curs_col;
6043 }
6044 else
6045 {
6046 spell_expand_check_cap(compl_col);
6047 compl_length = (int)curs_col - compl_col;
6048 }
6049 // Need to obtain "line" again, it may have become invalid.
6050 line = ml_get(curwin->w_cursor.lnum);
John Marriott5e6ea922024-11-23 14:01:57 +01006051 compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
6052 if (compl_pattern.string == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02006053 {
John Marriott5e6ea922024-11-23 14:01:57 +01006054 compl_pattern.length = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006055 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02006056 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006057
John Marriott5e6ea922024-11-23 14:01:57 +01006058 compl_pattern.length = (size_t)compl_length;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006059 ret = OK;
6060#endif
6061
6062 return ret;
6063}
6064
6065/*
6066 * Get the completion pattern, column and length.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006067 * "startcol" - start column number of the completion pattern/text
6068 * "cur_col" - current cursor column
6069 * On return, "line_invalid" is set to TRUE, if the current line may have
6070 * become invalid and needs to be fetched again.
6071 * Returns OK on success.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006072 */
6073 static int
6074compl_get_info(char_u *line, int startcol, colnr_T curs_col, int *line_invalid)
6075{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006076 if (ctrl_x_mode_normal()
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006077 || (ctrl_x_mode & CTRL_X_WANT_IDENT
6078 && !thesaurus_func_complete(ctrl_x_mode)))
6079 {
6080 return get_normal_compl_info(line, startcol, curs_col);
6081 }
6082 else if (ctrl_x_mode_line_or_eval())
6083 {
6084 return get_wholeline_compl_info(line, curs_col);
6085 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006086 else if (ctrl_x_mode_files())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006087 {
6088 return get_filename_compl_info(line, startcol, curs_col);
6089 }
6090 else if (ctrl_x_mode == CTRL_X_CMDLINE)
6091 {
6092 return get_cmdline_compl_info(line, curs_col);
6093 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006094 else if (ctrl_x_mode_function() || ctrl_x_mode_omni()
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006095 || thesaurus_func_complete(ctrl_x_mode))
6096 {
Girish Palyacbe53192025-04-14 22:13:15 +02006097 if (get_userdefined_compl_info(curs_col, NULL, NULL) != OK)
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006098 return FAIL;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006099 *line_invalid = TRUE; // "line" may have become invalid
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006100 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006101 else if (ctrl_x_mode_spell())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006102 {
6103 if (get_spell_compl_info(startcol, curs_col) == FAIL)
6104 return FAIL;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006105 *line_invalid = TRUE; // "line" may have become invalid
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00006106 }
6107 else
6108 {
6109 internal_error("ins_complete()");
6110 return FAIL;
6111 }
6112
6113 return OK;
6114}
6115
6116/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006117 * Continue an interrupted completion mode search in "line".
6118 *
6119 * If this same ctrl_x_mode has been interrupted use the text from
6120 * "compl_startpos" to the cursor as a pattern to add a new word instead of
6121 * expand the one before the cursor, in word-wise if "compl_startpos" is not in
6122 * the same line as the cursor then fix it (the line has been split because it
6123 * was longer than 'tw'). if SOL is set then skip the previous pattern, a word
6124 * at the beginning of the line has been inserted, we'll look for that.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006125 */
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006126 static void
6127ins_compl_continue_search(char_u *line)
6128{
6129 // it is a continued search
6130 compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006131 if (ctrl_x_mode_normal() || ctrl_x_mode_path_patterns()
6132 || ctrl_x_mode_path_defines())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006133 {
6134 if (compl_startpos.lnum != curwin->w_cursor.lnum)
6135 {
6136 // line (probably) wrapped, set compl_startpos to the
6137 // first non_blank in the line, if it is not a wordchar
6138 // include it to get a better pattern, but then we don't
6139 // want the "\\<" prefix, check it below
6140 compl_col = (colnr_T)getwhitecols(line);
6141 compl_startpos.col = compl_col;
6142 compl_startpos.lnum = curwin->w_cursor.lnum;
6143 compl_cont_status &= ~CONT_SOL; // clear SOL if present
6144 }
6145 else
6146 {
6147 // S_IPOS was set when we inserted a word that was at the
6148 // beginning of the line, which means that we'll go to SOL
6149 // mode but first we need to redefine compl_startpos
6150 if (compl_cont_status & CONT_S_IPOS)
6151 {
6152 compl_cont_status |= CONT_SOL;
6153 compl_startpos.col = (colnr_T)(skipwhite(
6154 line + compl_length
6155 + compl_startpos.col) - line);
6156 }
6157 compl_col = compl_startpos.col;
6158 }
6159 compl_length = curwin->w_cursor.col - (int)compl_col;
6160 // IObuff is used to add a "word from the next line" would we
6161 // have enough space? just being paranoid
6162#define MIN_SPACE 75
6163 if (compl_length > (IOSIZE - MIN_SPACE))
6164 {
6165 compl_cont_status &= ~CONT_SOL;
6166 compl_length = (IOSIZE - MIN_SPACE);
6167 compl_col = curwin->w_cursor.col - compl_length;
6168 }
6169 compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
6170 if (compl_length < 1)
6171 compl_cont_status &= CONT_LOCAL;
6172 }
6173 else if (ctrl_x_mode_line_or_eval())
6174 compl_cont_status = CONT_ADDING | CONT_N_ADDS;
6175 else
6176 compl_cont_status = 0;
6177}
6178
6179/*
6180 * start insert mode completion
6181 */
6182 static int
6183ins_compl_start(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006184{
glepnir19e1dd62025-05-08 22:50:38 +02006185 char_u *line = NULL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006186 int startcol = 0; // column where searched text starts
6187 colnr_T curs_col; // cursor column
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006188 int line_invalid = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006189 int save_did_ai = did_ai;
Bram Moolenaard9eefe32019-04-06 14:22:21 +02006190 int flags = CP_ORIGINAL_TEXT;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006191
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006192 // First time we hit ^N or ^P (in a row, I mean)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006193 did_ai = FALSE;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006194 did_si = FALSE;
6195 can_si = FALSE;
6196 can_si_back = FALSE;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006197 if (stop_arrow() == FAIL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006198 return FAIL;
6199
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006200 line = ml_get(curwin->w_cursor.lnum);
6201 curs_col = curwin->w_cursor.col;
6202 compl_pending = 0;
glepnir76bdb822025-02-08 19:04:51 +01006203 compl_lnum = curwin->w_cursor.lnum;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006204
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006205 if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
6206 && compl_cont_mode == ctrl_x_mode)
6207 // this same ctrl-x_mode was interrupted previously. Continue the
6208 // completion.
6209 ins_compl_continue_search(line);
6210 else
6211 compl_cont_status &= CONT_LOCAL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006212
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006213 if (!compl_status_adding()) // normal expansion
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006214 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006215 compl_cont_mode = ctrl_x_mode;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006216 if (ctrl_x_mode_not_default())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006217 // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
6218 compl_cont_status = 0;
6219 compl_cont_status |= CONT_N_ADDS;
6220 compl_startpos = curwin->w_cursor;
6221 startcol = (int)curs_col;
6222 compl_col = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006223 }
6224
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006225 // Work out completion pattern and original text -- webb
6226 if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL)
6227 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006228 if (ctrl_x_mode_function() || ctrl_x_mode_omni()
6229 || thesaurus_func_complete(ctrl_x_mode))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006230 // restore did_ai, so that adding comment leader works
6231 did_ai = save_did_ai;
6232 return FAIL;
6233 }
6234 // If "line" was changed while getting completion info get it again.
6235 if (line_invalid)
6236 line = ml_get(curwin->w_cursor.lnum);
6237
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006238 if (compl_status_adding())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006239 {
6240 edit_submode_pre = (char_u *)_(" Adding");
6241 if (ctrl_x_mode_line_or_eval())
6242 {
6243 // Insert a new line, keep indentation but ignore 'comments'.
6244 char_u *old = curbuf->b_p_com;
6245
6246 curbuf->b_p_com = (char_u *)"";
6247 compl_startpos.lnum = curwin->w_cursor.lnum;
6248 compl_startpos.col = compl_col;
6249 ins_eol('\r');
6250 curbuf->b_p_com = old;
6251 compl_length = 0;
6252 compl_col = curwin->w_cursor.col;
glepnir76bdb822025-02-08 19:04:51 +01006253 compl_lnum = curwin->w_cursor.lnum;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006254 }
glepnircfe502c2025-04-17 20:17:53 +02006255 else if (ctrl_x_mode_normal() && cfc_has_mode())
glepnir7cfe6932024-09-15 20:06:28 +02006256 {
6257 compl_startpos = curwin->w_cursor;
6258 compl_cont_status &= CONT_S_IPOS;
6259 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006260 }
6261 else
6262 {
6263 edit_submode_pre = NULL;
6264 compl_startpos.col = compl_col;
6265 }
6266
6267 if (compl_cont_status & CONT_LOCAL)
6268 edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
6269 else
6270 edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
6271
6272 // If any of the original typed text has been changed we need to fix
6273 // the redo buffer.
6274 ins_compl_fixRedoBufForLeader(NULL);
6275
6276 // Always add completion for the original text.
John Marriott5e6ea922024-11-23 14:01:57 +01006277 VIM_CLEAR_STRING(compl_orig_text);
6278 compl_orig_text.length = (size_t)compl_length;
6279 compl_orig_text.string = vim_strnsave(line + compl_col, (size_t)compl_length);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006280 if (p_ic)
6281 flags |= CP_ICASE;
zeertzjq4422de62025-03-07 19:06:02 +01006282 if (compl_orig_text.string == NULL
6283 || ins_compl_add(compl_orig_text.string,
6284 (int)compl_orig_text.length,
6285 NULL, NULL, NULL, 0, flags, FALSE, NULL, 0) != OK)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006286 {
John Marriott5e6ea922024-11-23 14:01:57 +01006287 VIM_CLEAR_STRING(compl_pattern);
6288 VIM_CLEAR_STRING(compl_orig_text);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006289 return FAIL;
6290 }
6291
6292 // showmode might reset the internal line pointers, so it must
6293 // be called before line = ml_get(), or when this address is no
6294 // longer needed. -- Acevedo.
6295 edit_submode_extra = (char_u *)_("-- Searching...");
6296 edit_submode_highl = HLF_COUNT;
6297 showmode();
6298 edit_submode_extra = NULL;
6299 out_flush();
6300
6301 return OK;
6302}
6303
6304/*
6305 * display the completion status message
6306 */
6307 static void
6308ins_compl_show_statusmsg(void)
6309{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006310 // we found no match if the list has only the "compl_orig_text"-entry
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006311 if (is_first_match(compl_first_match->cp_next))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006312 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006313 edit_submode_extra = compl_status_adding() && compl_length > 1
Bram Moolenaarb53a1902022-11-15 13:57:38 +00006314 ? (char_u *)_("Hit end of paragraph")
6315 : (char_u *)_("Pattern not found");
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006316 edit_submode_highl = HLF_E;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006317 }
6318
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006319 if (edit_submode_extra == NULL)
6320 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006321 if (match_at_original_text(compl_curr_match))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006322 {
6323 edit_submode_extra = (char_u *)_("Back at original");
6324 edit_submode_highl = HLF_W;
6325 }
6326 else if (compl_cont_status & CONT_S_IPOS)
6327 {
6328 edit_submode_extra = (char_u *)_("Word from other line");
6329 edit_submode_highl = HLF_COUNT;
6330 }
6331 else if (compl_curr_match->cp_next == compl_curr_match->cp_prev)
6332 {
6333 edit_submode_extra = (char_u *)_("The only match");
6334 edit_submode_highl = HLF_COUNT;
Bram Moolenaarf9d51352020-10-26 19:22:42 +01006335 compl_curr_match->cp_number = 1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006336 }
6337 else
6338 {
Bram Moolenaar977fd0b2020-10-27 09:12:45 +01006339#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006340 // Update completion sequence number when needed.
6341 if (compl_curr_match->cp_number == -1)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01006342 ins_compl_update_sequence_numbers();
Bram Moolenaar977fd0b2020-10-27 09:12:45 +01006343#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006344 // The match should always have a sequence number now, this is
6345 // just a safety check.
6346 if (compl_curr_match->cp_number != -1)
6347 {
6348 // Space for 10 text chars. + 2x10-digit no.s = 31.
6349 // Translations may need more than twice that.
6350 static char_u match_ref[81];
6351
6352 if (compl_matches > 0)
6353 vim_snprintf((char *)match_ref, sizeof(match_ref),
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006354 _("match %d of %d"),
6355 compl_curr_match->cp_number, compl_matches);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006356 else
6357 vim_snprintf((char *)match_ref, sizeof(match_ref),
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006358 _("match %d"),
6359 compl_curr_match->cp_number);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006360 edit_submode_extra = match_ref;
6361 edit_submode_highl = HLF_R;
6362 if (dollar_vcol >= 0)
6363 curs_columns(FALSE);
6364 }
6365 }
6366 }
6367
6368 // Show a message about what (completion) mode we're in.
6369 if (!compl_opt_suppress_empty)
6370 {
6371 showmode();
6372 if (!shortmess(SHM_COMPLETIONMENU))
6373 {
6374 if (edit_submode_extra != NULL)
6375 {
6376 if (!p_smd)
Bram Moolenaarcc233582020-12-12 13:32:07 +01006377 {
6378 msg_hist_off = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006379 msg_attr((char *)edit_submode_extra,
6380 edit_submode_highl < HLF_COUNT
6381 ? HL_ATTR(edit_submode_highl) : 0);
Bram Moolenaarcc233582020-12-12 13:32:07 +01006382 msg_hist_off = FALSE;
6383 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006384 }
6385 else
6386 msg_clr_cmdline(); // necessary for "noshowmode"
6387 }
6388 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006389}
6390
6391/*
6392 * Do Insert mode completion.
6393 * Called when character "c" was typed, which has a meaning for completion.
6394 * Returns OK if completion was done, FAIL if something failed (out of mem).
6395 */
6396 int
6397ins_complete(int c, int enable_pum)
6398{
6399 int n;
6400 int save_w_wrow;
6401 int save_w_leftcol;
6402 int insert_match;
6403
6404 compl_direction = ins_compl_key2dir(c);
6405 insert_match = ins_compl_use_match(c);
6406
6407 if (!compl_started)
6408 {
6409 if (ins_compl_start() == FAIL)
6410 return FAIL;
6411 }
6412 else if (insert_match && stop_arrow() == FAIL)
6413 return FAIL;
6414
glepnircf7f0122025-04-15 19:02:00 +02006415 compl_curr_win = curwin;
6416 compl_curr_buf = curwin->w_buffer;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006417 compl_shown_match = compl_curr_match;
6418 compl_shows_dir = compl_direction;
6419
6420 // Find next match (and following matches).
6421 save_w_wrow = curwin->w_wrow;
6422 save_w_leftcol = curwin->w_leftcol;
6423 n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE);
6424
6425 // may undisplay the popup menu
6426 ins_compl_upd_pum();
6427
6428 if (n > 1) // all matches have been found
6429 compl_matches = n;
6430 compl_curr_match = compl_shown_match;
6431 compl_direction = compl_shows_dir;
6432
6433 // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
6434 // mode.
6435 if (got_int && !global_busy)
6436 {
6437 (void)vgetc();
6438 got_int = FALSE;
6439 }
6440
6441 // we found no match if the list has only the "compl_orig_text"-entry
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006442 if (is_first_match(compl_first_match->cp_next))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006443 {
6444 // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
6445 // because we couldn't expand anything at first place, but if we used
6446 // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
6447 // (such as M in M'exico) if not tried already. -- Acevedo
6448 if (compl_length > 1
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006449 || compl_status_adding()
6450 || (ctrl_x_mode_not_default()
6451 && !ctrl_x_mode_path_patterns()
6452 && !ctrl_x_mode_path_defines()))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00006453 compl_cont_status &= ~CONT_N_ADDS;
6454 }
6455
6456 if (compl_curr_match->cp_flags & CP_CONT_S_IPOS)
6457 compl_cont_status |= CONT_S_IPOS;
6458 else
6459 compl_cont_status &= ~CONT_S_IPOS;
6460
6461 ins_compl_show_statusmsg();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006462
6463 // Show the popup menu, unless we got interrupted.
6464 if (enable_pum && !compl_interrupted)
6465 show_pum(save_w_wrow, save_w_leftcol);
6466
6467 compl_was_interrupted = compl_interrupted;
6468 compl_interrupted = FALSE;
6469
6470 return OK;
6471}
6472
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00006473/*
6474 * Remove (if needed) and show the popup menu
6475 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006476 static void
6477show_pum(int prev_w_wrow, int prev_w_leftcol)
6478{
6479 // RedrawingDisabled may be set when invoked through complete().
Bram Moolenaar79cdf022023-05-20 14:07:00 +01006480 int save_RedrawingDisabled = RedrawingDisabled;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006481 RedrawingDisabled = 0;
6482
6483 // If the cursor moved or the display scrolled we need to remove the pum
6484 // first.
6485 setcursor();
6486 if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol)
6487 ins_compl_del_pum();
6488
6489 ins_compl_show_pum();
6490 setcursor();
Bram Moolenaar79cdf022023-05-20 14:07:00 +01006491
6492 RedrawingDisabled = save_RedrawingDisabled;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006493}
6494
6495/*
6496 * Looks in the first "len" chars. of "src" for search-metachars.
6497 * If dest is not NULL the chars. are copied there quoting (with
6498 * a backslash) the metachars, and dest would be NUL terminated.
6499 * Returns the length (needed) of dest
6500 */
6501 static unsigned
6502quote_meta(char_u *dest, char_u *src, int len)
6503{
6504 unsigned m = (unsigned)len + 1; // one extra for the NUL
6505
6506 for ( ; --len >= 0; src++)
6507 {
6508 switch (*src)
6509 {
6510 case '.':
6511 case '*':
6512 case '[':
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006513 if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006514 break;
6515 // FALLTHROUGH
6516 case '~':
Bram Moolenaarf4e20992020-12-21 19:59:08 +01006517 if (!magic_isset()) // quote these only if magic is set
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006518 break;
6519 // FALLTHROUGH
6520 case '\\':
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00006521 if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006522 break;
6523 // FALLTHROUGH
6524 case '^': // currently it's not needed.
6525 case '$':
6526 m++;
6527 if (dest != NULL)
6528 *dest++ = '\\';
6529 break;
6530 }
6531 if (dest != NULL)
6532 *dest++ = *src;
6533 // Copy remaining bytes of a multibyte character.
6534 if (has_mbyte)
6535 {
glepnir19e1dd62025-05-08 22:50:38 +02006536 int mb_len = (*mb_ptr2len)(src) - 1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006537 if (mb_len > 0 && len >= mb_len)
glepnir19e1dd62025-05-08 22:50:38 +02006538 for (int i = 0; i < mb_len; ++i)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006539 {
6540 --len;
6541 ++src;
6542 if (dest != NULL)
6543 *dest++ = *src;
6544 }
6545 }
6546 }
6547 if (dest != NULL)
6548 *dest = NUL;
6549
6550 return m;
6551}
6552
Bram Moolenaare2c453d2019-08-21 14:37:09 +02006553#if defined(EXITFREE) || defined(PROTO)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006554 void
6555free_insexpand_stuff(void)
6556{
John Marriott5e6ea922024-11-23 14:01:57 +01006557 VIM_CLEAR_STRING(compl_orig_text);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00006558# ifdef FEAT_EVAL
6559 free_callback(&cfu_cb);
6560 free_callback(&ofu_cb);
6561 free_callback(&tsrfu_cb);
6562# endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006563}
Bram Moolenaare2c453d2019-08-21 14:37:09 +02006564#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006565
Bram Moolenaare2c453d2019-08-21 14:37:09 +02006566#ifdef FEAT_SPELL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006567/*
6568 * Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly
6569 * spelled word, if there is one.
6570 */
6571 static void
6572spell_back_to_badword(void)
6573{
6574 pos_T tpos = curwin->w_cursor;
6575
Christ van Willegen - van Noort8e4c4c72024-05-17 18:49:27 +02006576 spell_bad_len = spell_move_to(curwin, BACKWARD, SMT_ALL, TRUE, NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01006577 if (curwin->w_cursor.col != tpos.col)
6578 start_arrow(&tpos);
6579}
Bram Moolenaare2c453d2019-08-21 14:37:09 +02006580#endif
Girish Palyacbe53192025-04-14 22:13:15 +02006581
6582/*
6583 * Reset the info associated with completion sources.
6584 */
6585 static void
Girish Palya0ac1eb32025-04-16 20:18:33 +02006586cpt_sources_clear(void)
Girish Palyacbe53192025-04-14 22:13:15 +02006587{
Girish Palya0ac1eb32025-04-16 20:18:33 +02006588 VIM_CLEAR(cpt_sources_array);
6589 cpt_sources_index = -1;
6590 cpt_sources_count = 0;
Girish Palyacbe53192025-04-14 22:13:15 +02006591}
6592
6593/*
6594 * Initialize the info associated with completion sources.
6595 */
6596 static int
Girish Palya0ac1eb32025-04-16 20:18:33 +02006597cpt_sources_init(void)
Girish Palyacbe53192025-04-14 22:13:15 +02006598{
Girish Palya0ac1eb32025-04-16 20:18:33 +02006599 char_u buf[LSIZE];
6600 int slen;
Girish Palyacbe53192025-04-14 22:13:15 +02006601 int count = 0;
Girish Palya0ac1eb32025-04-16 20:18:33 +02006602 char_u *p;
Girish Palyacbe53192025-04-14 22:13:15 +02006603
Girish Palya0ac1eb32025-04-16 20:18:33 +02006604 for (p = curbuf->b_p_cpt; *p;)
Girish Palyacbe53192025-04-14 22:13:15 +02006605 {
6606 while (*p == ',' || *p == ' ') // Skip delimiters
6607 p++;
6608 if (*p) // If not end of string, count this segment
6609 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02006610 (void)copy_option_part(&p, buf, LSIZE, ","); // Advance p
Girish Palyacbe53192025-04-14 22:13:15 +02006611 count++;
Girish Palyacbe53192025-04-14 22:13:15 +02006612 }
6613 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02006614 cpt_sources_clear();
6615 cpt_sources_count = count;
Girish Palyacbe53192025-04-14 22:13:15 +02006616 if (count > 0)
6617 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02006618 cpt_sources_array = ALLOC_CLEAR_MULT(cpt_source_T, count);
6619 if (cpt_sources_array == NULL)
Girish Palyacbe53192025-04-14 22:13:15 +02006620 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02006621 cpt_sources_count = 0;
Girish Palyacbe53192025-04-14 22:13:15 +02006622 return FAIL;
6623 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02006624 count = 0;
6625 for (p = curbuf->b_p_cpt; *p;)
6626 {
6627 while (*p == ',' || *p == ' ') // Skip delimiters
6628 p++;
6629 if (*p) // If not end of string, count this segment
6630 {
6631 char_u *t;
6632
6633 vim_memset(buf, 0, LSIZE);
6634 slen = copy_option_part(&p, buf, LSIZE, ","); // Advance p
6635 if (slen > 0 && (t = vim_strchr(buf, '^')) != NULL)
6636 cpt_sources_array[count].max_matches = atoi((char *)t + 1);
6637 count++;
6638 }
6639 }
Girish Palyacbe53192025-04-14 22:13:15 +02006640 }
6641 return OK;
6642}
6643
6644/*
6645 * Return TRUE if any of the completion sources have 'refresh' set to 'always'.
6646 */
6647 static int
6648is_cpt_func_refresh_always(void)
6649{
6650#ifdef FEAT_COMPL_FUNC
glepnir19e1dd62025-05-08 22:50:38 +02006651 for (int i = 0; i < cpt_sources_count; i++)
Girish Palya0ac1eb32025-04-16 20:18:33 +02006652 if (cpt_sources_array[i].refresh_always)
Girish Palyacbe53192025-04-14 22:13:15 +02006653 return TRUE;
6654#endif
6655 return FALSE;
6656}
6657
6658/*
6659 * Make the completion list non-cyclic.
6660 */
6661#ifdef FEAT_COMPL_FUNC
6662 static void
6663ins_compl_make_linear(void)
6664{
6665 compl_T *m;
6666
6667 if (compl_first_match == NULL || compl_first_match->cp_prev == NULL)
6668 return;
6669 m = compl_first_match->cp_prev;
6670 m->cp_next = NULL;
6671 compl_first_match->cp_prev = NULL;
6672}
6673#endif
6674
6675/*
6676 * Remove the matches linked to the current completion source (as indicated by
Girish Palya0ac1eb32025-04-16 20:18:33 +02006677 * cpt_sources_index) from the completion list.
Girish Palyacbe53192025-04-14 22:13:15 +02006678 */
6679#ifdef FEAT_COMPL_FUNC
6680 static compl_T *
6681remove_old_matches(void)
6682{
6683 compl_T *sublist_start = NULL, *sublist_end = NULL, *insert_at = NULL;
6684 compl_T *current, *next;
Girish Palya0ac1eb32025-04-16 20:18:33 +02006685 int compl_shown_removed = FALSE;
6686 int forward = (compl_first_match->cp_cpt_source_idx < 0);
6687
6688 compl_direction = forward ? FORWARD : BACKWARD;
6689 compl_shows_dir = compl_direction;
Girish Palyacbe53192025-04-14 22:13:15 +02006690
6691 // Identify the sublist of old matches that needs removal
6692 for (current = compl_first_match; current != NULL; current = current->cp_next)
6693 {
Girish Palya0ac1eb32025-04-16 20:18:33 +02006694 if (current->cp_cpt_source_idx < cpt_sources_index &&
6695 (forward || (!forward && !insert_at)))
Girish Palyacbe53192025-04-14 22:13:15 +02006696 insert_at = current;
6697
Girish Palya0ac1eb32025-04-16 20:18:33 +02006698 if (current->cp_cpt_source_idx == cpt_sources_index)
Girish Palyacbe53192025-04-14 22:13:15 +02006699 {
6700 if (!sublist_start)
6701 sublist_start = current;
6702 sublist_end = current;
6703 if (!compl_shown_removed && compl_shown_match == current)
6704 compl_shown_removed = TRUE;
6705 }
6706
Girish Palya0ac1eb32025-04-16 20:18:33 +02006707 if ((forward && current->cp_cpt_source_idx > cpt_sources_index)
6708 || (!forward && insert_at))
Girish Palyacbe53192025-04-14 22:13:15 +02006709 break;
6710 }
6711
6712 // Re-assign compl_shown_match if necessary
6713 if (compl_shown_removed)
6714 {
6715 if (forward)
6716 compl_shown_match = compl_first_match;
6717 else
6718 { // Last node will have the prefix that is being completed
Girish Palya0ac1eb32025-04-16 20:18:33 +02006719 for (current = compl_first_match; current->cp_next != NULL;
6720 current = current->cp_next)
Girish Palyacbe53192025-04-14 22:13:15 +02006721 ;
6722 compl_shown_match = current;
6723 }
6724 }
6725
6726 if (!sublist_start) // No nodes to remove
6727 return insert_at;
6728
6729 // Update links to remove sublist
6730 if (sublist_start->cp_prev)
6731 sublist_start->cp_prev->cp_next = sublist_end->cp_next;
6732 else
6733 compl_first_match = sublist_end->cp_next;
6734
6735 if (sublist_end->cp_next)
6736 sublist_end->cp_next->cp_prev = sublist_start->cp_prev;
6737
6738 // Free all nodes in the sublist
6739 sublist_end->cp_next = NULL;
6740 for (current = sublist_start; current != NULL; current = next)
6741 {
6742 next = current->cp_next;
6743 ins_compl_item_free(current);
6744 }
6745
6746 return insert_at;
6747}
6748#endif
6749
6750/*
6751 * Retrieve completion matches using the callback function "cb" and store the
6752 * 'refresh:always' flag.
6753 */
6754#ifdef FEAT_COMPL_FUNC
6755 static void
6756get_cpt_func_completion_matches(callback_T *cb UNUSED)
6757{
glepnir19e1dd62025-05-08 22:50:38 +02006758 int ret;
6759 int startcol;
Girish Palyacbe53192025-04-14 22:13:15 +02006760
6761 VIM_CLEAR_STRING(cpt_compl_pattern);
6762 ret = get_userdefined_compl_info(curwin->w_cursor.col, cb, &startcol);
6763 if (ret == FAIL && startcol == -3)
Girish Palya0ac1eb32025-04-16 20:18:33 +02006764 cpt_sources_array[cpt_sources_index].refresh_always = FALSE;
Girish Palyacbe53192025-04-14 22:13:15 +02006765 else if (ret == OK)
6766 {
6767 expand_by_function(0, cpt_compl_pattern.string, cb);
Girish Palya0ac1eb32025-04-16 20:18:33 +02006768 cpt_sources_array[cpt_sources_index].refresh_always =
6769 compl_opt_refresh_always;
Girish Palyacbe53192025-04-14 22:13:15 +02006770 compl_opt_refresh_always = FALSE;
6771 }
6772}
6773#endif
6774
6775/*
6776 * Retrieve completion matches from functions in the 'cpt' option where the
6777 * 'refresh:always' flag is set.
6778 */
6779 static void
6780cpt_compl_refresh(void)
6781{
6782#ifdef FEAT_COMPL_FUNC
6783 char_u *cpt;
6784 char_u *p;
Christian Brabandtd2079cf2025-04-15 18:10:26 +02006785 callback_T *cb = NULL;
Girish Palyacbe53192025-04-14 22:13:15 +02006786
6787 // Make the completion list linear (non-cyclic)
6788 ins_compl_make_linear();
6789 // Make a copy of 'cpt' in case the buffer gets wiped out
6790 cpt = vim_strsave(curbuf->b_p_cpt);
Girish Palya0ac1eb32025-04-16 20:18:33 +02006791 strip_caret_numbers_in_place(cpt);
Girish Palyacbe53192025-04-14 22:13:15 +02006792
Girish Palya0ac1eb32025-04-16 20:18:33 +02006793 cpt_sources_index = 0;
6794 for (p = cpt; *p; cpt_sources_index++)
Girish Palyacbe53192025-04-14 22:13:15 +02006795 {
6796 while (*p == ',' || *p == ' ') // Skip delimiters
6797 p++;
6798
Girish Palya0ac1eb32025-04-16 20:18:33 +02006799 if (cpt_sources_array[cpt_sources_index].refresh_always)
Girish Palyacbe53192025-04-14 22:13:15 +02006800 {
6801 if (*p == 'o')
6802 cb = &curbuf->b_ofu_cb;
6803 else if (*p == 'f')
6804 cb = (*(p + 1) != ',' && *(p + 1) != NUL)
6805 ? get_cpt_func_callback(p + 1) : &curbuf->b_cfu_cb;
6806 if (cb)
6807 {
6808 compl_curr_match = remove_old_matches();
6809 get_cpt_func_completion_matches(cb);
6810 }
6811 }
6812
6813 copy_option_part(&p, IObuff, IOSIZE, ","); // Advance p
6814 }
Girish Palya0ac1eb32025-04-16 20:18:33 +02006815 cpt_sources_index = -1;
Girish Palyacbe53192025-04-14 22:13:15 +02006816
6817 vim_free(cpt);
6818 // Make the list cyclic
6819 compl_matches = ins_compl_make_cyclic();
6820#endif
6821}