blob: c22eb6dc2f543b36501ae1f30dd0baa924e6c710 [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[] = {
69 "keyword",
70 "ctrl_x",
zeertzjq27fef592021-10-03 12:01:27 +010071 "scroll",
Bram Moolenaar7591bb32019-03-30 13:53:47 +010072 "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"
zeertzjqdca29d92021-08-31 19:12:51 +020085 "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;
Bram Moolenaar73655cf2019-04-06 13:45:55 +020098 char_u *cp_str; // matched text
Bram Moolenaar73655cf2019-04-06 13:45:55 +020099 char_u *(cp_text[CPT_COUNT]); // text for the menu
Bram Moolenaarab782c52020-01-04 19:00:11 +0100100#ifdef FEAT_EVAL
Bram Moolenaar08928322020-01-04 14:32:48 +0100101 typval_T cp_user_data;
Bram Moolenaarab782c52020-01-04 19:00:11 +0100102#endif
glepnir0fe17f82024-10-08 22:26:44 +0200103 char_u *cp_fname; // file containing the match, allocated when
104 // cp_flags has CP_FREE_FNAME
105 int cp_flags; // CP_ values
106 int cp_number; // sequence number
107 int cp_score; // fuzzy match score
glepnir7baa0142024-10-09 20:19:25 +0200108 int cp_user_abbr_hlattr; // highlight attribute for abbr
glepnir0fe17f82024-10-08 22:26:44 +0200109 int cp_user_kind_hlattr; // highlight attribute for kind
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100110};
111
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200112// values for cp_flags
113# define CP_ORIGINAL_TEXT 1 // the original text when the expansion begun
114# define CP_FREE_FNAME 2 // cp_fname is allocated
115# define CP_CONT_S_IPOS 4 // use CONT_S_IPOS for compl_cont_status
116# define CP_EQUAL 8 // ins_compl_equal() always returns TRUE
117# define CP_ICASE 16 // ins_compl_equal() ignores case
Bram Moolenaar440cf092021-04-03 20:13:30 +0200118# define CP_FAST 32 // use fast_breakcheck instead of ui_breakcheck
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100119
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100120/*
121 * All the current matches are stored in a list.
122 * "compl_first_match" points to the start of the list.
123 * "compl_curr_match" points to the currently selected entry.
124 * "compl_shown_match" is different from compl_curr_match during
125 * ins_compl_get_exp().
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000126 * "compl_old_match" points to previous "compl_curr_match".
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100127 */
128static compl_T *compl_first_match = NULL;
129static compl_T *compl_curr_match = NULL;
130static compl_T *compl_shown_match = NULL;
131static compl_T *compl_old_match = NULL;
132
133// After using a cursor key <Enter> selects a match in the popup menu,
134// otherwise it inserts a line break.
135static int compl_enter_selects = FALSE;
136
137// When "compl_leader" is not NULL only matches that start with this string
138// are used.
139static char_u *compl_leader = NULL;
140
141static int compl_get_longest = FALSE; // put longest common string
142 // in compl_leader
143
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100144// Selected one of the matches. When FALSE the match was edited or using the
145// longest common string.
146static int compl_used_match;
147
148// didn't finish finding completions.
149static int compl_was_interrupted = FALSE;
150
151// Set when character typed while looking for matches and it means we should
152// stop looking for matches.
153static int compl_interrupted = FALSE;
154
155static int compl_restarting = FALSE; // don't insert match
156
157// When the first completion is done "compl_started" is set. When it's
158// FALSE the word to be completed must be located.
159static int compl_started = FALSE;
160
161// Which Ctrl-X mode are we in?
162static int ctrl_x_mode = CTRL_X_NORMAL;
163
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000164static int compl_matches = 0; // number of completion matches
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100165static char_u *compl_pattern = NULL;
John Marriott8c85a2a2024-05-20 19:18:26 +0200166static size_t compl_patternlen = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100167static int compl_direction = FORWARD;
168static int compl_shows_dir = FORWARD;
169static int compl_pending = 0; // > 1 for postponed CTRL-N
170static pos_T compl_startpos;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000171// Length in bytes of the text being completed (this is deleted to be replaced
172// by the match.)
173static int compl_length = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100174static colnr_T compl_col = 0; // column where the text starts
175 // that is being completed
176static char_u *compl_orig_text = NULL; // text as it was before
177 // completion started
178static int compl_cont_mode = 0;
179static expand_T compl_xp;
180
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000181// List of flags for method of completion.
182static int compl_cont_status = 0;
183# define CONT_ADDING 1 // "normal" or "adding" expansion
184# define CONT_INTRPT (2 + 4) // a ^X interrupted the current expansion
185 // it's set only iff N_ADDS is set
186# define CONT_N_ADDS 4 // next ^X<> will add-new or expand-current
187# define CONT_S_IPOS 8 // next ^X<> will set initial_pos?
188 // if so, word-wise-expansion will set SOL
189# define CONT_SOL 16 // pattern includes start of line, just for
190 // word-wise expansion, not set for ^X^L
191# define CONT_LOCAL 32 // for ctrl_x_mode 0, ^X^P/^X^N do a local
192 // expansion, (eg use complete=.)
193
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100194static int compl_opt_refresh_always = FALSE;
195static int compl_opt_suppress_empty = FALSE;
196
glepnira218cc62024-06-03 19:32:39 +0200197static int compl_selected_item = -1;
198
glepnir8159fb12024-07-17 20:32:54 +0200199static int *compl_fuzzy_scores;
200
glepnir38f99a12024-08-23 18:31:06 +0200201static 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_hlattr, int user_kind_hlattr);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100202static void ins_compl_longest_match(compl_T *match);
203static void ins_compl_del_pum(void);
204static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir);
205static char_u *find_line_end(char_u *ptr);
206static void ins_compl_free(void);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100207static int ins_compl_need_restart(void);
208static void ins_compl_new_leader(void);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000209static int get_compl_len(void);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100210static void ins_compl_restart(void);
211static void ins_compl_set_original_text(char_u *str);
212static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg);
213# if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
214static void ins_compl_add_list(list_T *list);
215static void ins_compl_add_dict(dict_T *dict);
216# endif
217static int ins_compl_key2dir(int c);
218static int ins_compl_pum_key(int c);
219static int ins_compl_key2count(int c);
220static void show_pum(int prev_w_wrow, int prev_w_leftcol);
221static unsigned quote_meta(char_u *dest, char_u *str, int len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100222
223#ifdef FEAT_SPELL
224static void spell_back_to_badword(void);
225static int spell_bad_len = 0; // length of located bad word
226#endif
227
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100228/*
229 * CTRL-X pressed in Insert mode.
230 */
231 void
232ins_ctrl_x(void)
233{
zeertzjqdca29d92021-08-31 19:12:51 +0200234 if (!ctrl_x_mode_cmdline())
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100235 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000236 // if the next ^X<> won't ADD nothing, then reset compl_cont_status
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100237 if (compl_cont_status & CONT_N_ADDS)
238 compl_cont_status |= CONT_INTRPT;
239 else
240 compl_cont_status = 0;
241 // We're not sure which CTRL-X mode it will be yet
242 ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
243 edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
244 edit_submode_pre = NULL;
245 showmode();
246 }
zeertzjqdca29d92021-08-31 19:12:51 +0200247 else
248 // CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X
249 // CTRL-V look like CTRL-N
250 ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +0100251
LemonBoy2bf52dd2022-04-09 18:17:34 +0100252 may_trigger_modechanged();
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100253}
254
255/*
256 * Functions to check the current CTRL-X mode.
257 */
Bram Moolenaarebfec1c2023-01-22 21:14:53 +0000258int ctrl_x_mode_none(void)
259 { return ctrl_x_mode == 0; }
260int ctrl_x_mode_normal(void)
261 { return ctrl_x_mode == CTRL_X_NORMAL; }
262int ctrl_x_mode_scroll(void)
263 { return ctrl_x_mode == CTRL_X_SCROLL; }
264int ctrl_x_mode_whole_line(void)
265 { return ctrl_x_mode == CTRL_X_WHOLE_LINE; }
266int ctrl_x_mode_files(void)
267 { return ctrl_x_mode == CTRL_X_FILES; }
268int ctrl_x_mode_tags(void)
269 { return ctrl_x_mode == CTRL_X_TAGS; }
270int ctrl_x_mode_path_patterns(void)
271 { return ctrl_x_mode == CTRL_X_PATH_PATTERNS; }
272int ctrl_x_mode_path_defines(void)
273 { return ctrl_x_mode == CTRL_X_PATH_DEFINES; }
274int ctrl_x_mode_dictionary(void)
275 { return ctrl_x_mode == CTRL_X_DICTIONARY; }
276int ctrl_x_mode_thesaurus(void)
277 { return ctrl_x_mode == CTRL_X_THESAURUS; }
278int ctrl_x_mode_cmdline(void)
279 { return ctrl_x_mode == CTRL_X_CMDLINE
zeertzjqdca29d92021-08-31 19:12:51 +0200280 || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X; }
Bram Moolenaarebfec1c2023-01-22 21:14:53 +0000281int ctrl_x_mode_function(void)
282 { return ctrl_x_mode == CTRL_X_FUNCTION; }
283int ctrl_x_mode_omni(void)
284 { return ctrl_x_mode == CTRL_X_OMNI; }
285int ctrl_x_mode_spell(void)
286 { return ctrl_x_mode == CTRL_X_SPELL; }
287static int ctrl_x_mode_eval(void)
288 { return ctrl_x_mode == CTRL_X_EVAL; }
289int ctrl_x_mode_line_or_eval(void)
290 { return ctrl_x_mode == CTRL_X_WHOLE_LINE || ctrl_x_mode == CTRL_X_EVAL; }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100291
292/*
293 * Whether other than default completion has been selected.
294 */
295 int
296ctrl_x_mode_not_default(void)
297{
298 return ctrl_x_mode != CTRL_X_NORMAL;
299}
300
301/*
zeertzjqdca29d92021-08-31 19:12:51 +0200302 * Whether CTRL-X was typed without a following character,
303 * not including when in CTRL-X CTRL-V mode.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100304 */
305 int
306ctrl_x_mode_not_defined_yet(void)
307{
308 return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET;
309}
310
311/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000312 * Return TRUE if currently in "normal" or "adding" insert completion matches
313 * state
314 */
315 int
316compl_status_adding(void)
317{
318 return compl_cont_status & CONT_ADDING;
319}
320
321/*
322 * Return TRUE if the completion pattern includes start of line, just for
323 * word-wise expansion.
324 */
325 int
326compl_status_sol(void)
327{
328 return compl_cont_status & CONT_SOL;
329}
330
331/*
332 * Return TRUE if ^X^P/^X^N will do a local completion (i.e. use complete=.)
333 */
334 int
335compl_status_local(void)
336{
337 return compl_cont_status & CONT_LOCAL;
338}
339
340/*
341 * Clear the completion status flags
342 */
343 void
344compl_status_clear(void)
345{
346 compl_cont_status = 0;
347}
348
349/*
350 * Return TRUE if completion is using the forward direction matches
351 */
352 static int
353compl_dir_forward(void)
354{
355 return compl_direction == FORWARD;
356}
357
358/*
359 * Return TRUE if currently showing forward completion matches
360 */
361 static int
362compl_shows_dir_forward(void)
363{
364 return compl_shows_dir == FORWARD;
365}
366
367/*
368 * Return TRUE if currently showing backward completion matches
369 */
370 static int
371compl_shows_dir_backward(void)
372{
373 return compl_shows_dir == BACKWARD;
374}
375
376/*
377 * Return TRUE if the 'dictionary' or 'thesaurus' option can be used.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100378 */
379 int
380has_compl_option(int dict_opt)
381{
382 if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL
Bram Moolenaare2c453d2019-08-21 14:37:09 +0200383#ifdef FEAT_SPELL
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100384 && !curwin->w_p_spell
Bram Moolenaare2c453d2019-08-21 14:37:09 +0200385#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100386 )
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +0100387 : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL
388#ifdef FEAT_COMPL_FUNC
Bram Moolenaarf4d8b762021-10-17 14:13:09 +0100389 && *curbuf->b_p_tsrfu == NUL && *p_tsrfu == NUL
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +0100390#endif
391 ))
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100392 {
393 ctrl_x_mode = CTRL_X_NORMAL;
394 edit_submode = NULL;
395 msg_attr(dict_opt ? _("'dictionary' option is empty")
396 : _("'thesaurus' option is empty"),
397 HL_ATTR(HLF_E));
Bram Moolenaar28ee8922020-10-28 20:20:00 +0100398 if (emsg_silent == 0 && !in_assert_fails)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100399 {
400 vim_beep(BO_COMPL);
401 setcursor();
402 out_flush();
403#ifdef FEAT_EVAL
404 if (!get_vim_var_nr(VV_TESTING))
405#endif
Bram Moolenaareda1da02019-11-17 17:06:33 +0100406 ui_delay(2004L, FALSE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100407 }
408 return FALSE;
409 }
410 return TRUE;
411}
412
413/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000414 * Is the character "c" a valid key to go to or keep us in CTRL-X mode?
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100415 * This depends on the current mode.
416 */
417 int
418vim_is_ctrl_x_key(int c)
419{
420 // Always allow ^R - let its results then be checked
421 if (c == Ctrl_R)
422 return TRUE;
423
424 // Accept <PageUp> and <PageDown> if the popup menu is visible.
425 if (ins_compl_pum_key(c))
426 return TRUE;
427
428 switch (ctrl_x_mode)
429 {
430 case 0: // Not in any CTRL-X mode
431 return (c == Ctrl_N || c == Ctrl_P || c == Ctrl_X);
432 case CTRL_X_NOT_DEFINED_YET:
zeertzjqdca29d92021-08-31 19:12:51 +0200433 case CTRL_X_CMDLINE_CTRL_X:
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100434 return ( c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E
435 || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB
436 || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P
437 || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V
438 || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O
zeertzjqdca29d92021-08-31 19:12:51 +0200439 || c == Ctrl_S || c == Ctrl_K || c == 's'
440 || c == Ctrl_Z);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100441 case CTRL_X_SCROLL:
442 return (c == Ctrl_Y || c == Ctrl_E);
443 case CTRL_X_WHOLE_LINE:
444 return (c == Ctrl_L || c == Ctrl_P || c == Ctrl_N);
445 case CTRL_X_FILES:
446 return (c == Ctrl_F || c == Ctrl_P || c == Ctrl_N);
447 case CTRL_X_DICTIONARY:
448 return (c == Ctrl_K || c == Ctrl_P || c == Ctrl_N);
449 case CTRL_X_THESAURUS:
450 return (c == Ctrl_T || c == Ctrl_P || c == Ctrl_N);
451 case CTRL_X_TAGS:
452 return (c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N);
453#ifdef FEAT_FIND_ID
454 case CTRL_X_PATH_PATTERNS:
455 return (c == Ctrl_P || c == Ctrl_N);
456 case CTRL_X_PATH_DEFINES:
457 return (c == Ctrl_D || c == Ctrl_P || c == Ctrl_N);
458#endif
459 case CTRL_X_CMDLINE:
460 return (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N
461 || c == Ctrl_X);
462#ifdef FEAT_COMPL_FUNC
463 case CTRL_X_FUNCTION:
464 return (c == Ctrl_U || c == Ctrl_P || c == Ctrl_N);
465 case CTRL_X_OMNI:
466 return (c == Ctrl_O || c == Ctrl_P || c == Ctrl_N);
467#endif
468 case CTRL_X_SPELL:
469 return (c == Ctrl_S || c == Ctrl_P || c == Ctrl_N);
470 case CTRL_X_EVAL:
471 return (c == Ctrl_P || c == Ctrl_N);
472 }
473 internal_error("vim_is_ctrl_x_key()");
474 return FALSE;
475}
476
477/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000478 * Return TRUE if "match" is the original text when the completion began.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000479 */
480 static int
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000481match_at_original_text(compl_T *match)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000482{
483 return match->cp_flags & CP_ORIGINAL_TEXT;
484}
485
486/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000487 * Returns TRUE if "match" is the first match in the completion list.
488 */
489 static int
490is_first_match(compl_T *match)
491{
492 return match == compl_first_match;
493}
494
495/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100496 * Return TRUE when character "c" is part of the item currently being
497 * completed. Used to decide whether to abandon complete mode when the menu
498 * is visible.
499 */
500 int
501ins_compl_accept_char(int c)
502{
503 if (ctrl_x_mode & CTRL_X_WANT_IDENT)
504 // When expanding an identifier only accept identifier chars.
505 return vim_isIDc(c);
506
507 switch (ctrl_x_mode)
508 {
509 case CTRL_X_FILES:
510 // When expanding file name only accept file name chars. But not
511 // path separators, so that "proto/<Tab>" expands files in
512 // "proto", not "proto/" as a whole
513 return vim_isfilec(c) && !vim_ispathsep(c);
514
515 case CTRL_X_CMDLINE:
zeertzjqdca29d92021-08-31 19:12:51 +0200516 case CTRL_X_CMDLINE_CTRL_X:
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100517 case CTRL_X_OMNI:
518 // Command line and Omni completion can work with just about any
519 // printable character, but do stop at white space.
520 return vim_isprintc(c) && !VIM_ISWHITE(c);
521
522 case CTRL_X_WHOLE_LINE:
523 // For while line completion a space can be part of the line.
524 return vim_isprintc(c);
525 }
526 return vim_iswordc(c);
527}
528
529/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000530 * Get the completed text by inferring the case of the originally typed text.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100531 * If the result is in allocated memory "tofree" is set to it.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000532 */
533 static char_u *
534ins_compl_infercase_gettext(
535 char_u *str,
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100536 int char_len,
537 int compl_char_len,
538 int min_len,
539 char_u **tofree)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000540{
541 int *wca; // Wide character array.
542 char_u *p;
543 int i, c;
544 int has_lower = FALSE;
545 int was_letter = FALSE;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100546 garray_T gap;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000547
548 IObuff[0] = NUL;
549
550 // Allocate wide character array for the completion and fill it.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100551 wca = ALLOC_MULT(int, char_len);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000552 if (wca == NULL)
553 return IObuff;
554
555 p = str;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100556 for (i = 0; i < char_len; ++i)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000557 if (has_mbyte)
558 wca[i] = mb_ptr2char_adv(&p);
559 else
560 wca[i] = *(p++);
561
562 // Rule 1: Were any chars converted to lower?
563 p = compl_orig_text;
564 for (i = 0; i < min_len; ++i)
565 {
566 if (has_mbyte)
567 c = mb_ptr2char_adv(&p);
568 else
569 c = *(p++);
570 if (MB_ISLOWER(c))
571 {
572 has_lower = TRUE;
573 if (MB_ISUPPER(wca[i]))
574 {
575 // Rule 1 is satisfied.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100576 for (i = compl_char_len; i < char_len; ++i)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000577 wca[i] = MB_TOLOWER(wca[i]);
578 break;
579 }
580 }
581 }
582
583 // Rule 2: No lower case, 2nd consecutive letter converted to
584 // upper case.
585 if (!has_lower)
586 {
587 p = compl_orig_text;
588 for (i = 0; i < min_len; ++i)
589 {
590 if (has_mbyte)
591 c = mb_ptr2char_adv(&p);
592 else
593 c = *(p++);
594 if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i]))
595 {
596 // Rule 2 is satisfied.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100597 for (i = compl_char_len; i < char_len; ++i)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000598 wca[i] = MB_TOUPPER(wca[i]);
599 break;
600 }
601 was_letter = MB_ISLOWER(c) || MB_ISUPPER(c);
602 }
603 }
604
605 // Copy the original case of the part we typed.
606 p = compl_orig_text;
607 for (i = 0; i < min_len; ++i)
608 {
609 if (has_mbyte)
610 c = mb_ptr2char_adv(&p);
611 else
612 c = *(p++);
613 if (MB_ISLOWER(c))
614 wca[i] = MB_TOLOWER(wca[i]);
615 else if (MB_ISUPPER(c))
616 wca[i] = MB_TOUPPER(wca[i]);
617 }
618
619 // Generate encoding specific output from wide character array.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000620 p = IObuff;
621 i = 0;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100622 ga_init2(&gap, 1, 500);
623 while (i < char_len)
624 {
625 if (gap.ga_data != NULL)
626 {
627 if (ga_grow(&gap, 10) == FAIL)
628 {
629 ga_clear(&gap);
630 return (char_u *)"[failed]";
631 }
632 p = (char_u *)gap.ga_data + gap.ga_len;
633 if (has_mbyte)
634 gap.ga_len += (*mb_char2bytes)(wca[i++], p);
635 else
636 {
637 *p = wca[i++];
638 ++gap.ga_len;
639 }
640 }
641 else if ((p - IObuff) + 6 >= IOSIZE)
642 {
643 // Multi-byte characters can occupy up to five bytes more than
644 // ASCII characters, and we also need one byte for NUL, so when
645 // getting to six bytes from the edge of IObuff switch to using a
646 // growarray. Add the character in the next round.
647 if (ga_grow(&gap, IOSIZE) == FAIL)
zeertzjq70e566b2024-03-21 07:11:58 +0100648 {
649 vim_free(wca);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100650 return (char_u *)"[failed]";
zeertzjq70e566b2024-03-21 07:11:58 +0100651 }
Bram Moolenaarb9e71732022-07-23 06:53:08 +0100652 *p = NUL;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100653 STRCPY(gap.ga_data, IObuff);
Bram Moolenaarc7bd2f02022-07-15 20:45:20 +0100654 gap.ga_len = (int)STRLEN(IObuff);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100655 }
656 else if (has_mbyte)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000657 p += (*mb_char2bytes)(wca[i++], p);
658 else
659 *(p++) = wca[i++];
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100660 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000661 vim_free(wca);
662
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100663 if (gap.ga_data != NULL)
664 {
665 *tofree = gap.ga_data;
666 return gap.ga_data;
667 }
668
669 *p = NUL;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000670 return IObuff;
671}
672
673/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100674 * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
675 * case of the originally typed text is used, and the case of the completed
676 * text is inferred, ie this tries to work out what case you probably wanted
677 * the rest of the word to be in -- webb
678 */
679 int
680ins_compl_add_infercase(
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200681 char_u *str_arg,
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100682 int len,
683 int icase,
684 char_u *fname,
685 int dir,
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200686 int cont_s_ipos) // next ^X<> will set initial_pos
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100687{
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200688 char_u *str = str_arg;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100689 char_u *p;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100690 int char_len; // count multi-byte characters
691 int compl_char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100692 int min_len;
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200693 int flags = 0;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100694 int res;
695 char_u *tofree = NULL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100696
697 if (p_ic && curbuf->b_p_inf && len > 0)
698 {
699 // Infer case of completed part.
700
701 // Find actual length of completion.
702 if (has_mbyte)
703 {
704 p = str;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100705 char_len = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100706 while (*p != NUL)
707 {
708 MB_PTR_ADV(p);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100709 ++char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100710 }
711 }
712 else
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100713 char_len = len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100714
715 // Find actual length of original text.
716 if (has_mbyte)
717 {
718 p = compl_orig_text;
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100719 compl_char_len = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100720 while (*p != NUL)
721 {
722 MB_PTR_ADV(p);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100723 ++compl_char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100724 }
725 }
726 else
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100727 compl_char_len = compl_length;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100728
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100729 // "char_len" may be smaller than "compl_char_len" when using
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100730 // thesaurus, only use the minimum when comparing.
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100731 min_len = char_len < compl_char_len ? char_len : compl_char_len;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100732
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100733 str = ins_compl_infercase_gettext(str, char_len,
734 compl_char_len, min_len, &tofree);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100735 }
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200736 if (cont_s_ipos)
737 flags |= CP_CONT_S_IPOS;
738 if (icase)
739 flags |= CP_ICASE;
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200740
glepnir38f99a12024-08-23 18:31:06 +0200741 res = ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE, -1, -1);
Bram Moolenaarcaea6642022-07-07 19:42:04 +0100742 vim_free(tofree);
743 return res;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100744}
745
746/*
Yegappan Lakshmanan37079142022-01-08 10:38:48 +0000747 * Add a match to the list of matches. The arguments are:
748 * str - text of the match to add
749 * len - length of "str". If -1, then the length of "str" is
750 * computed.
751 * fname - file name to associate with this match.
752 * cptext - list of strings to use with this match (for abbr, menu, info
753 * and kind)
754 * user_data - user supplied data (any vim type) for this match
755 * cdir - match direction. If 0, use "compl_direction".
756 * flags_arg - match flags (cp_flags)
757 * adup - accept this match even if it is already present.
758 * If "cdir" is FORWARD, then the match is added after the current match.
759 * Otherwise, it is added before the current match.
760 *
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100761 * If the given string is already in the list of completions, then return
762 * NOTDONE, otherwise add it to the list and return OK. If there is an error,
763 * maybe because alloc() returns NULL, then FAIL is returned.
764 */
765 static int
766ins_compl_add(
767 char_u *str,
768 int len,
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100769 char_u *fname,
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100770 char_u **cptext, // extra text for popup menu or NULL
771 typval_T *user_data UNUSED, // "user_data" entry or NULL
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100772 int cdir,
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200773 int flags_arg,
glepnir508e7852024-07-25 21:39:08 +0200774 int adup, // accept duplicate match
glepnir0fe17f82024-10-08 22:26:44 +0200775 int user_abbr_hlattr,
glepnir38f99a12024-08-23 18:31:06 +0200776 int user_kind_hlattr)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100777{
778 compl_T *match;
779 int dir = (cdir == 0 ? compl_direction : cdir);
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200780 int flags = flags_arg;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100781
Bram Moolenaarceb06192021-04-04 15:05:22 +0200782 if (flags & CP_FAST)
783 fast_breakcheck();
784 else
785 ui_breakcheck();
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100786 if (got_int)
787 return FAIL;
788 if (len < 0)
789 len = (int)STRLEN(str);
790
791 // If the same match is already present, don't add it.
792 if (compl_first_match != NULL && !adup)
793 {
794 match = compl_first_match;
795 do
796 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000797 if (!match_at_original_text(match)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100798 && STRNCMP(match->cp_str, str, len) == 0
Bram Moolenaarbaefde12022-07-07 19:59:49 +0100799 && ((int)STRLEN(match->cp_str) <= len
800 || match->cp_str[len] == NUL))
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100801 return NOTDONE;
802 match = match->cp_next;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000803 } while (match != NULL && !is_first_match(match));
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100804 }
805
806 // Remove any popup menu before changing the list of matches.
807 ins_compl_del_pum();
808
809 // Allocate a new match structure.
810 // Copy the values to the new match structure.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200811 match = ALLOC_CLEAR_ONE(compl_T);
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100812 if (match == NULL)
813 return FAIL;
814 match->cp_number = -1;
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200815 if (flags & CP_ORIGINAL_TEXT)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100816 match->cp_number = 0;
817 if ((match->cp_str = vim_strnsave(str, len)) == NULL)
818 {
819 vim_free(match);
820 return FAIL;
821 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100822
823 // match-fname is:
824 // - compl_curr_match->cp_fname if it is a string equal to fname.
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200825 // - a copy of fname, CP_FREE_FNAME is set to free later THE allocated mem.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100826 // - NULL otherwise. --Acevedo
827 if (fname != NULL
828 && compl_curr_match != NULL
829 && compl_curr_match->cp_fname != NULL
830 && STRCMP(fname, compl_curr_match->cp_fname) == 0)
831 match->cp_fname = compl_curr_match->cp_fname;
832 else if (fname != NULL)
833 {
834 match->cp_fname = vim_strsave(fname);
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200835 flags |= CP_FREE_FNAME;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100836 }
837 else
838 match->cp_fname = NULL;
839 match->cp_flags = flags;
glepnir0fe17f82024-10-08 22:26:44 +0200840 match->cp_user_abbr_hlattr = user_abbr_hlattr;
glepnir38f99a12024-08-23 18:31:06 +0200841 match->cp_user_kind_hlattr = user_kind_hlattr;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100842
843 if (cptext != NULL)
844 {
845 int i;
846
847 for (i = 0; i < CPT_COUNT; ++i)
848 if (cptext[i] != NULL && *cptext[i] != NUL)
849 match->cp_text[i] = vim_strsave(cptext[i]);
850 }
Bram Moolenaarab782c52020-01-04 19:00:11 +0100851#ifdef FEAT_EVAL
Bram Moolenaar08928322020-01-04 14:32:48 +0100852 if (user_data != NULL)
853 match->cp_user_data = *user_data;
Bram Moolenaarab782c52020-01-04 19:00:11 +0100854#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100855
Yegappan Lakshmanan37079142022-01-08 10:38:48 +0000856 // Link the new match structure after (FORWARD) or before (BACKWARD) the
857 // current match in the list of matches .
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100858 if (compl_first_match == NULL)
859 match->cp_next = match->cp_prev = NULL;
860 else if (dir == FORWARD)
861 {
862 match->cp_next = compl_curr_match->cp_next;
863 match->cp_prev = compl_curr_match;
864 }
865 else // BACKWARD
866 {
867 match->cp_next = compl_curr_match;
868 match->cp_prev = compl_curr_match->cp_prev;
869 }
870 if (match->cp_next)
871 match->cp_next->cp_prev = match;
872 if (match->cp_prev)
873 match->cp_prev->cp_next = match;
874 else // if there's nothing before, it is the first match
875 compl_first_match = match;
876 compl_curr_match = match;
877
878 // Find the longest common string if still doing that.
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200879 if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100880 ins_compl_longest_match(match);
881
882 return OK;
883}
884
885/*
886 * Return TRUE if "str[len]" matches with match->cp_str, considering
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200887 * match->cp_flags.
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100888 */
889 static int
890ins_compl_equal(compl_T *match, char_u *str, int len)
891{
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200892 if (match->cp_flags & CP_EQUAL)
Bram Moolenaar73655cf2019-04-06 13:45:55 +0200893 return TRUE;
Bram Moolenaard9eefe32019-04-06 14:22:21 +0200894 if (match->cp_flags & CP_ICASE)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100895 return STRNICMP(match->cp_str, str, (size_t)len) == 0;
896 return STRNCMP(match->cp_str, str, (size_t)len) == 0;
897}
898
899/*
900 * Reduce the longest common string for match "match".
901 */
902 static void
903ins_compl_longest_match(compl_T *match)
904{
905 char_u *p, *s;
906 int c1, c2;
907 int had_match;
908
909 if (compl_leader == NULL)
910 {
911 // First match, use it as a whole.
912 compl_leader = vim_strsave(match->cp_str);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000913 if (compl_leader == NULL)
914 return;
915
916 had_match = (curwin->w_cursor.col > compl_col);
917 ins_compl_delete();
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000918 ins_bytes(compl_leader + get_compl_len());
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000919 ins_redraw(FALSE);
920
921 // When the match isn't there (to avoid matching itself) remove it
922 // again after redrawing.
923 if (!had_match)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100924 ins_compl_delete();
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100925 compl_used_match = FALSE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000926
927 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100928 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000929
930 // Reduce the text if this match differs from compl_leader.
931 p = compl_leader;
932 s = match->cp_str;
933 while (*p != NUL)
934 {
935 if (has_mbyte)
936 {
937 c1 = mb_ptr2char(p);
938 c2 = mb_ptr2char(s);
939 }
940 else
941 {
942 c1 = *p;
943 c2 = *s;
944 }
945 if ((match->cp_flags & CP_ICASE)
946 ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) : (c1 != c2))
947 break;
948 if (has_mbyte)
949 {
950 MB_PTR_ADV(p);
951 MB_PTR_ADV(s);
952 }
953 else
954 {
955 ++p;
956 ++s;
957 }
958 }
959
960 if (*p != NUL)
961 {
962 // Leader was shortened, need to change the inserted text.
963 *p = NUL;
964 had_match = (curwin->w_cursor.col > compl_col);
965 ins_compl_delete();
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +0000966 ins_bytes(compl_leader + get_compl_len());
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +0000967 ins_redraw(FALSE);
968
969 // When the match isn't there (to avoid matching itself) remove it
970 // again after redrawing.
971 if (!had_match)
972 ins_compl_delete();
973 }
974
975 compl_used_match = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100976}
977
978/*
979 * Add an array of matches to the list of matches.
980 * Frees matches[].
981 */
982 static void
983ins_compl_add_matches(
984 int num_matches,
985 char_u **matches,
986 int icase)
987{
988 int i;
989 int add_r = OK;
990 int dir = compl_direction;
991
992 for (i = 0; i < num_matches && add_r != FAIL; i++)
Bram Moolenaar08928322020-01-04 14:32:48 +0100993 if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, NULL, dir,
glepnir38f99a12024-08-23 18:31:06 +0200994 CP_FAST | (icase ? CP_ICASE : 0), FALSE, -1, -1)) == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +0100995 // if dir was BACKWARD then honor it just once
996 dir = FORWARD;
997 FreeWild(num_matches, matches);
998}
999
1000/*
1001 * Make the completion list cyclic.
1002 * Return the number of matches (excluding the original).
1003 */
1004 static int
1005ins_compl_make_cyclic(void)
1006{
1007 compl_T *match;
1008 int count = 0;
1009
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001010 if (compl_first_match == NULL)
1011 return 0;
1012
1013 // Find the end of the list.
1014 match = compl_first_match;
1015 // there's always an entry for the compl_orig_text, it doesn't count.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001016 while (match->cp_next != NULL && !is_first_match(match->cp_next))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001017 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001018 match = match->cp_next;
1019 ++count;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001020 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001021 match->cp_next = compl_first_match;
1022 compl_first_match->cp_prev = match;
1023
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001024 return count;
1025}
1026
1027/*
1028 * Return whether there currently is a shown match.
1029 */
1030 int
1031ins_compl_has_shown_match(void)
1032{
1033 return compl_shown_match == NULL
1034 || compl_shown_match != compl_shown_match->cp_next;
1035}
1036
1037/*
1038 * Return whether the shown match is long enough.
1039 */
1040 int
1041ins_compl_long_shown_match(void)
1042{
1043 return (int)STRLEN(compl_shown_match->cp_str)
1044 > curwin->w_cursor.col - compl_col;
1045}
1046
1047/*
zeertzjq529b9ad2024-06-05 20:27:06 +02001048 * Get the local or global value of 'completeopt' flags.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001049 */
zeertzjq529b9ad2024-06-05 20:27:06 +02001050 unsigned int
1051get_cot_flags(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001052{
zeertzjq529b9ad2024-06-05 20:27:06 +02001053 return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001054}
1055
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001056
1057// "compl_match_array" points the currently displayed list of entries in the
1058// popup menu. It is NULL when there is no popup menu.
1059static pumitem_T *compl_match_array = NULL;
1060static int compl_match_arraysize;
1061
1062/*
1063 * Update the screen and when there is any scrolling remove the popup menu.
1064 */
1065 static void
1066ins_compl_upd_pum(void)
1067{
1068 int h;
1069
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001070 if (compl_match_array == NULL)
1071 return;
1072
1073 h = curwin->w_cline_height;
1074 // Update the screen later, before drawing the popup menu over it.
1075 pum_call_update_screen();
1076 if (h != curwin->w_cline_height)
1077 ins_compl_del_pum();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001078}
1079
1080/*
1081 * Remove any popup menu.
1082 */
1083 static void
1084ins_compl_del_pum(void)
1085{
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001086 if (compl_match_array == NULL)
1087 return;
1088
1089 pum_undisplay();
1090 VIM_CLEAR(compl_match_array);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001091}
1092
1093/*
1094 * Return TRUE if the popup menu should be displayed.
1095 */
1096 int
1097pum_wanted(void)
1098{
1099 // 'completeopt' must contain "menu" or "menuone"
zeertzjq529b9ad2024-06-05 20:27:06 +02001100 if ((get_cot_flags() & COT_ANY_MENU) == 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001101 return FALSE;
1102
1103 // The display looks bad on a B&W display.
1104 if (t_colors < 8
1105#ifdef FEAT_GUI
1106 && !gui.in_use
1107#endif
1108 )
1109 return FALSE;
1110 return TRUE;
1111}
1112
1113/*
1114 * Return TRUE if there are two or more matches to be shown in the popup menu.
1115 * One if 'completopt' contains "menuone".
1116 */
1117 static int
1118pum_enough_matches(void)
1119{
1120 compl_T *compl;
1121 int i;
1122
1123 // Don't display the popup menu if there are no matches or there is only
1124 // one (ignoring the original text).
1125 compl = compl_first_match;
1126 i = 0;
1127 do
1128 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001129 if (compl == NULL || (!match_at_original_text(compl) && ++i == 2))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001130 break;
1131 compl = compl->cp_next;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001132 } while (!is_first_match(compl));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001133
zeertzjq529b9ad2024-06-05 20:27:06 +02001134 if (get_cot_flags() & COT_MENUONE)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001135 return (i >= 1);
1136 return (i >= 2);
1137}
1138
Bram Moolenaar3075a452021-11-17 15:51:52 +00001139#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001140/*
1141 * Allocate Dict for the completed item.
1142 * { word, abbr, menu, kind, info }
1143 */
1144 static dict_T *
1145ins_compl_dict_alloc(compl_T *match)
1146{
1147 dict_T *dict = dict_alloc_lock(VAR_FIXED);
1148
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001149 if (dict == NULL)
1150 return NULL;
1151
1152 dict_add_string(dict, "word", match->cp_str);
1153 dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]);
1154 dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
1155 dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
1156 dict_add_string(dict, "info", match->cp_text[CPT_INFO]);
1157 if (match->cp_user_data.v_type == VAR_UNKNOWN)
1158 dict_add_string(dict, "user_data", (char_u *)"");
1159 else
1160 dict_add_tv(dict, "user_data", &match->cp_user_data);
1161
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001162 return dict;
1163}
1164
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001165/*
1166 * Trigger the CompleteChanged autocmd event. Invoked each time the Insert mode
1167 * completion menu is changed.
1168 */
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001169 static void
1170trigger_complete_changed_event(int cur)
1171{
1172 dict_T *v_event;
1173 dict_T *item;
1174 static int recursive = FALSE;
Bram Moolenaar3075a452021-11-17 15:51:52 +00001175 save_v_event_T save_v_event;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001176
1177 if (recursive)
1178 return;
1179
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001180 if (cur < 0)
1181 item = dict_alloc();
1182 else
1183 item = ins_compl_dict_alloc(compl_curr_match);
1184 if (item == NULL)
1185 return;
Bram Moolenaar3075a452021-11-17 15:51:52 +00001186 v_event = get_v_event(&save_v_event);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001187 dict_add_dict(v_event, "completed_item", item);
1188 pum_set_event_info(v_event);
1189 dict_set_items_ro(v_event);
1190
1191 recursive = TRUE;
zeertzjqcfe45652022-05-27 17:26:55 +01001192 textlock++;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001193 apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, FALSE, curbuf);
zeertzjqcfe45652022-05-27 17:26:55 +01001194 textlock--;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001195 recursive = FALSE;
1196
Bram Moolenaar3075a452021-11-17 15:51:52 +00001197 restore_v_event(v_event, &save_v_event);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001198}
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001199#endif
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001200
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001201/*
glepnira218cc62024-06-03 19:32:39 +02001202 * pumitem qsort compare func
1203 */
1204 static int
zeertzjq8e567472024-06-14 20:04:42 +02001205ins_compl_fuzzy_cmp(const void *a, const void *b)
glepnira218cc62024-06-03 19:32:39 +02001206{
1207 const int sa = (*(pumitem_T *)a).pum_score;
1208 const int sb = (*(pumitem_T *)b).pum_score;
zeertzjq8e567472024-06-14 20:04:42 +02001209 const int ia = (*(pumitem_T *)a).pum_idx;
1210 const int ib = (*(pumitem_T *)b).pum_idx;
1211 return sa == sb ? (ia == ib ? 0 : (ia < ib ? -1 : 1)) : (sa < sb ? 1 : -1);
glepnira218cc62024-06-03 19:32:39 +02001212}
1213
1214/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001215 * Build a popup menu to show the completion matches.
1216 * Returns the popup menu entry that should be selected. Returns -1 if nothing
1217 * should be selected.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001218 */
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001219 static int
1220ins_compl_build_pum(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001221{
1222 compl_T *compl;
1223 compl_T *shown_compl = NULL;
1224 int did_find_shown_match = FALSE;
1225 int shown_match_ok = FALSE;
1226 int i;
1227 int cur = -1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001228 int lead_len = 0;
glepnira218cc62024-06-03 19:32:39 +02001229 int max_fuzzy_score = 0;
zeertzjqaa925ee2024-06-09 18:24:05 +02001230 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02001231 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
1232 int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001233
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001234 // Need to build the popup menu list.
1235 compl_match_arraysize = 0;
1236 compl = compl_first_match;
1237 if (compl_leader != NULL)
1238 lead_len = (int)STRLEN(compl_leader);
1239
1240 do
1241 {
zeertzjq551d8c32024-06-05 19:53:32 +02001242 // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
1243 // set the cp_score for later comparisons.
glepnira218cc62024-06-03 19:32:39 +02001244 if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0)
1245 compl->cp_score = fuzzy_match_str(compl->cp_str, compl_leader);
1246
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001247 if (!match_at_original_text(compl)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001248 && (compl_leader == NULL
glepnira218cc62024-06-03 19:32:39 +02001249 || ins_compl_equal(compl, compl_leader, lead_len)
1250 || (compl_fuzzy_match && compl->cp_score > 0)))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001251 ++compl_match_arraysize;
1252 compl = compl->cp_next;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001253 } while (compl != NULL && !is_first_match(compl));
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001254
1255 if (compl_match_arraysize == 0)
1256 return -1;
1257
1258 compl_match_array = ALLOC_CLEAR_MULT(pumitem_T, compl_match_arraysize);
1259 if (compl_match_array == NULL)
1260 return -1;
1261
1262 // If the current match is the original text don't find the first
1263 // match after it, don't highlight anything.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001264 if (match_at_original_text(compl_shown_match))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001265 shown_match_ok = TRUE;
1266
glepnir53387c52024-05-27 15:11:01 +02001267 if (compl_leader != NULL
1268 && STRCMP(compl_leader, compl_orig_text) == 0
1269 && shown_match_ok == FALSE)
1270 compl_shown_match = compl_no_select ? compl_first_match
1271 : compl_first_match->cp_next;
1272
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001273 i = 0;
1274 compl = compl_first_match;
1275 do
1276 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001277 if (!match_at_original_text(compl)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001278 && (compl_leader == NULL
glepnira218cc62024-06-03 19:32:39 +02001279 || ins_compl_equal(compl, compl_leader, lead_len)
1280 || (compl_fuzzy_match && compl->cp_score > 0)))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001281 {
glepnira218cc62024-06-03 19:32:39 +02001282 if (!shown_match_ok && !compl_fuzzy_match)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001283 {
1284 if (compl == compl_shown_match || did_find_shown_match)
1285 {
1286 // This item is the shown match or this is the
1287 // first displayed item after the shown match.
1288 compl_shown_match = compl;
1289 did_find_shown_match = TRUE;
1290 shown_match_ok = TRUE;
1291 }
1292 else
1293 // Remember this displayed match for when the
1294 // shown match is just below it.
1295 shown_compl = compl;
1296 cur = i;
1297 }
glepnira218cc62024-06-03 19:32:39 +02001298 else if (compl_fuzzy_match)
1299 {
glepnirf94c9c42024-06-14 21:11:56 +02001300 if (i == 0)
glepnir105f7412024-06-15 15:32:22 +02001301 shown_compl = compl;
glepnirdca57fb2024-06-04 22:01:21 +02001302 // Update the maximum fuzzy score and the shown match
1303 // if the current item's score is higher
glepnira218cc62024-06-03 19:32:39 +02001304 if (compl->cp_score > max_fuzzy_score)
1305 {
1306 did_find_shown_match = TRUE;
1307 max_fuzzy_score = compl->cp_score;
glepnir753794b2024-08-20 19:58:44 +02001308 if (!compl_no_select)
1309 compl_shown_match = compl;
glepnir65407ce2024-07-06 16:09:19 +02001310 }
1311
1312 if (!shown_match_ok && compl == compl_shown_match && !compl_no_select)
1313 {
1314 cur = i;
glepnira218cc62024-06-03 19:32:39 +02001315 shown_match_ok = TRUE;
1316 }
1317
glepnirdca57fb2024-06-04 22:01:21 +02001318 // If there is no "no select" condition and the max fuzzy
1319 // score is positive, or there is no completion leader or the
1320 // leader length is zero, mark the shown match as valid and
1321 // reset the current index.
glepnira218cc62024-06-03 19:32:39 +02001322 if (!compl_no_select
1323 && (max_fuzzy_score > 0
1324 || (compl_leader == NULL || lead_len == 0)))
1325 {
glepnirf94c9c42024-06-14 21:11:56 +02001326 if (match_at_original_text(compl_shown_match))
glepnir105f7412024-06-15 15:32:22 +02001327 compl_shown_match = shown_compl;
glepnira218cc62024-06-03 19:32:39 +02001328 }
1329 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001330
1331 if (compl->cp_text[CPT_ABBR] != NULL)
glepnir6a89c942024-10-01 20:32:12 +02001332 compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR];
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001333 else
1334 compl_match_array[i].pum_text = compl->cp_str;
1335 compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
1336 compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
glepnira218cc62024-06-03 19:32:39 +02001337 compl_match_array[i].pum_score = compl->cp_score;
glepnir0fe17f82024-10-08 22:26:44 +02001338 compl_match_array[i].pum_user_abbr_hlattr = compl->cp_user_abbr_hlattr;
glepnir38f99a12024-08-23 18:31:06 +02001339 compl_match_array[i].pum_user_kind_hlattr = compl->cp_user_kind_hlattr;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001340 if (compl->cp_text[CPT_MENU] != NULL)
1341 compl_match_array[i++].pum_extra =
1342 compl->cp_text[CPT_MENU];
1343 else
1344 compl_match_array[i++].pum_extra = compl->cp_fname;
1345 }
1346
glepnira218cc62024-06-03 19:32:39 +02001347 if (compl == compl_shown_match && !compl_fuzzy_match)
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001348 {
1349 did_find_shown_match = TRUE;
1350
1351 // When the original text is the shown match don't set
1352 // compl_shown_match.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001353 if (match_at_original_text(compl))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001354 shown_match_ok = TRUE;
1355
1356 if (!shown_match_ok && shown_compl != NULL)
1357 {
1358 // The shown match isn't displayed, set it to the
1359 // previously displayed match.
1360 compl_shown_match = shown_compl;
1361 shown_match_ok = TRUE;
1362 }
1363 }
1364 compl = compl->cp_next;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001365 } while (compl != NULL && !is_first_match(compl));
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001366
glepnira218cc62024-06-03 19:32:39 +02001367 if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0)
zeertzjq8e567472024-06-14 20:04:42 +02001368 {
1369 for (i = 0; i < compl_match_arraysize; i++)
1370 compl_match_array[i].pum_idx = i;
glepnira218cc62024-06-03 19:32:39 +02001371 // sort by the largest score of fuzzy match
zeertzjq8e567472024-06-14 20:04:42 +02001372 qsort(compl_match_array, (size_t)compl_match_arraysize,
1373 sizeof(pumitem_T), ins_compl_fuzzy_cmp);
glepnir65407ce2024-07-06 16:09:19 +02001374 shown_match_ok = TRUE;
zeertzjq8e567472024-06-14 20:04:42 +02001375 }
glepnira218cc62024-06-03 19:32:39 +02001376
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001377 if (!shown_match_ok) // no displayed match at all
1378 cur = -1;
1379
1380 return cur;
1381}
1382
1383/*
1384 * Show the popup menu for the list of matches.
1385 * Also adjusts "compl_shown_match" to an entry that is actually displayed.
1386 */
1387 void
1388ins_compl_show_pum(void)
1389{
1390 int i;
1391 int cur = -1;
1392 colnr_T col;
1393
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001394 if (!pum_wanted() || !pum_enough_matches())
1395 return;
1396
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001397 // Update the screen later, before drawing the popup menu over it.
1398 pum_call_update_screen();
1399
1400 if (compl_match_array == NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001401 // Need to build the popup menu list.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001402 cur = ins_compl_build_pum();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001403 else
1404 {
1405 // popup menu already exists, only need to find the current item.
1406 for (i = 0; i < compl_match_arraysize; ++i)
1407 if (compl_match_array[i].pum_text == compl_shown_match->cp_str
1408 || compl_match_array[i].pum_text
1409 == compl_shown_match->cp_text[CPT_ABBR])
1410 {
1411 cur = i;
1412 break;
1413 }
1414 }
1415
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001416 if (compl_match_array == NULL)
glepnir0d3c0a62024-02-11 17:52:40 +01001417 {
1418#ifdef FEAT_EVAL
1419 if (compl_started && has_completechanged())
1420 trigger_complete_changed_event(cur);
1421#endif
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001422 return;
glepnir0d3c0a62024-02-11 17:52:40 +01001423 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001424
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001425 // In Replace mode when a $ is displayed at the end of the line only
1426 // part of the screen would be updated. We do need to redraw here.
1427 dollar_vcol = -1;
1428
1429 // Compute the screen column of the start of the completed text.
1430 // Use the cursor to get all wrapping and other settings right.
1431 col = curwin->w_cursor.col;
1432 curwin->w_cursor.col = compl_col;
glepnira218cc62024-06-03 19:32:39 +02001433 compl_selected_item = cur;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001434 pum_display(compl_match_array, compl_match_arraysize, cur);
1435 curwin->w_cursor.col = col;
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001436
glepnircbb46b42024-02-03 18:11:13 +01001437 // After adding leader, set the current match to shown match.
1438 if (compl_started && compl_curr_match != compl_shown_match)
1439 compl_curr_match = compl_shown_match;
1440
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001441#ifdef FEAT_EVAL
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001442 if (has_completechanged())
1443 trigger_complete_changed_event(cur);
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001444#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001445}
1446
1447#define DICT_FIRST (1) // use just first element in "dict"
1448#define DICT_EXACT (2) // "dict" is the exact name of a file
1449
1450/*
glepnir40c1c332024-06-11 19:37:04 +02001451 * Get current completion leader
1452 */
1453 char_u *
1454ins_compl_leader(void)
1455{
glepnirf1891382024-06-17 18:35:25 +02001456 return compl_leader != NULL ? compl_leader : compl_orig_text;
glepnir40c1c332024-06-11 19:37:04 +02001457}
1458
1459/*
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001460 * Add any identifiers that match the given pattern "pat" in the list of
1461 * dictionary files "dict_start" to the list of completions.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001462 */
1463 static void
1464ins_compl_dictionaries(
1465 char_u *dict_start,
1466 char_u *pat,
1467 int flags, // DICT_FIRST and/or DICT_EXACT
1468 int thesaurus) // Thesaurus completion
1469{
1470 char_u *dict = dict_start;
1471 char_u *ptr;
1472 char_u *buf;
1473 regmatch_T regmatch;
1474 char_u **files;
1475 int count;
1476 int save_p_scs;
1477 int dir = compl_direction;
1478
1479 if (*dict == NUL)
1480 {
1481#ifdef FEAT_SPELL
1482 // When 'dictionary' is empty and spell checking is enabled use
1483 // "spell".
1484 if (!thesaurus && curwin->w_p_spell)
1485 dict = (char_u *)"spell";
1486 else
1487#endif
1488 return;
1489 }
1490
1491 buf = alloc(LSIZE);
1492 if (buf == NULL)
1493 return;
1494 regmatch.regprog = NULL; // so that we can goto theend
1495
1496 // If 'infercase' is set, don't use 'smartcase' here
1497 save_p_scs = p_scs;
1498 if (curbuf->b_p_inf)
1499 p_scs = FALSE;
1500
1501 // When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern
1502 // to only match at the start of a line. Otherwise just match the
1503 // pattern. Also need to double backslashes.
1504 if (ctrl_x_mode_line_or_eval())
1505 {
1506 char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\");
1507 size_t len;
1508
1509 if (pat_esc == NULL)
1510 goto theend;
1511 len = STRLEN(pat_esc) + 10;
Bram Moolenaar964b3742019-05-24 18:54:09 +02001512 ptr = alloc(len);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001513 if (ptr == NULL)
1514 {
1515 vim_free(pat_esc);
1516 goto theend;
1517 }
1518 vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc);
1519 regmatch.regprog = vim_regcomp(ptr, RE_MAGIC);
1520 vim_free(pat_esc);
1521 vim_free(ptr);
1522 }
1523 else
1524 {
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001525 regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001526 if (regmatch.regprog == NULL)
1527 goto theend;
1528 }
1529
1530 // ignore case depends on 'ignorecase', 'smartcase' and "pat"
1531 regmatch.rm_ic = ignorecase(pat);
1532 while (*dict != NUL && !got_int && !compl_interrupted)
1533 {
1534 // copy one dictionary file name into buf
1535 if (flags == DICT_EXACT)
1536 {
1537 count = 1;
1538 files = &dict;
1539 }
1540 else
1541 {
1542 // Expand wildcards in the dictionary name, but do not allow
1543 // backticks (for security, the 'dict' option may have been set in
1544 // a modeline).
1545 copy_option_part(&dict, buf, LSIZE, ",");
1546# ifdef FEAT_SPELL
1547 if (!thesaurus && STRCMP(buf, "spell") == 0)
1548 count = -1;
1549 else
1550# endif
1551 if (vim_strchr(buf, '`') != NULL
1552 || expand_wildcards(1, &buf, &count, &files,
1553 EW_FILE|EW_SILENT) != OK)
1554 count = 0;
1555 }
1556
1557# ifdef FEAT_SPELL
1558 if (count == -1)
1559 {
1560 // Complete from active spelling. Skip "\<" in the pattern, we
1561 // don't use it as a RE.
1562 if (pat[0] == '\\' && pat[1] == '<')
1563 ptr = pat + 2;
1564 else
1565 ptr = pat;
1566 spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0);
1567 }
1568 else
1569# endif
1570 if (count > 0) // avoid warning for using "files" uninit
1571 {
1572 ins_compl_files(count, files, thesaurus, flags,
1573 &regmatch, buf, &dir);
1574 if (flags != DICT_EXACT)
1575 FreeWild(count, files);
1576 }
1577 if (flags != 0)
1578 break;
1579 }
1580
1581theend:
1582 p_scs = save_p_scs;
1583 vim_regfree(regmatch.regprog);
1584 vim_free(buf);
1585}
1586
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001587/*
1588 * Add all the words in the line "*buf_arg" from the thesaurus file "fname"
1589 * skipping the word at 'skip_word'. Returns OK on success.
1590 */
1591 static int
zeertzjq5fb3aab2022-08-24 16:48:23 +01001592thesaurus_add_words_in_line(
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001593 char_u *fname,
1594 char_u **buf_arg,
1595 int dir,
1596 char_u *skip_word)
1597{
1598 int status = OK;
1599 char_u *ptr;
1600 char_u *wstart;
1601
1602 // Add the other matches on the line
1603 ptr = *buf_arg;
1604 while (!got_int)
1605 {
1606 // Find start of the next word. Skip white
1607 // space and punctuation.
1608 ptr = find_word_start(ptr);
1609 if (*ptr == NUL || *ptr == NL)
1610 break;
1611 wstart = ptr;
1612
1613 // Find end of the word.
1614 if (has_mbyte)
1615 // Japanese words may have characters in
1616 // different classes, only separate words
1617 // with single-byte non-word characters.
1618 while (*ptr != NUL)
1619 {
1620 int l = (*mb_ptr2len)(ptr);
1621
1622 if (l < 2 && !vim_iswordc(*ptr))
1623 break;
1624 ptr += l;
1625 }
1626 else
1627 ptr = find_word_end(ptr);
1628
1629 // Add the word. Skip the regexp match.
1630 if (wstart != skip_word)
1631 {
1632 status = ins_compl_add_infercase(wstart, (int)(ptr - wstart), p_ic,
1633 fname, dir, FALSE);
1634 if (status == FAIL)
1635 break;
1636 }
1637 }
1638
1639 *buf_arg = ptr;
1640 return status;
1641}
1642
1643/*
1644 * Process "count" dictionary/thesaurus "files" and add the text matching
1645 * "regmatch".
1646 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001647 static void
1648ins_compl_files(
1649 int count,
1650 char_u **files,
1651 int thesaurus,
1652 int flags,
1653 regmatch_T *regmatch,
1654 char_u *buf,
1655 int *dir)
1656{
1657 char_u *ptr;
1658 int i;
1659 FILE *fp;
1660 int add_r;
1661
1662 for (i = 0; i < count && !got_int && !compl_interrupted; i++)
1663 {
1664 fp = mch_fopen((char *)files[i], "r"); // open dictionary file
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01001665 if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001666 {
Bram Moolenaarcc233582020-12-12 13:32:07 +01001667 msg_hist_off = TRUE; // reset in msg_trunc_attr()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001668 vim_snprintf((char *)IObuff, IOSIZE,
1669 _("Scanning dictionary: %s"), (char *)files[i]);
1670 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
1671 }
1672
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001673 if (fp == NULL)
1674 continue;
1675
1676 // Read dictionary file line by line.
1677 // Check each line for a match.
1678 while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001679 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001680 ptr = buf;
1681 while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf)))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001682 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001683 ptr = regmatch->startp[0];
1684 if (ctrl_x_mode_line_or_eval())
1685 ptr = find_line_end(ptr);
1686 else
1687 ptr = find_word_end(ptr);
1688 add_r = ins_compl_add_infercase(regmatch->startp[0],
1689 (int)(ptr - regmatch->startp[0]),
1690 p_ic, files[i], *dir, FALSE);
1691 if (thesaurus)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001692 {
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001693 // For a thesaurus, add all the words in the line
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001694 ptr = buf;
zeertzjq5fb3aab2022-08-24 16:48:23 +01001695 add_r = thesaurus_add_words_in_line(files[i], &ptr, *dir,
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001696 regmatch->startp[0]);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001697 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001698 if (add_r == OK)
1699 // if dir was BACKWARD then honor it just once
1700 *dir = FORWARD;
1701 else if (add_r == FAIL)
1702 break;
1703 // avoid expensive call to vim_regexec() when at end
1704 // of line
1705 if (*ptr == '\n' || got_int)
1706 break;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001707 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001708 line_breakcheck();
1709 ins_compl_check_keys(50, FALSE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001710 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00001711 fclose(fp);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001712 }
1713}
1714
1715/*
1716 * Find the start of the next word.
1717 * Returns a pointer to the first char of the word. Also stops at a NUL.
1718 */
1719 char_u *
1720find_word_start(char_u *ptr)
1721{
1722 if (has_mbyte)
1723 while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1)
1724 ptr += (*mb_ptr2len)(ptr);
1725 else
1726 while (*ptr != NUL && *ptr != '\n' && !vim_iswordc(*ptr))
1727 ++ptr;
1728 return ptr;
1729}
1730
1731/*
1732 * Find the end of the word. Assumes it starts inside a word.
1733 * Returns a pointer to just after the word.
1734 */
1735 char_u *
1736find_word_end(char_u *ptr)
1737{
1738 int start_class;
1739
1740 if (has_mbyte)
1741 {
1742 start_class = mb_get_class(ptr);
1743 if (start_class > 1)
1744 while (*ptr != NUL)
1745 {
1746 ptr += (*mb_ptr2len)(ptr);
1747 if (mb_get_class(ptr) != start_class)
1748 break;
1749 }
1750 }
1751 else
1752 while (vim_iswordc(*ptr))
1753 ++ptr;
1754 return ptr;
1755}
1756
1757/*
1758 * Find the end of the line, omitting CR and NL at the end.
1759 * Returns a pointer to just after the line.
1760 */
1761 static char_u *
1762find_line_end(char_u *ptr)
1763{
1764 char_u *s;
1765
1766 s = ptr + STRLEN(ptr);
1767 while (s > ptr && (s[-1] == CAR || s[-1] == NL))
1768 --s;
1769 return s;
1770}
1771
1772/*
1773 * Free the list of completions
1774 */
1775 static void
1776ins_compl_free(void)
1777{
1778 compl_T *match;
1779 int i;
1780
1781 VIM_CLEAR(compl_pattern);
John Marriott8c85a2a2024-05-20 19:18:26 +02001782 compl_patternlen = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001783 VIM_CLEAR(compl_leader);
1784
1785 if (compl_first_match == NULL)
1786 return;
1787
1788 ins_compl_del_pum();
1789 pum_clear();
1790
1791 compl_curr_match = compl_first_match;
1792 do
1793 {
1794 match = compl_curr_match;
1795 compl_curr_match = compl_curr_match->cp_next;
1796 vim_free(match->cp_str);
1797 // several entries may use the same fname, free it just once.
Bram Moolenaard9eefe32019-04-06 14:22:21 +02001798 if (match->cp_flags & CP_FREE_FNAME)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001799 vim_free(match->cp_fname);
1800 for (i = 0; i < CPT_COUNT; ++i)
1801 vim_free(match->cp_text[i]);
Bram Moolenaarab782c52020-01-04 19:00:11 +01001802#ifdef FEAT_EVAL
Bram Moolenaar08928322020-01-04 14:32:48 +01001803 clear_tv(&match->cp_user_data);
Bram Moolenaarab782c52020-01-04 19:00:11 +01001804#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001805 vim_free(match);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001806 } while (compl_curr_match != NULL && !is_first_match(compl_curr_match));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001807 compl_first_match = compl_curr_match = NULL;
1808 compl_shown_match = NULL;
1809 compl_old_match = NULL;
1810}
1811
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001812/*
1813 * Reset/clear the completion state.
1814 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001815 void
1816ins_compl_clear(void)
1817{
1818 compl_cont_status = 0;
1819 compl_started = FALSE;
1820 compl_matches = 0;
1821 VIM_CLEAR(compl_pattern);
John Marriott8c85a2a2024-05-20 19:18:26 +02001822 compl_patternlen = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001823 VIM_CLEAR(compl_leader);
1824 edit_submode_extra = NULL;
1825 VIM_CLEAR(compl_orig_text);
1826 compl_enter_selects = FALSE;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001827#ifdef FEAT_EVAL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001828 // clear v:completed_item
1829 set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001830#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001831}
1832
1833/*
1834 * Return TRUE when Insert completion is active.
1835 */
1836 int
1837ins_compl_active(void)
1838{
1839 return compl_started;
1840}
1841
1842/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001843 * Selected one of the matches. When FALSE the match was edited or using the
1844 * longest common string.
1845 */
1846 int
1847ins_compl_used_match(void)
1848{
1849 return compl_used_match;
1850}
1851
1852/*
1853 * Initialize get longest common string.
1854 */
1855 void
1856ins_compl_init_get_longest(void)
1857{
1858 compl_get_longest = FALSE;
1859}
1860
1861/*
1862 * Returns TRUE when insert completion is interrupted.
1863 */
1864 int
1865ins_compl_interrupted(void)
1866{
1867 return compl_interrupted;
1868}
1869
1870/*
1871 * Returns TRUE if the <Enter> key selects a match in the completion popup
1872 * menu.
1873 */
1874 int
1875ins_compl_enter_selects(void)
1876{
1877 return compl_enter_selects;
1878}
1879
1880/*
1881 * Return the column where the text starts that is being completed
1882 */
1883 colnr_T
1884ins_compl_col(void)
1885{
1886 return compl_col;
1887}
1888
1889/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001890 * Return the length in bytes of the text being completed
1891 */
1892 int
1893ins_compl_len(void)
1894{
1895 return compl_length;
1896}
1897
1898/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001899 * Delete one character before the cursor and show the subset of the matches
1900 * that match the word that is now before the cursor.
1901 * Returns the character to be used, NUL if the work is done and another char
1902 * to be got from the user.
1903 */
1904 int
1905ins_compl_bs(void)
1906{
1907 char_u *line;
1908 char_u *p;
1909
1910 line = ml_get_curline();
1911 p = line + curwin->w_cursor.col;
1912 MB_PTR_BACK(line, p);
1913
1914 // Stop completion when the whole word was deleted. For Omni completion
1915 // allow the word to be deleted, we won't match everything.
1916 // Respect the 'backspace' option.
1917 if ((int)(p - line) - (int)compl_col < 0
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001918 || ((int)(p - line) - (int)compl_col == 0 && !ctrl_x_mode_omni())
1919 || ctrl_x_mode_eval()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001920 || (!can_bs(BS_START) && (int)(p - line) - (int)compl_col
1921 - compl_length < 0))
1922 return K_BS;
1923
1924 // Deleted more than what was used to find matches or didn't finish
1925 // finding all matches: need to look for matches all over again.
1926 if (curwin->w_cursor.col <= compl_col + compl_length
1927 || ins_compl_need_restart())
1928 ins_compl_restart();
1929
1930 vim_free(compl_leader);
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001931 compl_leader = vim_strnsave(line + compl_col, (p - line) - compl_col);
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00001932 if (compl_leader == NULL)
1933 return K_BS;
1934
1935 ins_compl_new_leader();
1936 if (compl_shown_match != NULL)
1937 // Make sure current match is not a hidden item.
1938 compl_curr_match = compl_shown_match;
1939 return NUL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001940}
1941
1942/*
1943 * Return TRUE when we need to find matches again, ins_compl_restart() is to
1944 * be called.
1945 */
1946 static int
1947ins_compl_need_restart(void)
1948{
1949 // Return TRUE if we didn't complete finding matches or when the
1950 // 'completefunc' returned "always" in the "refresh" dictionary item.
1951 return compl_was_interrupted
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001952 || ((ctrl_x_mode_function() || ctrl_x_mode_omni())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001953 && compl_opt_refresh_always);
1954}
1955
1956/*
1957 * Called after changing "compl_leader".
1958 * Show the popup menu with a different set of matches.
1959 * May also search for matches again if the previous search was interrupted.
1960 */
1961 static void
1962ins_compl_new_leader(void)
1963{
1964 ins_compl_del_pum();
1965 ins_compl_delete();
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00001966 ins_bytes(compl_leader + get_compl_len());
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001967 compl_used_match = FALSE;
1968
1969 if (compl_started)
1970 ins_compl_set_original_text(compl_leader);
1971 else
1972 {
1973#ifdef FEAT_SPELL
1974 spell_bad_len = 0; // need to redetect bad word
1975#endif
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001976 // Matches were cleared, need to search for them now. Before drawing
Bram Moolenaar7591bb32019-03-30 13:53:47 +01001977 // the popup menu display the changed text before the cursor. Set
1978 // "compl_restarting" to avoid that the first match is inserted.
1979 pum_call_update_screen();
1980#ifdef FEAT_GUI
1981 if (gui.in_use)
1982 {
1983 // Show the cursor after the match, not after the redrawn text.
1984 setcursor();
1985 out_flush_cursor(FALSE, FALSE);
1986 }
1987#endif
1988 compl_restarting = TRUE;
1989 if (ins_complete(Ctrl_N, TRUE) == FAIL)
1990 compl_cont_status = 0;
1991 compl_restarting = FALSE;
1992 }
1993
1994 compl_enter_selects = !compl_used_match;
1995
1996 // Show the popup menu with a different set of matches.
1997 ins_compl_show_pum();
1998
1999 // Don't let Enter select the original text when there is no popup menu.
2000 if (compl_match_array == NULL)
2001 compl_enter_selects = FALSE;
2002}
2003
2004/*
2005 * Return the length of the completion, from the completion start column to
2006 * the cursor column. Making sure it never goes below zero.
2007 */
2008 static int
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002009get_compl_len(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002010{
2011 int off = (int)curwin->w_cursor.col - (int)compl_col;
2012
2013 if (off < 0)
2014 return 0;
2015 return off;
2016}
2017
2018/*
2019 * Append one character to the match leader. May reduce the number of
2020 * matches.
2021 */
2022 void
2023ins_compl_addleader(int c)
2024{
2025 int cc;
2026
2027 if (stop_arrow() == FAIL)
2028 return;
2029 if (has_mbyte && (cc = (*mb_char2len)(c)) > 1)
2030 {
2031 char_u buf[MB_MAXBYTES + 1];
2032
2033 (*mb_char2bytes)(c, buf);
2034 buf[cc] = NUL;
2035 ins_char_bytes(buf, cc);
2036 if (compl_opt_refresh_always)
2037 AppendToRedobuff(buf);
2038 }
2039 else
2040 {
2041 ins_char(c);
2042 if (compl_opt_refresh_always)
2043 AppendCharToRedobuff(c);
2044 }
2045
2046 // If we didn't complete finding matches we must search again.
2047 if (ins_compl_need_restart())
2048 ins_compl_restart();
2049
2050 // When 'always' is set, don't reset compl_leader. While completing,
2051 // cursor doesn't point original position, changing compl_leader would
2052 // break redo.
2053 if (!compl_opt_refresh_always)
2054 {
2055 vim_free(compl_leader);
2056 compl_leader = vim_strnsave(ml_get_curline() + compl_col,
Bram Moolenaar71ccd032020-06-12 22:59:11 +02002057 curwin->w_cursor.col - compl_col);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002058 if (compl_leader != NULL)
2059 ins_compl_new_leader();
2060 }
2061}
2062
2063/*
2064 * Setup for finding completions again without leaving CTRL-X mode. Used when
2065 * BS or a key was typed while still searching for matches.
2066 */
2067 static void
2068ins_compl_restart(void)
2069{
2070 ins_compl_free();
2071 compl_started = FALSE;
2072 compl_matches = 0;
2073 compl_cont_status = 0;
2074 compl_cont_mode = 0;
2075}
2076
2077/*
2078 * Set the first match, the original text.
2079 */
2080 static void
2081ins_compl_set_original_text(char_u *str)
2082{
2083 char_u *p;
2084
2085 // Replace the original text entry.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002086 // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
2087 // be at the last item for backward completion
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002088 if (match_at_original_text(compl_first_match)) // safety check
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002089 {
2090 p = vim_strsave(str);
2091 if (p != NULL)
2092 {
2093 vim_free(compl_first_match->cp_str);
2094 compl_first_match->cp_str = p;
2095 }
2096 }
2097 else if (compl_first_match->cp_prev != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002098 && match_at_original_text(compl_first_match->cp_prev))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002099 {
2100 p = vim_strsave(str);
2101 if (p != NULL)
2102 {
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002103 vim_free(compl_first_match->cp_prev->cp_str);
2104 compl_first_match->cp_prev->cp_str = p;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002105 }
2106 }
2107}
2108
2109/*
2110 * Append one character to the match leader. May reduce the number of
2111 * matches.
2112 */
2113 void
2114ins_compl_addfrommatch(void)
2115{
2116 char_u *p;
2117 int len = (int)curwin->w_cursor.col - (int)compl_col;
2118 int c;
2119 compl_T *cp;
2120
2121 p = compl_shown_match->cp_str;
2122 if ((int)STRLEN(p) <= len) // the match is too short
2123 {
2124 // When still at the original match use the first entry that matches
2125 // the leader.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002126 if (!match_at_original_text(compl_shown_match))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002127 return;
2128
2129 p = NULL;
2130 for (cp = compl_shown_match->cp_next; cp != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002131 && !is_first_match(cp); cp = cp->cp_next)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002132 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002133 if (compl_leader == NULL
2134 || ins_compl_equal(cp, compl_leader,
2135 (int)STRLEN(compl_leader)))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002136 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002137 p = cp->cp_str;
2138 break;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002139 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002140 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00002141 if (p == NULL || (int)STRLEN(p) <= len)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002142 return;
2143 }
2144 p += len;
2145 c = PTR2CHAR(p);
2146 ins_compl_addleader(c);
2147}
2148
2149/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002150 * Set the CTRL-X completion mode based on the key "c" typed after a CTRL-X.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002151 * Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre,
2152 * compl_cont_mode and compl_cont_status.
2153 * Returns TRUE when the character is not to be inserted.
2154 */
2155 static int
2156set_ctrl_x_mode(int c)
2157{
2158 int retval = FALSE;
2159
2160 switch (c)
2161 {
2162 case Ctrl_E:
2163 case Ctrl_Y:
2164 // scroll the window one line up or down
2165 ctrl_x_mode = CTRL_X_SCROLL;
2166 if (!(State & REPLACE_FLAG))
2167 edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
2168 else
2169 edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
2170 edit_submode_pre = NULL;
2171 showmode();
2172 break;
2173 case Ctrl_L:
2174 // complete whole line
2175 ctrl_x_mode = CTRL_X_WHOLE_LINE;
2176 break;
2177 case Ctrl_F:
2178 // complete filenames
2179 ctrl_x_mode = CTRL_X_FILES;
2180 break;
2181 case Ctrl_K:
zeertzjq5fb3aab2022-08-24 16:48:23 +01002182 // complete words from a dictionary
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002183 ctrl_x_mode = CTRL_X_DICTIONARY;
2184 break;
2185 case Ctrl_R:
2186 // Register insertion without exiting CTRL-X mode
2187 // Simply allow ^R to happen without affecting ^X mode
2188 break;
2189 case Ctrl_T:
2190 // complete words from a thesaurus
2191 ctrl_x_mode = CTRL_X_THESAURUS;
2192 break;
2193#ifdef FEAT_COMPL_FUNC
2194 case Ctrl_U:
2195 // user defined completion
2196 ctrl_x_mode = CTRL_X_FUNCTION;
2197 break;
2198 case Ctrl_O:
2199 // omni completion
2200 ctrl_x_mode = CTRL_X_OMNI;
2201 break;
2202#endif
2203 case 's':
2204 case Ctrl_S:
2205 // complete spelling suggestions
2206 ctrl_x_mode = CTRL_X_SPELL;
2207#ifdef FEAT_SPELL
2208 ++emsg_off; // Avoid getting the E756 error twice.
2209 spell_back_to_badword();
2210 --emsg_off;
2211#endif
2212 break;
2213 case Ctrl_RSB:
2214 // complete tag names
2215 ctrl_x_mode = CTRL_X_TAGS;
2216 break;
2217#ifdef FEAT_FIND_ID
2218 case Ctrl_I:
2219 case K_S_TAB:
2220 // complete keywords from included files
2221 ctrl_x_mode = CTRL_X_PATH_PATTERNS;
2222 break;
2223 case Ctrl_D:
2224 // complete definitions from included files
2225 ctrl_x_mode = CTRL_X_PATH_DEFINES;
2226 break;
2227#endif
2228 case Ctrl_V:
2229 case Ctrl_Q:
2230 // complete vim commands
2231 ctrl_x_mode = CTRL_X_CMDLINE;
2232 break;
2233 case Ctrl_Z:
2234 // stop completion
2235 ctrl_x_mode = CTRL_X_NORMAL;
2236 edit_submode = NULL;
2237 showmode();
2238 retval = TRUE;
2239 break;
2240 case Ctrl_P:
2241 case Ctrl_N:
2242 // ^X^P means LOCAL expansion if nothing interrupted (eg we
2243 // just started ^X mode, or there were enough ^X's to cancel
2244 // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
2245 // do normal expansion when interrupting a different mode (say
2246 // ^X^F^X^P or ^P^X^X^P, see below)
2247 // nothing changes if interrupting mode 0, (eg, the flag
2248 // doesn't change when going to ADDING mode -- Acevedo
2249 if (!(compl_cont_status & CONT_INTRPT))
2250 compl_cont_status |= CONT_LOCAL;
2251 else if (compl_cont_mode != 0)
2252 compl_cont_status &= ~CONT_LOCAL;
2253 // FALLTHROUGH
2254 default:
2255 // If we have typed at least 2 ^X's... for modes != 0, we set
2256 // compl_cont_status = 0 (eg, as if we had just started ^X
2257 // mode).
2258 // For mode 0, we set "compl_cont_mode" to an impossible
2259 // value, in both cases ^X^X can be used to restart the same
2260 // mode (avoiding ADDING mode).
2261 // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
2262 // 'complete' and local ^P expansions respectively.
2263 // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
2264 // mode -- Acevedo
2265 if (c == Ctrl_X)
2266 {
2267 if (compl_cont_mode != 0)
2268 compl_cont_status = 0;
2269 else
2270 compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
2271 }
2272 ctrl_x_mode = CTRL_X_NORMAL;
2273 edit_submode = NULL;
2274 showmode();
2275 break;
2276 }
2277
2278 return retval;
2279}
2280
2281/*
2282 * Stop insert completion mode
2283 */
2284 static int
2285ins_compl_stop(int c, int prev_mode, int retval)
2286{
2287 char_u *ptr;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002288 int want_cindent;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002289
2290 // Get here when we have finished typing a sequence of ^N and
2291 // ^P or other completion characters in CTRL-X mode. Free up
2292 // memory that was used, and make sure we can redo the insert.
2293 if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E)
2294 {
2295 // If any of the original typed text has been changed, eg when
2296 // ignorecase is set, we must add back-spaces to the redo
2297 // buffer. We add as few as necessary to delete just the part
2298 // of the original text that has changed.
2299 // When using the longest match, edited the match or used
2300 // CTRL-E then don't use the current match.
2301 if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E)
2302 ptr = compl_curr_match->cp_str;
2303 else
2304 ptr = NULL;
2305 ins_compl_fixRedoBufForLeader(ptr);
2306 }
2307
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002308 want_cindent = (get_can_cindent() && cindent_on());
Bram Moolenaar8e145b82022-05-21 20:17:31 +01002309
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002310 // When completing whole lines: fix indent for 'cindent'.
2311 // Otherwise, break line if it's too long.
2312 if (compl_cont_mode == CTRL_X_WHOLE_LINE)
2313 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002314 // re-indent the current line
2315 if (want_cindent)
2316 {
2317 do_c_expr_indent();
2318 want_cindent = FALSE; // don't do it again
2319 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002320 }
2321 else
2322 {
2323 int prev_col = curwin->w_cursor.col;
2324
2325 // put the cursor on the last char, for 'tw' formatting
2326 if (prev_col > 0)
2327 dec_cursor();
2328 // only format when something was inserted
2329 if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E)
2330 insertchar(NUL, 0, -1);
2331 if (prev_col > 0
2332 && ml_get_curline()[curwin->w_cursor.col] != NUL)
2333 inc_cursor();
2334 }
2335
2336 // If the popup menu is displayed pressing CTRL-Y means accepting
2337 // the selection without inserting anything. When
2338 // compl_enter_selects is set the Enter key does the same.
2339 if ((c == Ctrl_Y || (compl_enter_selects
2340 && (c == CAR || c == K_KENTER || c == NL)))
2341 && pum_visible())
2342 retval = TRUE;
2343
2344 // CTRL-E means completion is Ended, go back to the typed text.
2345 // but only do this, if the Popup is still visible
2346 if (c == Ctrl_E)
2347 {
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002348 char_u *p = NULL;
2349
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002350 ins_compl_delete();
2351 if (compl_leader != NULL)
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002352 p = compl_leader;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002353 else if (compl_first_match != NULL)
Bram Moolenaarf12129f2022-07-01 19:58:30 +01002354 p = compl_orig_text;
2355 if (p != NULL)
2356 {
2357 int compl_len = get_compl_len();
2358 int len = (int)STRLEN(p);
2359
2360 if (len > compl_len)
2361 ins_bytes_len(p + compl_len, len - compl_len);
2362 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002363 retval = TRUE;
2364 }
2365
2366 auto_format(FALSE, TRUE);
2367
2368 // Trigger the CompleteDonePre event to give scripts a chance to
2369 // act upon the completion before clearing the info, and restore
2370 // ctrl_x_mode, so that complete_info() can be used.
2371 ctrl_x_mode = prev_mode;
2372 ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
2373
2374 ins_compl_free();
2375 compl_started = FALSE;
2376 compl_matches = 0;
2377 if (!shortmess(SHM_COMPLETIONMENU))
2378 msg_clr_cmdline(); // necessary for "noshowmode"
2379 ctrl_x_mode = CTRL_X_NORMAL;
2380 compl_enter_selects = FALSE;
2381 if (edit_submode != NULL)
2382 {
2383 edit_submode = NULL;
2384 showmode();
2385 }
2386
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002387 if (c == Ctrl_C && cmdwin_type != 0)
2388 // Avoid the popup menu remains displayed when leaving the
2389 // command line window.
2390 update_screen(0);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002391 // Indent now if a key was typed that is in 'cinkeys'.
2392 if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0)))
2393 do_c_expr_indent();
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002394 // Trigger the CompleteDone event to give scripts a chance to act
2395 // upon the end of completion.
2396 ins_apply_autocmds(EVENT_COMPLETEDONE);
2397
2398 return retval;
2399}
2400
2401/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002402 * Prepare for Insert mode completion, or stop it.
2403 * Called just after typing a character in Insert mode.
2404 * Returns TRUE when the character is not to be inserted;
2405 */
2406 int
2407ins_compl_prep(int c)
2408{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002409 int retval = FALSE;
Bram Moolenaar17e04782020-01-17 18:58:59 +01002410 int prev_mode = ctrl_x_mode;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002411
2412 // Forget any previous 'special' messages if this is actually
2413 // a ^X mode key - bar ^R, in which case we wait to see what it gives us.
2414 if (c != Ctrl_R && vim_is_ctrl_x_key(c))
2415 edit_submode_extra = NULL;
2416
zeertzjq440d4cb2023-03-02 17:51:32 +00002417 // Ignore end of Select mode mapping and mouse scroll/movement.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002418 if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
zeertzjq440d4cb2023-03-02 17:51:32 +00002419 || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_MOUSEMOVE
Bram Moolenaare32c3c42022-01-15 18:26:04 +00002420 || c == K_COMMAND || c == K_SCRIPT_COMMAND)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002421 return retval;
2422
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002423#ifdef FEAT_PROP_POPUP
Bram Moolenaarf0bc15c2019-08-18 19:23:45 +02002424 // Ignore mouse events in a popup window
2425 if (is_mouse_key(c))
2426 {
2427 // Ignore drag and release events, the position does not need to be in
2428 // the popup and it may have just closed.
2429 if (c == K_LEFTRELEASE
2430 || c == K_LEFTRELEASE_NM
2431 || c == K_MIDDLERELEASE
2432 || c == K_RIGHTRELEASE
2433 || c == K_X1RELEASE
2434 || c == K_X2RELEASE
2435 || c == K_LEFTDRAG
2436 || c == K_MIDDLEDRAG
2437 || c == K_RIGHTDRAG
2438 || c == K_X1DRAG
2439 || c == K_X2DRAG)
2440 return retval;
2441 if (popup_visible)
2442 {
2443 int row = mouse_row;
2444 int col = mouse_col;
2445 win_T *wp = mouse_find_win(&row, &col, FIND_POPUP);
2446
2447 if (wp != NULL && WIN_IS_POPUP(wp))
2448 return retval;
2449 }
2450 }
2451#endif
2452
zeertzjqdca29d92021-08-31 19:12:51 +02002453 if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X)
2454 {
2455 if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c)
2456 || !vim_is_ctrl_x_key(c))
2457 {
2458 // Not starting another completion mode.
2459 ctrl_x_mode = CTRL_X_CMDLINE;
2460
2461 // CTRL-X CTRL-Z should stop completion without inserting anything
2462 if (c == Ctrl_Z)
2463 retval = TRUE;
2464 }
2465 else
2466 {
2467 ctrl_x_mode = CTRL_X_CMDLINE;
2468
2469 // Other CTRL-X keys first stop completion, then start another
2470 // completion mode.
2471 ins_compl_prep(' ');
2472 ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
2473 }
2474 }
2475
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002476 // Set "compl_get_longest" when finding the first matches.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002477 if (ctrl_x_mode_not_defined_yet()
2478 || (ctrl_x_mode_normal() && !compl_started))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002479 {
zeertzjq529b9ad2024-06-05 20:27:06 +02002480 compl_get_longest = (get_cot_flags() & COT_LONGEST) != 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002481 compl_used_match = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002482 }
2483
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002484 if (ctrl_x_mode_not_defined_yet())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002485 // We have just typed CTRL-X and aren't quite sure which CTRL-X mode
2486 // it will be yet. Now we decide.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002487 retval = set_ctrl_x_mode(c);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002488 else if (ctrl_x_mode_not_default())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002489 {
2490 // We're already in CTRL-X mode, do we stay in it?
2491 if (!vim_is_ctrl_x_key(c))
2492 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002493 if (ctrl_x_mode_scroll())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002494 ctrl_x_mode = CTRL_X_NORMAL;
2495 else
2496 ctrl_x_mode = CTRL_X_FINISHED;
2497 edit_submode = NULL;
2498 }
2499 showmode();
2500 }
2501
2502 if (compl_started || ctrl_x_mode == CTRL_X_FINISHED)
2503 {
2504 // Show error message from attempted keyword completion (probably
2505 // 'Pattern not found') until another key is hit, then go back to
2506 // showing what mode we are in.
2507 showmode();
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002508 if ((ctrl_x_mode_normal() && c != Ctrl_N && c != Ctrl_P
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002509 && c != Ctrl_R && !ins_compl_pum_key(c))
2510 || ctrl_x_mode == CTRL_X_FINISHED)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00002511 retval = ins_compl_stop(c, prev_mode, retval);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002512 }
2513 else if (ctrl_x_mode == CTRL_X_LOCAL_MSG)
2514 // Trigger the CompleteDone event to give scripts a chance to act
2515 // upon the (possibly failed) completion.
2516 ins_apply_autocmds(EVENT_COMPLETEDONE);
2517
LemonBoy2bf52dd2022-04-09 18:17:34 +01002518 may_trigger_modechanged();
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01002519
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002520 // reset continue_* if we left expansion-mode, if we stay they'll be
2521 // (re)set properly in ins_complete()
2522 if (!vim_is_ctrl_x_key(c))
2523 {
2524 compl_cont_status = 0;
2525 compl_cont_mode = 0;
2526 }
2527
2528 return retval;
2529}
2530
2531/*
2532 * Fix the redo buffer for the completion leader replacing some of the typed
2533 * text. This inserts backspaces and appends the changed text.
2534 * "ptr" is the known leader text or NUL.
2535 */
2536 static void
2537ins_compl_fixRedoBufForLeader(char_u *ptr_arg)
2538{
2539 int len;
2540 char_u *p;
2541 char_u *ptr = ptr_arg;
2542
2543 if (ptr == NULL)
2544 {
2545 if (compl_leader != NULL)
2546 ptr = compl_leader;
2547 else
2548 return; // nothing to do
2549 }
2550 if (compl_orig_text != NULL)
2551 {
2552 p = compl_orig_text;
2553 for (len = 0; p[len] != NUL && p[len] == ptr[len]; ++len)
2554 ;
2555 if (len > 0)
2556 len -= (*mb_head_off)(p, p + len);
2557 for (p += len; *p != NUL; MB_PTR_ADV(p))
2558 AppendCharToRedobuff(K_BS);
2559 }
2560 else
2561 len = 0;
2562 if (ptr != NULL)
2563 AppendToRedobuffLit(ptr + len, -1);
2564}
2565
2566/*
2567 * Loops through the list of windows, loaded-buffers or non-loaded-buffers
2568 * (depending on flag) starting from buf and looking for a non-scanned
2569 * buffer (other than curbuf). curbuf is special, if it is called with
2570 * buf=curbuf then it has to be the first call for a given flag/expansion.
2571 *
2572 * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo
2573 */
2574 static buf_T *
2575ins_compl_next_buf(buf_T *buf, int flag)
2576{
2577 static win_T *wp = NULL;
2578
2579 if (flag == 'w') // just windows
2580 {
Bram Moolenaar0ff01832022-09-24 19:20:30 +01002581 if (buf == curbuf || !win_valid(wp))
2582 // first call for this flag/expansion or window was closed
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002583 wp = curwin;
2584 while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
2585 && wp->w_buffer->b_scanned)
2586 ;
2587 buf = wp->w_buffer;
2588 }
2589 else
2590 // 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U'
2591 // (unlisted buffers)
2592 // When completing whole lines skip unloaded buffers.
2593 while ((buf = (buf->b_next != NULL ? buf->b_next : firstbuf)) != curbuf
2594 && ((flag == 'U'
2595 ? buf->b_p_bl
2596 : (!buf->b_p_bl
2597 || (buf->b_ml.ml_mfp == NULL) != (flag == 'u')))
2598 || buf->b_scanned))
2599 ;
2600 return buf;
2601}
2602
2603#ifdef FEAT_COMPL_FUNC
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002604
2605# ifdef FEAT_EVAL
2606static callback_T cfu_cb; // 'completefunc' callback function
2607static callback_T ofu_cb; // 'omnifunc' callback function
2608static callback_T tsrfu_cb; // 'thesaurusfunc' callback function
2609# endif
2610
2611/*
2612 * Copy a global callback function to a buffer local callback.
2613 */
2614 static void
2615copy_global_to_buflocal_cb(callback_T *globcb, callback_T *bufcb)
2616{
2617 free_callback(bufcb);
2618 if (globcb->cb_name != NULL && *globcb->cb_name != NUL)
2619 copy_callback(bufcb, globcb);
2620}
2621
2622/*
2623 * Parse the 'completefunc' option value and set the callback function.
2624 * Invoked when the 'completefunc' option is set. The option value can be a
2625 * name of a function (string), or function(<name>) or funcref(<name>) or a
2626 * lambda expression.
2627 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002628 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00002629did_set_completefunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002630{
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002631 if (option_set_callback_func(curbuf->b_p_cfu, &cfu_cb) == FAIL)
2632 return e_invalid_argument;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002633
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002634 set_buflocal_cfu_callback(curbuf);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002635
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002636 return NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002637}
2638
2639/*
2640 * Copy the global 'completefunc' callback function to the buffer-local
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002641 * 'completefunc' callback for "buf".
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002642 */
2643 void
2644set_buflocal_cfu_callback(buf_T *buf UNUSED)
2645{
2646# ifdef FEAT_EVAL
2647 copy_global_to_buflocal_cb(&cfu_cb, &buf->b_cfu_cb);
2648# endif
2649}
2650
2651/*
2652 * Parse the 'omnifunc' option value and set the callback function.
2653 * Invoked when the 'omnifunc' option is set. The option value can be a
2654 * name of a function (string), or function(<name>) or funcref(<name>) or a
2655 * lambda expression.
2656 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002657 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00002658did_set_omnifunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002659{
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002660 if (option_set_callback_func(curbuf->b_p_ofu, &ofu_cb) == FAIL)
2661 return e_invalid_argument;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002662
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002663 set_buflocal_ofu_callback(curbuf);
2664 return NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002665}
2666
2667/*
2668 * Copy the global 'omnifunc' callback function to the buffer-local 'omnifunc'
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002669 * callback for "buf".
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002670 */
2671 void
2672set_buflocal_ofu_callback(buf_T *buf UNUSED)
2673{
2674# ifdef FEAT_EVAL
2675 copy_global_to_buflocal_cb(&ofu_cb, &buf->b_ofu_cb);
2676# endif
2677}
2678
2679/*
2680 * Parse the 'thesaurusfunc' option value and set the callback function.
2681 * Invoked when the 'thesaurusfunc' option is set. The option value can be a
2682 * name of a function (string), or function(<name>) or funcref(<name>) or a
2683 * lambda expression.
2684 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002685 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00002686did_set_thesaurusfunc(optset_T *args UNUSED)
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002687{
2688 int retval;
2689
2690 if (*curbuf->b_p_tsrfu != NUL)
2691 {
2692 // buffer-local option set
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002693 retval = option_set_callback_func(curbuf->b_p_tsrfu,
2694 &curbuf->b_tsrfu_cb);
2695 }
2696 else
2697 {
2698 // global option set
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002699 retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
2700 }
2701
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00002702 return retval == FAIL ? e_invalid_argument : NULL;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002703}
2704
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00002705/*
2706 * Mark the global 'completefunc' 'omnifunc' and 'thesaurusfunc' callbacks with
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002707 * "copyID" so that they are not garbage collected.
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00002708 */
2709 int
2710set_ref_in_insexpand_funcs(int copyID)
2711{
2712 int abort = FALSE;
2713
2714 abort = set_ref_in_callback(&cfu_cb, copyID);
2715 abort = abort || set_ref_in_callback(&ofu_cb, copyID);
2716 abort = abort || set_ref_in_callback(&tsrfu_cb, copyID);
2717
2718 return abort;
2719}
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002720
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002721/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002722 * Get the user-defined completion function name for completion "type"
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01002723 */
2724 static char_u *
2725get_complete_funcname(int type)
2726{
2727 switch (type)
2728 {
2729 case CTRL_X_FUNCTION:
2730 return curbuf->b_p_cfu;
2731 case CTRL_X_OMNI:
2732 return curbuf->b_p_ofu;
2733 case CTRL_X_THESAURUS:
Bram Moolenaarf4d8b762021-10-17 14:13:09 +01002734 return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu;
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01002735 default:
2736 return (char_u *)"";
2737 }
2738}
2739
2740/*
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002741 * Get the callback to use for insert mode completion.
2742 */
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002743 static callback_T *
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002744get_insert_callback(int type)
2745{
2746 if (type == CTRL_X_FUNCTION)
2747 return &curbuf->b_cfu_cb;
2748 if (type == CTRL_X_OMNI)
2749 return &curbuf->b_ofu_cb;
2750 // CTRL_X_THESAURUS
2751 return (*curbuf->b_p_tsrfu != NUL) ? &curbuf->b_tsrfu_cb : &tsrfu_cb;
2752}
2753
2754/*
Yegappan Lakshmanan05e59e32021-12-01 10:30:07 +00002755 * Execute user defined complete function 'completefunc', 'omnifunc' or
2756 * 'thesaurusfunc', and get matches in "matches".
2757 * "type" is either CTRL_X_OMNI or CTRL_X_FUNCTION or CTRL_X_THESAURUS.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002758 */
2759 static void
Yegappan Lakshmanan05e59e32021-12-01 10:30:07 +00002760expand_by_function(int type, char_u *base)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002761{
2762 list_T *matchlist = NULL;
2763 dict_T *matchdict = NULL;
2764 typval_T args[3];
2765 char_u *funcname;
2766 pos_T pos;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002767 callback_T *cb;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002768 typval_T rettv;
2769 int save_State = State;
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002770 int retval;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002771
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01002772 funcname = get_complete_funcname(type);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002773 if (*funcname == NUL)
2774 return;
2775
2776 // Call 'completefunc' to obtain the list of matches.
2777 args[0].v_type = VAR_NUMBER;
2778 args[0].vval.v_number = 0;
2779 args[1].v_type = VAR_STRING;
2780 args[1].vval.v_string = base != NULL ? base : (char_u *)"";
2781 args[2].v_type = VAR_UNKNOWN;
2782
2783 pos = curwin->w_cursor;
Bram Moolenaar28976e22021-01-29 21:07:07 +01002784 // Lock the text to avoid weird things from happening. Also disallow
2785 // switching to another window, it should not be needed and may end up in
2786 // Insert mode in another buffer.
zeertzjqcfe45652022-05-27 17:26:55 +01002787 ++textlock;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002788
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002789 cb = get_insert_callback(type);
2790 retval = call_callback(cb, 0, &rettv, 2, args);
2791
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002792 // Call a function, which returns a list or dict.
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00002793 if (retval == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002794 {
2795 switch (rettv.v_type)
2796 {
2797 case VAR_LIST:
2798 matchlist = rettv.vval.v_list;
2799 break;
2800 case VAR_DICT:
2801 matchdict = rettv.vval.v_dict;
2802 break;
2803 case VAR_SPECIAL:
2804 if (rettv.vval.v_number == VVAL_NONE)
2805 compl_opt_suppress_empty = TRUE;
2806 // FALLTHROUGH
2807 default:
2808 // TODO: Give error message?
2809 clear_tv(&rettv);
2810 break;
2811 }
2812 }
zeertzjqcfe45652022-05-27 17:26:55 +01002813 --textlock;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002814
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002815 curwin->w_cursor = pos; // restore the cursor position
zeertzjq0a419e02024-04-02 19:01:14 +02002816 check_cursor(); // make sure cursor position is valid, just in case
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002817 validate_cursor();
2818 if (!EQUAL_POS(curwin->w_cursor, pos))
2819 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00002820 emsg(_(e_complete_function_deleted_text));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002821 goto theend;
2822 }
2823
2824 if (matchlist != NULL)
2825 ins_compl_add_list(matchlist);
2826 else if (matchdict != NULL)
2827 ins_compl_add_dict(matchdict);
2828
2829theend:
2830 // Restore State, it might have been changed.
2831 State = save_State;
2832
2833 if (matchdict != NULL)
2834 dict_unref(matchdict);
2835 if (matchlist != NULL)
2836 list_unref(matchlist);
2837}
2838#endif // FEAT_COMPL_FUNC
2839
2840#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) || defined(PROTO)
glepnir38f99a12024-08-23 18:31:06 +02002841
2842 static inline int
2843get_user_highlight_attr(char_u *hlname)
2844{
2845 if (hlname != NULL && *hlname != NUL)
2846 return syn_name2attr(hlname);
2847 return -1;
2848}
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002849/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002850 * Add a match to the list of matches from a typeval_T.
2851 * If the given string is already in the list of completions, then return
2852 * NOTDONE, otherwise add it to the list and return OK. If there is an error,
2853 * maybe because alloc() returns NULL, then FAIL is returned.
Bram Moolenaar440cf092021-04-03 20:13:30 +02002854 * When "fast" is TRUE use fast_breakcheck() instead of ui_breakcheck().
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002855 */
2856 static int
Bram Moolenaar440cf092021-04-03 20:13:30 +02002857ins_compl_add_tv(typval_T *tv, int dir, int fast)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002858{
2859 char_u *word;
2860 int dup = FALSE;
2861 int empty = FALSE;
Bram Moolenaar440cf092021-04-03 20:13:30 +02002862 int flags = fast ? CP_FAST : 0;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002863 char_u *(cptext[CPT_COUNT]);
Bram Moolenaar08928322020-01-04 14:32:48 +01002864 typval_T user_data;
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00002865 int status;
glepnir0fe17f82024-10-08 22:26:44 +02002866 char_u *user_abbr_hlname;
2867 int user_abbr_hlattr = -1;
glepnir38f99a12024-08-23 18:31:06 +02002868 char_u *user_kind_hlname;
glepnir38f99a12024-08-23 18:31:06 +02002869 int user_kind_hlattr = -1;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002870
Bram Moolenaar08928322020-01-04 14:32:48 +01002871 user_data.v_type = VAR_UNKNOWN;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002872 if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL)
2873 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01002874 word = dict_get_string(tv->vval.v_dict, "word", FALSE);
2875 cptext[CPT_ABBR] = dict_get_string(tv->vval.v_dict, "abbr", FALSE);
2876 cptext[CPT_MENU] = dict_get_string(tv->vval.v_dict, "menu", FALSE);
2877 cptext[CPT_KIND] = dict_get_string(tv->vval.v_dict, "kind", FALSE);
2878 cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict, "info", FALSE);
glepnir38f99a12024-08-23 18:31:06 +02002879
glepnir0fe17f82024-10-08 22:26:44 +02002880 user_abbr_hlname = dict_get_string(tv->vval.v_dict, "abbr_hlgroup", FALSE);
2881 user_abbr_hlattr = get_user_highlight_attr(user_abbr_hlname);
glepnir38f99a12024-08-23 18:31:06 +02002882
2883 user_kind_hlname = dict_get_string(tv->vval.v_dict, "kind_hlgroup", FALSE);
2884 user_kind_hlattr = get_user_highlight_attr(user_kind_hlname);
glepnir508e7852024-07-25 21:39:08 +02002885
Bram Moolenaard61efa52022-07-23 09:52:04 +01002886 dict_get_tv(tv->vval.v_dict, "user_data", &user_data);
2887 if (dict_get_string(tv->vval.v_dict, "icase", FALSE) != NULL
2888 && dict_get_number(tv->vval.v_dict, "icase"))
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002889 flags |= CP_ICASE;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002890 if (dict_get_string(tv->vval.v_dict, "dup", FALSE) != NULL)
2891 dup = dict_get_number(tv->vval.v_dict, "dup");
2892 if (dict_get_string(tv->vval.v_dict, "empty", FALSE) != NULL)
2893 empty = dict_get_number(tv->vval.v_dict, "empty");
2894 if (dict_get_string(tv->vval.v_dict, "equal", FALSE) != NULL
2895 && dict_get_number(tv->vval.v_dict, "equal"))
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002896 flags |= CP_EQUAL;
2897 }
2898 else
2899 {
2900 word = tv_get_string_chk(tv);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002901 CLEAR_FIELD(cptext);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002902 }
2903 if (word == NULL || (!empty && *word == NUL))
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00002904 {
2905 clear_tv(&user_data);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002906 return FAIL;
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00002907 }
glepnir508e7852024-07-25 21:39:08 +02002908 status = ins_compl_add(word, -1, NULL, cptext,
glepnir0fe17f82024-10-08 22:26:44 +02002909 &user_data, dir, flags, dup,
2910 user_abbr_hlattr, user_kind_hlattr);
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00002911 if (status != OK)
2912 clear_tv(&user_data);
2913 return status;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02002914}
2915
2916/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002917 * Add completions from a list.
2918 */
2919 static void
2920ins_compl_add_list(list_T *list)
2921{
2922 listitem_T *li;
2923 int dir = compl_direction;
2924
2925 // Go through the List with matches and add each of them.
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02002926 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002927 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002928 {
Bram Moolenaar440cf092021-04-03 20:13:30 +02002929 if (ins_compl_add_tv(&li->li_tv, dir, TRUE) == OK)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01002930 // if dir was BACKWARD then honor it just once
2931 dir = FORWARD;
2932 else if (did_emsg)
2933 break;
2934 }
2935}
2936
2937/*
2938 * Add completions from a dict.
2939 */
2940 static void
2941ins_compl_add_dict(dict_T *dict)
2942{
2943 dictitem_T *di_refresh;
2944 dictitem_T *di_words;
2945
2946 // Check for optional "refresh" item.
2947 compl_opt_refresh_always = FALSE;
2948 di_refresh = dict_find(dict, (char_u *)"refresh", 7);
2949 if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING)
2950 {
2951 char_u *v = di_refresh->di_tv.vval.v_string;
2952
2953 if (v != NULL && STRCMP(v, (char_u *)"always") == 0)
2954 compl_opt_refresh_always = TRUE;
2955 }
2956
2957 // Add completions from a "words" list.
2958 di_words = dict_find(dict, (char_u *)"words", 5);
2959 if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST)
2960 ins_compl_add_list(di_words->di_tv.vval.v_list);
2961}
2962
2963/*
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002964 * Start completion for the complete() function.
2965 * "startcol" is where the matched text starts (1 is first column).
2966 * "list" is the list of matches.
2967 */
2968 static void
2969set_completion(colnr_T startcol, list_T *list)
2970{
2971 int save_w_wrow = curwin->w_wrow;
2972 int save_w_leftcol = curwin->w_leftcol;
2973 int flags = CP_ORIGINAL_TEXT;
zeertzjqaa925ee2024-06-09 18:24:05 +02002974 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02002975 int compl_longest = (cur_cot_flags & COT_LONGEST) != 0;
2976 int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
2977 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002978
2979 // If already doing completions stop it.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00002980 if (ctrl_x_mode_not_default())
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002981 ins_compl_prep(' ');
2982 ins_compl_clear();
2983 ins_compl_free();
bfredl87af60c2022-09-24 11:17:51 +01002984 compl_get_longest = compl_longest;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002985
2986 compl_direction = FORWARD;
2987 if (startcol > curwin->w_cursor.col)
2988 startcol = curwin->w_cursor.col;
2989 compl_col = startcol;
2990 compl_length = (int)curwin->w_cursor.col - (int)startcol;
2991 // compl_pattern doesn't need to be set
2992 compl_orig_text = vim_strnsave(ml_get_curline() + compl_col, compl_length);
2993 if (p_ic)
2994 flags |= CP_ICASE;
2995 if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
Bram Moolenaar440cf092021-04-03 20:13:30 +02002996 -1, NULL, NULL, NULL, 0,
glepnir38f99a12024-08-23 18:31:06 +02002997 flags | CP_FAST, FALSE, -1, -1) != OK)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02002998 return;
2999
3000 ctrl_x_mode = CTRL_X_EVAL;
3001
3002 ins_compl_add_list(list);
3003 compl_matches = ins_compl_make_cyclic();
3004 compl_started = TRUE;
3005 compl_used_match = TRUE;
3006 compl_cont_status = 0;
3007
3008 compl_curr_match = compl_first_match;
bfredl87af60c2022-09-24 11:17:51 +01003009 int no_select = compl_no_select || compl_longest;
3010 if (compl_no_insert || no_select)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003011 {
3012 ins_complete(K_DOWN, FALSE);
bfredl87af60c2022-09-24 11:17:51 +01003013 if (no_select)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003014 // Down/Up has no real effect.
3015 ins_complete(K_UP, FALSE);
3016 }
3017 else
3018 ins_complete(Ctrl_N, FALSE);
3019 compl_enter_selects = compl_no_insert;
3020
3021 // Lazily show the popup menu, unless we got interrupted.
3022 if (!compl_interrupted)
3023 show_pum(save_w_wrow, save_w_leftcol);
LemonBoy2bf52dd2022-04-09 18:17:34 +01003024 may_trigger_modechanged();
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003025 out_flush();
3026}
3027
3028/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003029 * "complete()" function
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003030 */
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003031 void
3032f_complete(typval_T *argvars, typval_T *rettv UNUSED)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003033{
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003034 int startcol;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003035
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003036 if (in_vim9script()
3037 && (check_for_number_arg(argvars, 0) == FAIL
3038 || check_for_list_arg(argvars, 1) == FAIL))
3039 return;
3040
Bram Moolenaar24959102022-05-07 20:01:16 +01003041 if ((State & MODE_INSERT) == 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003042 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00003043 emsg(_(e_complete_can_only_be_used_in_insert_mode));
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003044 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003045 }
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003046
3047 // Check for undo allowed here, because if something was already inserted
3048 // the line was already saved for undo and this check isn't done.
3049 if (!undo_allowed())
3050 return;
3051
Bram Moolenaard83392a2022-09-01 12:22:46 +01003052 if (check_for_nonnull_list_arg(argvars, 1) != FAIL)
Bram Moolenaarff06f282020-04-21 22:01:14 +02003053 {
3054 startcol = (int)tv_get_number_chk(&argvars[0], NULL);
3055 if (startcol > 0)
3056 set_completion(startcol - 1, argvars[1].vval.v_list);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003057 }
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003058}
3059
3060/*
3061 * "complete_add()" function
3062 */
3063 void
3064f_complete_add(typval_T *argvars, typval_T *rettv)
3065{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003066 if (in_vim9script() && check_for_string_or_dict_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003067 return;
3068
Bram Moolenaar440cf092021-04-03 20:13:30 +02003069 rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, FALSE);
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003070}
3071
3072/*
3073 * "complete_check()" function
3074 */
3075 void
3076f_complete_check(typval_T *argvars UNUSED, typval_T *rettv)
3077{
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003078 int save_RedrawingDisabled = RedrawingDisabled;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003079 RedrawingDisabled = 0;
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003080
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003081 ins_compl_check_keys(0, TRUE);
3082 rettv->vval.v_number = ins_compl_interrupted();
Bram Moolenaar79cdf022023-05-20 14:07:00 +01003083
3084 RedrawingDisabled = save_RedrawingDisabled;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003085}
3086
3087/*
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003088 * Return Insert completion mode name string
3089 */
3090 static char_u *
3091ins_compl_mode(void)
3092{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003093 if (ctrl_x_mode_not_defined_yet() || ctrl_x_mode_scroll() || compl_started)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003094 return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
3095
3096 return (char_u *)"";
3097}
3098
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00003099/*
3100 * Assign the sequence number to all the completion matches which don't have
3101 * one assigned yet.
3102 */
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003103 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00003104ins_compl_update_sequence_numbers(void)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003105{
3106 int number = 0;
3107 compl_T *match;
3108
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003109 if (compl_dir_forward())
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003110 {
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003111 // Search backwards for the first valid (!= -1) number.
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003112 // This should normally succeed already at the first loop
3113 // cycle, so it's fast!
3114 for (match = compl_curr_match->cp_prev; match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003115 && !is_first_match(match); match = match->cp_prev)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003116 if (match->cp_number != -1)
3117 {
3118 number = match->cp_number;
3119 break;
3120 }
3121 if (match != NULL)
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003122 // go up and assign all numbers which are not assigned yet
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003123 for (match = match->cp_next;
3124 match != NULL && match->cp_number == -1;
3125 match = match->cp_next)
3126 match->cp_number = ++number;
3127 }
3128 else // BACKWARD
3129 {
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003130 // Search forwards (upwards) for the first valid (!= -1)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003131 // number. This should normally succeed already at the
3132 // first loop cycle, so it's fast!
3133 for (match = compl_curr_match->cp_next; match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003134 && !is_first_match(match); match = match->cp_next)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003135 if (match->cp_number != -1)
3136 {
3137 number = match->cp_number;
3138 break;
3139 }
3140 if (match != NULL)
Bram Moolenaarf39d9e92023-04-22 22:54:40 +01003141 // go down and assign all numbers which are not assigned yet
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003142 for (match = match->cp_prev; match
3143 && match->cp_number == -1;
3144 match = match->cp_prev)
3145 match->cp_number = ++number;
3146 }
3147}
3148
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003149/*
3150 * Get complete information
3151 */
3152 static void
3153get_complete_info(list_T *what_list, dict_T *retdict)
3154{
3155 int ret = OK;
3156 listitem_T *item;
3157#define CI_WHAT_MODE 0x01
3158#define CI_WHAT_PUM_VISIBLE 0x02
3159#define CI_WHAT_ITEMS 0x04
3160#define CI_WHAT_SELECTED 0x08
3161#define CI_WHAT_INSERTED 0x10
3162#define CI_WHAT_ALL 0xff
3163 int what_flag;
3164
3165 if (what_list == NULL)
3166 what_flag = CI_WHAT_ALL;
3167 else
3168 {
3169 what_flag = 0;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02003170 CHECK_LIST_MATERIALIZE(what_list);
Bram Moolenaaraeea7212020-04-02 18:50:46 +02003171 FOR_ALL_LIST_ITEMS(what_list, item)
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003172 {
3173 char_u *what = tv_get_string(&item->li_tv);
3174
3175 if (STRCMP(what, "mode") == 0)
3176 what_flag |= CI_WHAT_MODE;
3177 else if (STRCMP(what, "pum_visible") == 0)
3178 what_flag |= CI_WHAT_PUM_VISIBLE;
3179 else if (STRCMP(what, "items") == 0)
3180 what_flag |= CI_WHAT_ITEMS;
3181 else if (STRCMP(what, "selected") == 0)
3182 what_flag |= CI_WHAT_SELECTED;
3183 else if (STRCMP(what, "inserted") == 0)
3184 what_flag |= CI_WHAT_INSERTED;
3185 }
3186 }
3187
3188 if (ret == OK && (what_flag & CI_WHAT_MODE))
3189 ret = dict_add_string(retdict, "mode", ins_compl_mode());
3190
3191 if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE))
3192 ret = dict_add_number(retdict, "pum_visible", pum_visible());
3193
Girish Palya8950bf72024-03-20 20:07:29 +01003194 if (ret == OK && (what_flag & CI_WHAT_ITEMS || what_flag & CI_WHAT_SELECTED))
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003195 {
Christian Brabandt9eb236f2024-03-21 20:12:59 +01003196 list_T *li = NULL;
Girish Palya8950bf72024-03-20 20:07:29 +01003197 dict_T *di;
3198 compl_T *match;
3199 int selected_idx = -1;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003200
Girish Palya8950bf72024-03-20 20:07:29 +01003201 if (what_flag & CI_WHAT_ITEMS)
3202 {
3203 li = list_alloc();
3204 if (li == NULL)
3205 return;
3206 ret = dict_add_list(retdict, "items", li);
3207 }
3208 if (ret == OK && what_flag & CI_WHAT_SELECTED)
3209 if (compl_curr_match != NULL && compl_curr_match->cp_number == -1)
3210 ins_compl_update_sequence_numbers();
3211 if (ret == OK && compl_first_match != NULL)
3212 {
3213 int list_idx = 0;
3214 match = compl_first_match;
3215 do
3216 {
3217 if (!match_at_original_text(match))
3218 {
3219 if (what_flag & CI_WHAT_ITEMS)
3220 {
3221 di = dict_alloc();
3222 if (di == NULL)
3223 return;
3224 ret = list_append_dict(li, di);
3225 if (ret != OK)
3226 return;
3227 dict_add_string(di, "word", match->cp_str);
3228 dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]);
3229 dict_add_string(di, "menu", match->cp_text[CPT_MENU]);
3230 dict_add_string(di, "kind", match->cp_text[CPT_KIND]);
3231 dict_add_string(di, "info", match->cp_text[CPT_INFO]);
3232 if (match->cp_user_data.v_type == VAR_UNKNOWN)
3233 // Add an empty string for backwards compatibility
3234 dict_add_string(di, "user_data", (char_u *)"");
3235 else
3236 dict_add_tv(di, "user_data", &match->cp_user_data);
3237 }
3238 if (compl_curr_match != NULL && compl_curr_match->cp_number == match->cp_number)
3239 selected_idx = list_idx;
3240 list_idx += 1;
3241 }
3242 match = match->cp_next;
3243 }
3244 while (match != NULL && !is_first_match(match));
3245 }
3246 if (ret == OK && (what_flag & CI_WHAT_SELECTED))
3247 ret = dict_add_number(retdict, "selected", selected_idx);
Bram Moolenaarf9d51352020-10-26 19:22:42 +01003248 }
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003249
Yegappan Lakshmanan6b085b92022-09-04 12:47:21 +01003250 if (ret == OK && (what_flag & CI_WHAT_INSERTED))
3251 {
3252 // TODO
3253 }
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02003254}
3255
3256/*
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003257 * "complete_info()" function
3258 */
3259 void
3260f_complete_info(typval_T *argvars, typval_T *rettv)
3261{
3262 list_T *what_list = NULL;
3263
Bram Moolenaar93a10962022-06-16 11:42:09 +01003264 if (rettv_dict_alloc(rettv) == FAIL)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003265 return;
3266
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003267 if (in_vim9script() && check_for_opt_list_arg(argvars, 0) == FAIL)
3268 return;
3269
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003270 if (argvars[0].v_type != VAR_UNKNOWN)
3271 {
Bram Moolenaard83392a2022-09-01 12:22:46 +01003272 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003273 return;
Bram Moolenaar9bca58f2019-08-15 21:31:52 +02003274 what_list = argvars[0].vval.v_list;
3275 }
3276 get_complete_info(what_list, rettv->vval.v_dict);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003277}
3278#endif
3279
3280/*
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01003281 * Returns TRUE when using a user-defined function for thesaurus completion.
3282 */
3283 static int
3284thesaurus_func_complete(int type UNUSED)
3285{
3286#ifdef FEAT_COMPL_FUNC
Bram Moolenaarf4d8b762021-10-17 14:13:09 +01003287 return type == CTRL_X_THESAURUS
3288 && (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL);
Yegappan Lakshmanan160e9942021-10-16 15:41:29 +01003289#else
3290 return FALSE;
3291#endif
3292}
3293
3294/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003295 * Return value of process_next_cpt_value()
3296 */
3297enum
3298{
3299 INS_COMPL_CPT_OK = 1,
3300 INS_COMPL_CPT_CONT,
3301 INS_COMPL_CPT_END
3302};
3303
3304/*
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003305 * state information used for getting the next set of insert completion
3306 * matches.
3307 */
3308typedef struct
3309{
Bram Moolenaar0ff01832022-09-24 19:20:30 +01003310 char_u *e_cpt_copy; // copy of 'complete'
3311 char_u *e_cpt; // current entry in "e_cpt_copy"
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003312 buf_T *ins_buf; // buffer being scanned
Bram Moolenaar0ff01832022-09-24 19:20:30 +01003313 pos_T *cur_match_pos; // current match position
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003314 pos_T prev_match_pos; // previous match position
3315 int set_match_pos; // save first_match_pos/last_match_pos
3316 pos_T first_match_pos; // first match position
3317 pos_T last_match_pos; // last match position
3318 int found_all; // found all matches of a certain type.
3319 char_u *dict; // dictionary file to search
3320 int dict_f; // "dict" is an exact file name or not
3321} ins_compl_next_state_T;
3322
3323/*
3324 * Process the next 'complete' option value in st->e_cpt.
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003325 *
3326 * If successful, the arguments are set as below:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003327 * st->cpt - pointer to the next option value in "st->cpt"
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003328 * compl_type_arg - type of insert mode completion to use
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003329 * st->found_all - all matches of this type are found
3330 * st->ins_buf - search for completions in this buffer
3331 * st->first_match_pos - position of the first completion match
3332 * st->last_match_pos - position of the last completion match
3333 * st->set_match_pos - TRUE if the first match position should be saved to
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01003334 * avoid loops after the search wraps around.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003335 * st->dict - name of the dictionary or thesaurus file to search
3336 * st->dict_f - flag specifying whether "dict" is an exact file name or not
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003337 *
3338 * Returns INS_COMPL_CPT_OK if the next value is processed successfully.
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00003339 * Returns INS_COMPL_CPT_CONT to skip the current completion source matching
3340 * the "st->e_cpt" option value and process the next matching source.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003341 * Returns INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003342 */
3343 static int
3344process_next_cpt_value(
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003345 ins_compl_next_state_T *st,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003346 int *compl_type_arg,
glepnir8159fb12024-07-17 20:32:54 +02003347 pos_T *start_match_pos,
3348 int in_fuzzy)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003349{
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003350 int compl_type = -1;
3351 int status = INS_COMPL_CPT_OK;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003352
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003353 st->found_all = FALSE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003354
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003355 while (*st->e_cpt == ',' || *st->e_cpt == ' ')
3356 st->e_cpt++;
3357
3358 if (*st->e_cpt == '.' && !curbuf->b_scanned)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003359 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003360 st->ins_buf = curbuf;
3361 st->first_match_pos = *start_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003362 // Move the cursor back one character so that ^N can match the
3363 // word immediately after the cursor.
glepnir8159fb12024-07-17 20:32:54 +02003364 if (ctrl_x_mode_normal() && (!in_fuzzy && dec(&st->first_match_pos) < 0))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003365 {
3366 // Move the cursor to after the last character in the
3367 // buffer, so that word at start of buffer is found
3368 // correctly.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003369 st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count;
zeertzjq94b7c322024-03-12 21:50:32 +01003370 st->first_match_pos.col = ml_get_len(st->first_match_pos.lnum);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003371 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003372 st->last_match_pos = st->first_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003373 compl_type = 0;
3374
3375 // Remember the first match so that the loop stops when we
3376 // wrap and come back there a second time.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003377 st->set_match_pos = TRUE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003378 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003379 else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL
Bram Moolenaar0ff01832022-09-24 19:20:30 +01003380 && (st->ins_buf = ins_compl_next_buf(
3381 st->ins_buf, *st->e_cpt)) != curbuf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003382 {
3383 // Scan a buffer, but not the current one.
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003384 if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003385 {
3386 compl_started = TRUE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003387 st->first_match_pos.col = st->last_match_pos.col = 0;
3388 st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1;
3389 st->last_match_pos.lnum = 0;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003390 compl_type = 0;
3391 }
3392 else // unloaded buffer, scan like dictionary
3393 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003394 st->found_all = TRUE;
3395 if (st->ins_buf->b_fname == NULL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003396 {
3397 status = INS_COMPL_CPT_CONT;
3398 goto done;
3399 }
3400 compl_type = CTRL_X_DICTIONARY;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003401 st->dict = st->ins_buf->b_fname;
3402 st->dict_f = DICT_EXACT;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003403 }
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01003404 if (!shortmess(SHM_COMPLETIONSCAN))
3405 {
3406 msg_hist_off = TRUE; // reset in msg_trunc_attr()
3407 vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
3408 st->ins_buf->b_fname == NULL
3409 ? buf_spname(st->ins_buf)
3410 : st->ins_buf->b_sfname == NULL
3411 ? st->ins_buf->b_fname
3412 : st->ins_buf->b_sfname);
3413 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
3414 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003415 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003416 else if (*st->e_cpt == NUL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003417 status = INS_COMPL_CPT_END;
3418 else
3419 {
3420 if (ctrl_x_mode_line_or_eval())
3421 compl_type = -1;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003422 else if (*st->e_cpt == 'k' || *st->e_cpt == 's')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003423 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003424 if (*st->e_cpt == 'k')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003425 compl_type = CTRL_X_DICTIONARY;
3426 else
3427 compl_type = CTRL_X_THESAURUS;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003428 if (*++st->e_cpt != ',' && *st->e_cpt != NUL)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003429 {
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003430 st->dict = st->e_cpt;
3431 st->dict_f = DICT_FIRST;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003432 }
3433 }
3434#ifdef FEAT_FIND_ID
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003435 else if (*st->e_cpt == 'i')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003436 compl_type = CTRL_X_PATH_PATTERNS;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003437 else if (*st->e_cpt == 'd')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003438 compl_type = CTRL_X_PATH_DEFINES;
3439#endif
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003440 else if (*st->e_cpt == ']' || *st->e_cpt == 't')
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003441 {
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003442 compl_type = CTRL_X_TAGS;
=?UTF-8?q?Bj=C3=B6rn=20Linse?=91ccbad2022-10-13 12:51:13 +01003443 if (!shortmess(SHM_COMPLETIONSCAN))
3444 {
3445 msg_hist_off = TRUE; // reset in msg_trunc_attr()
3446 vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags."));
3447 (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
3448 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003449 }
3450 else
3451 compl_type = -1;
3452
3453 // in any case e_cpt is advanced to the next entry
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003454 (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ",");
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003455
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003456 st->found_all = TRUE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003457 if (compl_type == -1)
3458 status = INS_COMPL_CPT_CONT;
3459 }
3460
3461done:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003462 *compl_type_arg = compl_type;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003463 return status;
3464}
3465
3466#ifdef FEAT_FIND_ID
3467/*
3468 * Get the next set of identifiers or defines matching "compl_pattern" in
3469 * included files.
3470 */
3471 static void
3472get_next_include_file_completion(int compl_type)
3473{
3474 find_pattern_in_path(compl_pattern, compl_direction,
Mike Williams16b63bd2024-06-01 11:33:40 +02003475 (int)compl_patternlen, FALSE, FALSE,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003476 (compl_type == CTRL_X_PATH_DEFINES
3477 && !(compl_cont_status & CONT_SOL))
3478 ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND,
Colin Kennedy21570352024-03-03 16:16:47 +01003479 (linenr_T)1, (linenr_T)MAXLNUM, FALSE);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003480}
3481#endif
3482
3483/*
3484 * Get the next set of words matching "compl_pattern" in dictionary or
3485 * thesaurus files.
3486 */
3487 static void
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003488get_next_dict_tsr_completion(int compl_type, char_u *dict, int dict_f)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003489{
3490#ifdef FEAT_COMPL_FUNC
3491 if (thesaurus_func_complete(compl_type))
3492 expand_by_function(compl_type, compl_pattern);
3493 else
3494#endif
3495 ins_compl_dictionaries(
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003496 dict != NULL ? dict
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003497 : (compl_type == CTRL_X_THESAURUS
3498 ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
3499 : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
3500 compl_pattern,
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003501 dict != NULL ? dict_f : 0,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003502 compl_type == CTRL_X_THESAURUS);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003503}
3504
3505/*
3506 * Get the next set of tag names matching "compl_pattern".
3507 */
3508 static void
3509get_next_tag_completion(void)
3510{
3511 int save_p_ic;
3512 char_u **matches;
3513 int num_matches;
3514
3515 // set p_ic according to p_ic, p_scs and pat for find_tags().
3516 save_p_ic = p_ic;
3517 p_ic = ignorecase(compl_pattern);
3518
3519 // Find up to TAG_MANY matches. Avoids that an enormous number
3520 // of matches is found when compl_pattern is empty
3521 g_tag_at_cursor = TRUE;
3522 if (find_tags(compl_pattern, &num_matches, &matches,
3523 TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003524 | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003525 TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0)
3526 ins_compl_add_matches(num_matches, matches, p_ic);
3527 g_tag_at_cursor = FALSE;
3528 p_ic = save_p_ic;
3529}
3530
3531/*
glepnir8159fb12024-07-17 20:32:54 +02003532 * Compare function for qsort
3533 */
3534static int compare_scores(const void *a, const void *b)
3535{
3536 int idx_a = *(const int *)a;
3537 int idx_b = *(const int *)b;
3538 int score_a = compl_fuzzy_scores[idx_a];
3539 int score_b = compl_fuzzy_scores[idx_b];
zeertzjq58d70522024-08-31 17:05:39 +02003540 return score_a == score_b ? (idx_a == idx_b ? 0 : (idx_a < idx_b ? -1 : 1))
3541 : (score_a > score_b ? -1 : 1);
glepnir8159fb12024-07-17 20:32:54 +02003542}
3543
3544/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003545 * Get the next set of filename matching "compl_pattern".
3546 */
3547 static void
3548get_next_filename_completion(void)
3549{
3550 char_u **matches;
3551 int num_matches;
glepnir8159fb12024-07-17 20:32:54 +02003552 char_u *ptr;
3553 garray_T fuzzy_indices;
3554 int i;
3555 int score;
3556 char_u *leader = ins_compl_leader();
Ken Takata073cb022024-07-28 17:08:15 +02003557 size_t leader_len = STRLEN(leader);
glepnir8159fb12024-07-17 20:32:54 +02003558 int in_fuzzy = ((get_cot_flags() & COT_FUZZY) != 0 && leader_len > 0);
3559 char_u **sorted_matches;
3560 int *fuzzy_indices_data;
glepnir0be03e12024-07-19 16:45:05 +02003561 char_u *last_sep = NULL;
3562 size_t path_with_wildcard_len;
3563 char_u *path_with_wildcard;
glepnir8159fb12024-07-17 20:32:54 +02003564
glepnirb9de1a02024-08-02 19:14:38 +02003565#ifdef BACKSLASH_IN_FILENAME
3566 char pathsep = (curbuf->b_p_csl[0] == 's') ?
3567 '/' : (curbuf->b_p_csl[0] == 'b') ? '\\' : PATHSEP;
3568#else
3569 char pathsep = PATHSEP;
3570#endif
3571
glepnir8159fb12024-07-17 20:32:54 +02003572 if (in_fuzzy)
3573 {
glepnirb9de1a02024-08-02 19:14:38 +02003574#ifdef BACKSLASH_IN_FILENAME
3575 if (curbuf->b_p_csl[0] == 's')
3576 {
3577 for (i = 0; i < leader_len; i++)
3578 {
3579 if (leader[i] == '\\')
3580 leader[i] = '/';
3581 }
3582 }
3583 else if (curbuf->b_p_csl[0] == 'b')
3584 {
3585 for (i = 0; i < leader_len; i++)
3586 {
3587 if (leader[i] == '/')
3588 leader[i] = '\\';
3589 }
3590 }
3591#endif
3592 last_sep = vim_strrchr(leader, pathsep);
glepnir0be03e12024-07-19 16:45:05 +02003593 if (last_sep == NULL)
3594 {
3595 // No path separator or separator is the last character,
3596 // fuzzy match the whole leader
3597 vim_free(compl_pattern);
3598 compl_pattern = vim_strsave((char_u *)"*");
3599 compl_patternlen = STRLEN(compl_pattern);
3600 }
3601 else if (*(last_sep + 1) == '\0')
3602 in_fuzzy = FALSE;
3603 else
3604 {
3605 // Split leader into path and file parts
3606 int path_len = last_sep - leader + 1;
3607 path_with_wildcard_len = path_len + 2;
3608 path_with_wildcard = alloc(path_with_wildcard_len);
3609 if (path_with_wildcard != NULL)
3610 {
3611 vim_strncpy(path_with_wildcard, leader, path_len);
3612 vim_strcat(path_with_wildcard, (char_u *)"*", path_with_wildcard_len);
3613 vim_free(compl_pattern);
3614 compl_pattern = path_with_wildcard;
3615 compl_patternlen = STRLEN(compl_pattern);
3616
3617 // Move leader to the file part
3618 leader = last_sep + 1;
glepnir6b6280c2024-07-27 16:25:45 +02003619 leader_len = STRLEN(leader);
glepnir0be03e12024-07-19 16:45:05 +02003620 }
3621 }
glepnir8159fb12024-07-17 20:32:54 +02003622 }
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003623
3624 if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
3625 EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK)
3626 return;
3627
3628 // May change home directory back to "~".
3629 tilde_replace(compl_pattern, num_matches, matches);
3630#ifdef BACKSLASH_IN_FILENAME
3631 if (curbuf->b_p_csl[0] != NUL)
3632 {
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003633 for (i = 0; i < num_matches; ++i)
3634 {
glepnir8159fb12024-07-17 20:32:54 +02003635 ptr = matches[i];
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003636 while (*ptr != NUL)
3637 {
3638 if (curbuf->b_p_csl[0] == 's' && *ptr == '\\')
3639 *ptr = '/';
3640 else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/')
3641 *ptr = '\\';
3642 ptr += (*mb_ptr2len)(ptr);
3643 }
3644 }
3645 }
3646#endif
glepnir8159fb12024-07-17 20:32:54 +02003647
3648 if (in_fuzzy)
3649 {
3650 ga_init2(&fuzzy_indices, sizeof(int), 10);
3651 compl_fuzzy_scores = (int *)alloc(sizeof(int) * num_matches);
3652
3653 for (i = 0; i < num_matches; i++)
3654 {
3655 ptr = matches[i];
3656 score = fuzzy_match_str(ptr, leader);
3657 if (score > 0)
3658 {
3659 if (ga_grow(&fuzzy_indices, 1) == OK)
3660 {
3661 ((int *)fuzzy_indices.ga_data)[fuzzy_indices.ga_len] = i;
3662 compl_fuzzy_scores[i] = score;
3663 fuzzy_indices.ga_len++;
3664 }
3665 }
3666 }
3667
Christian Brabandt220474d2024-07-20 13:26:44 +02003668 // prevent qsort from deref NULL pointer
3669 if (fuzzy_indices.ga_len > 0)
3670 {
3671 fuzzy_indices_data = (int *)fuzzy_indices.ga_data;
3672 qsort(fuzzy_indices_data, fuzzy_indices.ga_len, sizeof(int), compare_scores);
glepnir8159fb12024-07-17 20:32:54 +02003673
Christian Brabandt220474d2024-07-20 13:26:44 +02003674 sorted_matches = (char_u **)alloc(sizeof(char_u *) * fuzzy_indices.ga_len);
3675 for (i = 0; i < fuzzy_indices.ga_len; ++i)
3676 sorted_matches[i] = vim_strsave(matches[fuzzy_indices_data[i]]);
glepnir8159fb12024-07-17 20:32:54 +02003677
Christian Brabandt220474d2024-07-20 13:26:44 +02003678 FreeWild(num_matches, matches);
3679 matches = sorted_matches;
3680 num_matches = fuzzy_indices.ga_len;
3681 }
glepnir6b6280c2024-07-27 16:25:45 +02003682 else if (leader_len > 0)
3683 {
3684 FreeWild(num_matches, matches);
3685 num_matches = 0;
3686 }
Christian Brabandt220474d2024-07-20 13:26:44 +02003687
glepnir8159fb12024-07-17 20:32:54 +02003688 vim_free(compl_fuzzy_scores);
3689 ga_clear(&fuzzy_indices);
3690 }
3691
glepnir6b6280c2024-07-27 16:25:45 +02003692 if (num_matches > 0)
3693 ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003694}
3695
3696/*
3697 * Get the next set of command-line completions matching "compl_pattern".
3698 */
3699 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00003700get_next_cmdline_completion(void)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003701{
3702 char_u **matches;
3703 int num_matches;
3704
3705 if (expand_cmdline(&compl_xp, compl_pattern,
Mike Williams16b63bd2024-06-01 11:33:40 +02003706 (int)compl_patternlen, &num_matches, &matches) == EXPAND_OK)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003707 ins_compl_add_matches(num_matches, matches, FALSE);
3708}
3709
3710/*
3711 * Get the next set of spell suggestions matching "compl_pattern".
3712 */
3713 static void
3714get_next_spell_completion(linenr_T lnum UNUSED)
3715{
3716#ifdef FEAT_SPELL
3717 char_u **matches;
3718 int num_matches;
3719
3720 num_matches = expand_spelling(lnum, compl_pattern, &matches);
3721 if (num_matches > 0)
3722 ins_compl_add_matches(num_matches, matches, p_ic);
Bram Moolenaar8e7cc6b2021-12-30 10:32:25 +00003723 else
3724 vim_free(matches);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003725#endif
3726}
3727
3728/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00003729 * Return the next word or line from buffer "ins_buf" at position
3730 * "cur_match_pos" for completion. The length of the match is set in "len".
3731 */
3732 static char_u *
zeertzjq440d4cb2023-03-02 17:51:32 +00003733ins_compl_get_next_word_or_line(
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00003734 buf_T *ins_buf, // buffer being scanned
3735 pos_T *cur_match_pos, // current match position
3736 int *match_len,
3737 int *cont_s_ipos) // next ^X<> will set initial_pos
3738{
3739 char_u *ptr;
3740 int len;
3741
3742 *match_len = 0;
3743 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) +
3744 cur_match_pos->col;
3745 if (ctrl_x_mode_line_or_eval())
3746 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003747 if (compl_status_adding())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00003748 {
3749 if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count)
3750 return NULL;
3751 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
3752 if (!p_paste)
3753 ptr = skipwhite(ptr);
3754 }
3755 len = (int)STRLEN(ptr);
3756 }
3757 else
3758 {
3759 char_u *tmp_ptr = ptr;
3760
Bram Moolenaara6f9e302022-07-28 21:51:37 +01003761 if (compl_status_adding() && compl_length <= (int)STRLEN(tmp_ptr))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00003762 {
3763 tmp_ptr += compl_length;
3764 // Skip if already inside a word.
3765 if (vim_iswordp(tmp_ptr))
3766 return NULL;
3767 // Find start of next word.
3768 tmp_ptr = find_word_start(tmp_ptr);
3769 }
3770 // Find end of this word.
3771 tmp_ptr = find_word_end(tmp_ptr);
3772 len = (int)(tmp_ptr - ptr);
3773
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003774 if (compl_status_adding() && len == compl_length)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00003775 {
3776 if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count)
3777 {
3778 // Try next line, if any. the new word will be
3779 // "join" as if the normal command "J" was used.
3780 // IOSIZE is always greater than
3781 // compl_length, so the next STRNCPY always
3782 // works -- Acevedo
3783 STRNCPY(IObuff, ptr, len);
3784 ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
3785 tmp_ptr = ptr = skipwhite(ptr);
3786 // Find start of next word.
3787 tmp_ptr = find_word_start(tmp_ptr);
3788 // Find end of next word.
3789 tmp_ptr = find_word_end(tmp_ptr);
3790 if (tmp_ptr > ptr)
3791 {
3792 if (*ptr != ')' && IObuff[len - 1] != TAB)
3793 {
3794 if (IObuff[len - 1] != ' ')
3795 IObuff[len++] = ' ';
3796 // IObuf =~ "\k.* ", thus len >= 2
3797 if (p_js
3798 && (IObuff[len - 2] == '.'
3799 || (vim_strchr(p_cpo, CPO_JOINSP)
3800 == NULL
3801 && (IObuff[len - 2] == '?'
3802 || IObuff[len - 2] == '!'))))
3803 IObuff[len++] = ' ';
3804 }
3805 // copy as much as possible of the new word
3806 if (tmp_ptr - ptr >= IOSIZE - len)
3807 tmp_ptr = ptr + IOSIZE - len - 1;
3808 STRNCPY(IObuff + len, ptr, tmp_ptr - ptr);
3809 len += (int)(tmp_ptr - ptr);
3810 *cont_s_ipos = TRUE;
3811 }
3812 IObuff[len] = NUL;
3813 ptr = IObuff;
3814 }
3815 if (len == compl_length)
3816 return NULL;
3817 }
3818 }
3819
3820 *match_len = len;
3821 return ptr;
3822}
3823
3824/*
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003825 * Get the next set of words matching "compl_pattern" for default completion(s)
3826 * (normal ^P/^N and ^X^L).
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003827 * Search for "compl_pattern" in the buffer "st->ins_buf" starting from the
3828 * position "st->start_pos" in the "compl_direction" direction. If
3829 * "st->set_match_pos" is TRUE, then set the "st->first_match_pos" and
3830 * "st->last_match_pos".
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003831 * Returns OK if a new next match is found, otherwise returns FAIL.
3832 */
3833 static int
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003834get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003835{
3836 int found_new_match = FAIL;
3837 int save_p_scs;
3838 int save_p_ws;
3839 int looped_around = FALSE;
glepnir8159fb12024-07-17 20:32:54 +02003840 char_u *ptr = NULL;
3841 int len = 0;
3842 int in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0 && compl_length > 0;
3843 char_u *leader = ins_compl_leader();
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003844
3845 // If 'infercase' is set, don't use 'smartcase' here
3846 save_p_scs = p_scs;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003847 if (st->ins_buf->b_p_inf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003848 p_scs = FALSE;
3849
3850 // Buffers other than curbuf are scanned from the beginning or the
3851 // end but never from the middle, thus setting nowrapscan in this
3852 // buffer is a good idea, on the other hand, we always set
3853 // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
3854 save_p_ws = p_ws;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003855 if (st->ins_buf != curbuf)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003856 p_ws = FALSE;
glepnir8159fb12024-07-17 20:32:54 +02003857 else if (*st->e_cpt == '.' && !in_fuzzy)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003858 p_ws = TRUE;
3859 looped_around = FALSE;
3860 for (;;)
3861 {
3862 int cont_s_ipos = FALSE;
3863
3864 ++msg_silent; // Don't want messages for wrapscan.
3865
3866 // ctrl_x_mode_line_or_eval() || word-wise search that
3867 // has added a word that was at the beginning of the line
glepnir8159fb12024-07-17 20:32:54 +02003868 if ((ctrl_x_mode_whole_line() && !in_fuzzy) || ctrl_x_mode_eval() || (compl_cont_status & CONT_SOL))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003869 found_new_match = search_for_exact_line(st->ins_buf,
3870 st->cur_match_pos, compl_direction, compl_pattern);
glepnir8159fb12024-07-17 20:32:54 +02003871 else if (in_fuzzy)
3872 found_new_match = search_for_fuzzy_match(st->ins_buf,
3873 st->cur_match_pos, leader, compl_direction,
3874 start_pos, &len, &ptr, ctrl_x_mode_whole_line());
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003875 else
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003876 found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
John Marriott8c85a2a2024-05-20 19:18:26 +02003877 NULL, compl_direction, compl_pattern, compl_patternlen,
3878 1L, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003879 --msg_silent;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003880 if (!compl_started || st->set_match_pos)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003881 {
3882 // set "compl_started" even on fail
3883 compl_started = TRUE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003884 st->first_match_pos = *st->cur_match_pos;
3885 st->last_match_pos = *st->cur_match_pos;
3886 st->set_match_pos = FALSE;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003887 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003888 else if (st->first_match_pos.lnum == st->last_match_pos.lnum
3889 && st->first_match_pos.col == st->last_match_pos.col)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003890 {
3891 found_new_match = FAIL;
3892 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003893 else if (compl_dir_forward()
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003894 && (st->prev_match_pos.lnum > st->cur_match_pos->lnum
3895 || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
3896 && st->prev_match_pos.col >= st->cur_match_pos->col)))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003897 {
3898 if (looped_around)
3899 found_new_match = FAIL;
3900 else
3901 looped_around = TRUE;
3902 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003903 else if (!compl_dir_forward()
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003904 && (st->prev_match_pos.lnum < st->cur_match_pos->lnum
3905 || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
3906 && st->prev_match_pos.col <= st->cur_match_pos->col)))
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003907 {
3908 if (looped_around)
3909 found_new_match = FAIL;
3910 else
3911 looped_around = TRUE;
3912 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003913 st->prev_match_pos = *st->cur_match_pos;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003914 if (found_new_match == FAIL)
3915 break;
3916
3917 // when ADDING, the text before the cursor matches, skip it
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003918 if (compl_status_adding() && st->ins_buf == curbuf
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003919 && start_pos->lnum == st->cur_match_pos->lnum
3920 && start_pos->col == st->cur_match_pos->col)
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003921 continue;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003922
glepnir8159fb12024-07-17 20:32:54 +02003923 if (!in_fuzzy)
3924 ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
3925 &len, &cont_s_ipos);
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00003926 if (ptr == NULL)
3927 continue;
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003928
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003929 if (ins_compl_add_infercase(ptr, len, p_ic,
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003930 st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname,
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003931 0, cont_s_ipos) != NOTDONE)
3932 {
3933 found_new_match = OK;
3934 break;
3935 }
3936 }
3937 p_scs = save_p_scs;
3938 p_ws = save_p_ws;
3939
3940 return found_new_match;
3941}
3942
3943/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00003944 * get the next set of completion matches for "type".
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003945 * Returns TRUE if a new match is found. Otherwise returns FALSE.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003946 */
3947 static int
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003948get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003949{
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003950 int found_new_match = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003951
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003952 switch (type)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003953 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003954 case -1:
3955 break;
3956#ifdef FEAT_FIND_ID
3957 case CTRL_X_PATH_PATTERNS:
3958 case CTRL_X_PATH_DEFINES:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003959 get_next_include_file_completion(type);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003960 break;
3961#endif
3962
3963 case CTRL_X_DICTIONARY:
3964 case CTRL_X_THESAURUS:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003965 get_next_dict_tsr_completion(type, st->dict, st->dict_f);
3966 st->dict = NULL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003967 break;
3968
3969 case CTRL_X_TAGS:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003970 get_next_tag_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003971 break;
3972
3973 case CTRL_X_FILES:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003974 get_next_filename_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003975 break;
3976
3977 case CTRL_X_CMDLINE:
zeertzjqdca29d92021-08-31 19:12:51 +02003978 case CTRL_X_CMDLINE_CTRL_X:
Yegappan Lakshmananedc6f102021-12-29 17:38:46 +00003979 get_next_cmdline_completion();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003980 break;
3981
3982#ifdef FEAT_COMPL_FUNC
3983 case CTRL_X_FUNCTION:
3984 case CTRL_X_OMNI:
3985 expand_by_function(type, compl_pattern);
3986 break;
3987#endif
3988
3989 case CTRL_X_SPELL:
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003990 get_next_spell_completion(st->first_match_pos.lnum);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003991 break;
3992
3993 default: // normal ^P/^N and ^X^L
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00003994 found_new_match = get_next_default_completion(st, ini);
3995 if (found_new_match == FAIL && st->ins_buf == curbuf)
3996 st->found_all = TRUE;
3997 }
3998
3999 // check if compl_curr_match has changed, (e.g. other type of
4000 // expansion added something)
4001 if (type != 0 && compl_curr_match != compl_old_match)
4002 found_new_match = OK;
4003
4004 return found_new_match;
4005}
4006
4007/*
4008 * Get the next expansion(s), using "compl_pattern".
4009 * The search starts at position "ini" in curbuf and in the direction
4010 * compl_direction.
4011 * When "compl_started" is FALSE start at that position, otherwise continue
4012 * where we stopped searching before.
4013 * This may return before finding all the matches.
4014 * Return the total number of matches or -1 if still unknown -- Acevedo
4015 */
4016 static int
4017ins_compl_get_exp(pos_T *ini)
4018{
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004019 static ins_compl_next_state_T st;
4020 static int st_cleared = FALSE;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004021 int i;
4022 int found_new_match;
4023 int type = ctrl_x_mode;
glepnir8159fb12024-07-17 20:32:54 +02004024 int in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004025
4026 if (!compl_started)
4027 {
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004028 buf_T *buf;
4029
4030 FOR_ALL_BUFFERS(buf)
4031 buf->b_scanned = 0;
4032 if (!st_cleared)
4033 {
4034 CLEAR_FIELD(st);
4035 st_cleared = TRUE;
4036 }
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004037 st.found_all = FALSE;
4038 st.ins_buf = curbuf;
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004039 vim_free(st.e_cpt_copy);
Christian Brabandtee17b6f2023-09-09 11:23:50 +02004040 // Make a copy of 'complete', in case the buffer is wiped out.
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004041 st.e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL)
4042 ? (char_u *)"." : curbuf->b_p_cpt);
4043 st.e_cpt = st.e_cpt_copy == NULL ? (char_u *)"" : st.e_cpt_copy;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004044 st.last_match_pos = st.first_match_pos = *ini;
4045 }
4046 else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
4047 st.ins_buf = curbuf; // In case the buffer was wiped out.
4048
4049 compl_old_match = compl_curr_match; // remember the last current match
Christian Brabandt13032a42024-07-28 21:16:48 +02004050 st.cur_match_pos = (compl_dir_forward())
glepnir8159fb12024-07-17 20:32:54 +02004051 ? &st.last_match_pos : &st.first_match_pos;
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004052
4053 // For ^N/^P loop over all the flags/windows/buffers in 'complete'.
4054 for (;;)
4055 {
4056 found_new_match = FAIL;
4057 st.set_match_pos = FALSE;
4058
4059 // For ^N/^P pick a new entry from e_cpt if compl_started is off,
4060 // or if found_all says this entry is done. For ^X^L only use the
4061 // entries from 'complete' that look in loaded buffers.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004062 if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004063 && (!compl_started || st.found_all))
4064 {
glepnir8159fb12024-07-17 20:32:54 +02004065 int status = process_next_cpt_value(&st, &type, ini, in_fuzzy);
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004066
4067 if (status == INS_COMPL_CPT_END)
4068 break;
4069 if (status == INS_COMPL_CPT_CONT)
4070 continue;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004071 }
4072
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004073 // If complete() was called then compl_pattern has been reset. The
4074 // following won't work then, bail out.
4075 if (compl_pattern == NULL)
4076 break;
4077
4078 // get the next set of completion matches
4079 found_new_match = get_next_completion_match(type, &st, ini);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004080
4081 // break the loop for specialized modes (use 'complete' just for the
4082 // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new
4083 // match
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004084 if ((ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())
4085 || found_new_match != FAIL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004086 {
4087 if (got_int)
4088 break;
4089 // Fill the popup menu as soon as possible.
4090 if (type != -1)
4091 ins_compl_check_keys(0, FALSE);
4092
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004093 if ((ctrl_x_mode_not_default()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004094 && !ctrl_x_mode_line_or_eval()) || compl_interrupted)
4095 break;
4096 compl_started = TRUE;
4097 }
4098 else
4099 {
4100 // Mark a buffer scanned when it has been scanned completely
Christian Brabandtee9166e2023-09-03 21:24:33 +02004101 if (buf_valid(st.ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS))
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004102 st.ins_buf->b_scanned = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004103
4104 compl_started = FALSE;
4105 }
4106 }
4107 compl_started = TRUE;
4108
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004109 if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
Yegappan Lakshmanan6ad84ab2021-12-31 12:59:53 +00004110 && *st.e_cpt == NUL) // Got to end of 'complete'
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004111 found_new_match = FAIL;
4112
4113 i = -1; // total of matches, unknown
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004114 if (found_new_match == FAIL || (ctrl_x_mode_not_default()
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004115 && !ctrl_x_mode_line_or_eval()))
4116 i = ins_compl_make_cyclic();
4117
4118 if (compl_old_match != NULL)
4119 {
4120 // If several matches were added (FORWARD) or the search failed and has
4121 // just been made cyclic then we have to move compl_curr_match to the
4122 // next or previous entry (if any) -- Acevedo
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004123 compl_curr_match = compl_dir_forward() ? compl_old_match->cp_next
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004124 : compl_old_match->cp_prev;
4125 if (compl_curr_match == NULL)
4126 compl_curr_match = compl_old_match;
4127 }
LemonBoy2bf52dd2022-04-09 18:17:34 +01004128 may_trigger_modechanged();
=?UTF-8?q?Magnus=20Gro=C3=9F?=25def2c2021-10-22 18:56:39 +01004129
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004130 return i;
4131}
4132
4133/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004134 * Update "compl_shown_match" to the actually shown match, it may differ when
4135 * "compl_leader" is used to omit some of the matches.
4136 */
4137 static void
4138ins_compl_update_shown_match(void)
4139{
4140 while (!ins_compl_equal(compl_shown_match,
4141 compl_leader, (int)STRLEN(compl_leader))
4142 && compl_shown_match->cp_next != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004143 && !is_first_match(compl_shown_match->cp_next))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004144 compl_shown_match = compl_shown_match->cp_next;
4145
4146 // If we didn't find it searching forward, and compl_shows_dir is
4147 // backward, find the last match.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004148 if (compl_shows_dir_backward()
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004149 && !ins_compl_equal(compl_shown_match,
4150 compl_leader, (int)STRLEN(compl_leader))
4151 && (compl_shown_match->cp_next == NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004152 || is_first_match(compl_shown_match->cp_next)))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004153 {
4154 while (!ins_compl_equal(compl_shown_match,
4155 compl_leader, (int)STRLEN(compl_leader))
4156 && compl_shown_match->cp_prev != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004157 && !is_first_match(compl_shown_match->cp_prev))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004158 compl_shown_match = compl_shown_match->cp_prev;
4159 }
4160}
4161
4162/*
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004163 * Delete the old text being completed.
4164 */
4165 void
4166ins_compl_delete(void)
4167{
4168 int col;
4169
4170 // In insert mode: Delete the typed part.
4171 // In replace mode: Put the old characters back, if any.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004172 col = compl_col + (compl_status_adding() ? compl_length : 0);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004173 if ((int)curwin->w_cursor.col > col)
4174 {
4175 if (stop_arrow() == FAIL)
4176 return;
4177 backspace_until_column(col);
4178 }
4179
4180 // TODO: is this sufficient for redrawing? Redrawing everything causes
4181 // flicker, thus we can't do that.
4182 changed_cline_bef_curs();
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02004183#ifdef FEAT_EVAL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004184 // clear v:completed_item
4185 set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02004186#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004187}
4188
4189/*
4190 * Insert the new text being completed.
4191 * "in_compl_func" is TRUE when called from complete_check().
4192 */
4193 void
4194ins_compl_insert(int in_compl_func)
4195{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004196 int compl_len = get_compl_len();
Bram Moolenaar4b28ba32021-12-27 19:28:37 +00004197
4198 // Make sure we don't go over the end of the string, this can happen with
4199 // illegal bytes.
4200 if (compl_len < (int)STRLEN(compl_shown_match->cp_str))
4201 ins_bytes(compl_shown_match->cp_str + compl_len);
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004202 if (match_at_original_text(compl_shown_match))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004203 compl_used_match = FALSE;
4204 else
4205 compl_used_match = TRUE;
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02004206#ifdef FEAT_EVAL
4207 {
4208 dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
4209
4210 set_vim_var_dict(VV_COMPLETED_ITEM, dict);
4211 }
4212#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004213 if (!in_compl_func)
4214 compl_curr_match = compl_shown_match;
4215}
4216
4217/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004218 * show the file name for the completion match (if any). Truncate the file
4219 * name to avoid a wait for return.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004220 */
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004221 static void
4222ins_compl_show_filename(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004223{
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004224 char *lead = _("match in file");
4225 int space = sc_col - vim_strsize((char_u *)lead) - 2;
4226 char_u *s;
4227 char_u *e;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004228
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004229 if (space <= 0)
4230 return;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004231
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004232 // We need the tail that fits. With double-byte encoding going
4233 // back from the end is very slow, thus go from the start and keep
4234 // the text that fits in "space" between "s" and "e".
4235 for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004236 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004237 space -= ptr2cells(e);
4238 while (space < 0)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004239 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004240 space += ptr2cells(s);
4241 MB_PTR_ADV(s);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004242 }
4243 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004244 msg_hist_off = TRUE;
4245 vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
4246 s > compl_shown_match->cp_fname ? "<" : "", s);
4247 msg((char *)IObuff);
4248 msg_hist_off = FALSE;
4249 redraw_cmdline = FALSE; // don't overwrite!
4250}
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004251
glepnirdca57fb2024-06-04 22:01:21 +02004252/*
zeertzjq551d8c32024-06-05 19:53:32 +02004253 * Find a completion item when 'completeopt' contains "fuzzy".
glepnirdca57fb2024-06-04 22:01:21 +02004254 */
glepnira218cc62024-06-03 19:32:39 +02004255 static compl_T *
4256find_comp_when_fuzzy(void)
4257{
4258 int score;
4259 char_u* str;
4260 int target_idx = -1;
4261 int is_forward = compl_shows_dir_forward();
4262 int is_backward = compl_shows_dir_backward();
4263 compl_T *comp = NULL;
4264
glepnir43eef882024-06-19 20:20:48 +02004265 if ((is_forward && compl_selected_item == compl_match_arraysize - 1)
glepnira218cc62024-06-03 19:32:39 +02004266 || (is_backward && compl_selected_item == 0))
glepnir65407ce2024-07-06 16:09:19 +02004267 return compl_first_match != compl_shown_match ? compl_first_match :
4268 (compl_first_match->cp_prev ? compl_first_match->cp_prev : NULL);
glepnira218cc62024-06-03 19:32:39 +02004269
4270 if (is_forward)
4271 target_idx = compl_selected_item + 1;
4272 else if (is_backward)
4273 target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1
glepnirdca57fb2024-06-04 22:01:21 +02004274 : compl_selected_item - 1;
glepnira218cc62024-06-03 19:32:39 +02004275
4276 score = compl_match_array[target_idx].pum_score;
4277 str = compl_match_array[target_idx].pum_text;
4278
4279 comp = compl_first_match;
4280 do {
4281 if (comp->cp_score == score && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR]))
4282 return comp;
4283 comp = comp->cp_next;
4284 } while (comp != NULL && !is_first_match(comp));
4285
4286 return NULL;
4287}
4288
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004289/*
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004290 * Find the next set of matches for completion. Repeat the completion "todo"
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004291 * times. The number of matches found is returned in 'num_matches'.
4292 *
4293 * If "allow_get_expansion" is TRUE, then ins_compl_get_exp() may be called to
4294 * get more completions. If it is FALSE, then do nothing when there are no more
4295 * completions in the given direction.
4296 *
4297 * If "advance" is TRUE, then completion will move to the first match.
4298 * Otherwise, the original text will be shown.
4299 *
4300 * Returns OK on success and -1 if the number of matches are unknown.
4301 */
4302 static int
4303find_next_completion_match(
4304 int allow_get_expansion,
4305 int todo, // repeat completion this many times
4306 int advance,
4307 int *num_matches)
4308{
4309 int found_end = FALSE;
4310 compl_T *found_compl = NULL;
zeertzjqaa925ee2024-06-09 18:24:05 +02004311 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02004312 int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
4313 int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004314
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004315 while (--todo >= 0)
4316 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004317 if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004318 {
glepniraced8c22024-06-15 15:13:05 +02004319 compl_shown_match = compl_fuzzy_match && compl_match_array != NULL
4320 ? find_comp_when_fuzzy() : compl_shown_match->cp_next;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004321 found_end = (compl_first_match != NULL
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004322 && (is_first_match(compl_shown_match->cp_next)
4323 || is_first_match(compl_shown_match)));
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004324 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004325 else if (compl_shows_dir_backward()
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004326 && compl_shown_match->cp_prev != NULL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004327 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004328 found_end = is_first_match(compl_shown_match);
glepniraced8c22024-06-15 15:13:05 +02004329 compl_shown_match = compl_fuzzy_match && compl_match_array != NULL
4330 ? find_comp_when_fuzzy() : compl_shown_match->cp_prev;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004331 found_end |= is_first_match(compl_shown_match);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004332 }
4333 else
4334 {
4335 if (!allow_get_expansion)
4336 {
4337 if (advance)
4338 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004339 if (compl_shows_dir_backward())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004340 compl_pending -= todo + 1;
4341 else
4342 compl_pending += todo + 1;
4343 }
4344 return -1;
4345 }
4346
4347 if (!compl_no_select && advance)
4348 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004349 if (compl_shows_dir_backward())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004350 --compl_pending;
4351 else
4352 ++compl_pending;
4353 }
4354
4355 // Find matches.
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004356 *num_matches = ins_compl_get_exp(&compl_startpos);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004357
4358 // handle any pending completions
4359 while (compl_pending != 0 && compl_direction == compl_shows_dir
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004360 && advance)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004361 {
4362 if (compl_pending > 0 && compl_shown_match->cp_next != NULL)
4363 {
4364 compl_shown_match = compl_shown_match->cp_next;
4365 --compl_pending;
4366 }
4367 if (compl_pending < 0 && compl_shown_match->cp_prev != NULL)
4368 {
4369 compl_shown_match = compl_shown_match->cp_prev;
4370 ++compl_pending;
4371 }
4372 else
4373 break;
4374 }
4375 found_end = FALSE;
4376 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004377 if (!match_at_original_text(compl_shown_match)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004378 && compl_leader != NULL
4379 && !ins_compl_equal(compl_shown_match,
glepnira218cc62024-06-03 19:32:39 +02004380 compl_leader, (int)STRLEN(compl_leader))
4381 && !(compl_fuzzy_match && compl_shown_match->cp_score > 0))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004382 ++todo;
4383 else
4384 // Remember a matching item.
4385 found_compl = compl_shown_match;
4386
4387 // Stop at the end of the list when we found a usable match.
4388 if (found_end)
4389 {
4390 if (found_compl != NULL)
4391 {
4392 compl_shown_match = found_compl;
4393 break;
4394 }
4395 todo = 1; // use first usable match after wrapping around
4396 }
4397 }
4398
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004399 return OK;
4400}
4401
4402/*
4403 * Fill in the next completion in the current direction.
4404 * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to
4405 * get more completions. If it is FALSE, then we just do nothing when there
4406 * are no more completions in a given direction. The latter case is used when
4407 * we are still in the middle of finding completions, to allow browsing
4408 * through the ones found so far.
4409 * Return the total number of matches, or -1 if still unknown -- webb.
4410 *
4411 * compl_curr_match is currently being used by ins_compl_get_exp(), so we use
4412 * compl_shown_match here.
4413 *
4414 * Note that this function may be called recursively once only. First with
4415 * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn
4416 * calls this function with "allow_get_expansion" FALSE.
4417 */
4418 static int
4419ins_compl_next(
4420 int allow_get_expansion,
4421 int count, // repeat completion this many times; should
4422 // be at least 1
4423 int insert_match, // Insert the newly selected match
4424 int in_compl_func) // called from complete_check()
4425{
4426 int num_matches = -1;
4427 int todo = count;
4428 int advance;
4429 int started = compl_started;
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004430 buf_T *orig_curbuf = curbuf;
zeertzjqaa925ee2024-06-09 18:24:05 +02004431 unsigned int cur_cot_flags = get_cot_flags();
zeertzjq529b9ad2024-06-05 20:27:06 +02004432 int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
4433 int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004434
4435 // When user complete function return -1 for findstart which is next
4436 // time of 'always', compl_shown_match become NULL.
4437 if (compl_shown_match == NULL)
4438 return -1;
4439
glepnira218cc62024-06-03 19:32:39 +02004440 if (compl_leader != NULL
4441 && !match_at_original_text(compl_shown_match)
4442 && !compl_fuzzy_match)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004443 // Update "compl_shown_match" to the actually shown match
4444 ins_compl_update_shown_match();
4445
4446 if (allow_get_expansion && insert_match
nwounkn2e3cd522023-10-17 11:05:38 +02004447 && (!compl_get_longest || compl_used_match))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004448 // Delete old text to be replaced
4449 ins_compl_delete();
4450
4451 // When finding the longest common text we stick at the original text,
4452 // don't let CTRL-N or CTRL-P move to the first match.
4453 advance = count != 1 || !allow_get_expansion || !compl_get_longest;
4454
4455 // When restarting the search don't insert the first match either.
4456 if (compl_restarting)
4457 {
4458 advance = FALSE;
4459 compl_restarting = FALSE;
4460 }
4461
4462 // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
4463 // around.
4464 if (find_next_completion_match(allow_get_expansion, todo, advance,
4465 &num_matches) == -1)
4466 return -1;
4467
Bram Moolenaar0ff01832022-09-24 19:20:30 +01004468 if (curbuf != orig_curbuf)
4469 {
4470 // In case some completion function switched buffer, don't want to
4471 // insert the completion elsewhere.
4472 return -1;
4473 }
4474
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004475 // Insert the text of the new completion, or the compl_leader.
4476 if (compl_no_insert && !started)
4477 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004478 ins_bytes(compl_orig_text + get_compl_len());
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004479 compl_used_match = FALSE;
4480 }
4481 else if (insert_match)
4482 {
4483 if (!compl_get_longest || compl_used_match)
4484 ins_compl_insert(in_compl_func);
4485 else
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004486 ins_bytes(compl_leader + get_compl_len());
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004487 }
4488 else
4489 compl_used_match = FALSE;
4490
4491 if (!allow_get_expansion)
4492 {
4493 // may undisplay the popup menu first
4494 ins_compl_upd_pum();
4495
4496 if (pum_enough_matches())
4497 // Will display the popup menu, don't redraw yet to avoid flicker.
4498 pum_call_update_screen();
4499 else
4500 // Not showing the popup menu yet, redraw to show the user what was
4501 // inserted.
4502 update_screen(0);
4503
4504 // display the updated popup menu
4505 ins_compl_show_pum();
4506#ifdef FEAT_GUI
4507 if (gui.in_use)
4508 {
4509 // Show the cursor after the match, not after the redrawn text.
4510 setcursor();
4511 out_flush_cursor(FALSE, FALSE);
4512 }
4513#endif
4514
4515 // Delete old text to be replaced, since we're still searching and
4516 // don't want to match ourselves!
4517 ins_compl_delete();
4518 }
4519
4520 // Enter will select a match when the match wasn't inserted and the popup
4521 // menu is visible.
4522 if (compl_no_insert && !started)
4523 compl_enter_selects = TRUE;
4524 else
4525 compl_enter_selects = !insert_match && compl_match_array != NULL;
4526
4527 // Show the file name for the match (if any)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004528 if (compl_shown_match->cp_fname != NULL)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00004529 ins_compl_show_filename();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004530
4531 return num_matches;
4532}
4533
4534/*
4535 * Call this while finding completions, to check whether the user has hit a key
4536 * that should change the currently displayed completion, or exit completion
4537 * mode. Also, when compl_pending is not zero, show a completion as soon as
4538 * possible. -- webb
4539 * "frequency" specifies out of how many calls we actually check.
4540 * "in_compl_func" is TRUE when called from complete_check(), don't set
4541 * compl_curr_match.
4542 */
4543 void
4544ins_compl_check_keys(int frequency, int in_compl_func)
4545{
4546 static int count = 0;
4547 int c;
4548
4549 // Don't check when reading keys from a script, :normal or feedkeys().
4550 // That would break the test scripts. But do check for keys when called
4551 // from complete_check().
4552 if (!in_compl_func && (using_script() || ex_normal_busy))
4553 return;
4554
4555 // Only do this at regular intervals
4556 if (++count < frequency)
4557 return;
4558 count = 0;
4559
4560 // Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key()
4561 // can't do its work correctly.
4562 c = vpeekc_any();
4563 if (c != NUL)
4564 {
4565 if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R)
4566 {
4567 c = safe_vgetc(); // Eat the character
4568 compl_shows_dir = ins_compl_key2dir(c);
4569 (void)ins_compl_next(FALSE, ins_compl_key2count(c),
4570 c != K_UP && c != K_DOWN, in_compl_func);
4571 }
4572 else
4573 {
4574 // Need to get the character to have KeyTyped set. We'll put it
4575 // back with vungetc() below. But skip K_IGNORE.
4576 c = safe_vgetc();
4577 if (c != K_IGNORE)
4578 {
4579 // Don't interrupt completion when the character wasn't typed,
4580 // e.g., when doing @q to replay keys.
4581 if (c != Ctrl_R && KeyTyped)
4582 compl_interrupted = TRUE;
4583
4584 vungetc(c);
4585 }
4586 }
4587 }
zeertzjq529b9ad2024-06-05 20:27:06 +02004588 if (compl_pending != 0 && !got_int && !(cot_flags & COT_NOINSERT))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01004589 {
4590 int todo = compl_pending > 0 ? compl_pending : -compl_pending;
4591
4592 compl_pending = 0;
4593 (void)ins_compl_next(FALSE, todo, TRUE, in_compl_func);
4594 }
4595}
4596
4597/*
4598 * Decide the direction of Insert mode complete from the key typed.
4599 * Returns BACKWARD or FORWARD.
4600 */
4601 static int
4602ins_compl_key2dir(int c)
4603{
4604 if (c == Ctrl_P || c == Ctrl_L
4605 || c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP || c == K_UP)
4606 return BACKWARD;
4607 return FORWARD;
4608}
4609
4610/*
4611 * Return TRUE for keys that are used for completion only when the popup menu
4612 * is visible.
4613 */
4614 static int
4615ins_compl_pum_key(int c)
4616{
4617 return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP
4618 || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == K_S_DOWN
4619 || c == K_UP || c == K_DOWN);
4620}
4621
4622/*
4623 * Decide the number of completions to move forward.
4624 * Returns 1 for most keys, height of the popup menu for page-up/down keys.
4625 */
4626 static int
4627ins_compl_key2count(int c)
4628{
4629 int h;
4630
4631 if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN)
4632 {
4633 h = pum_get_height();
4634 if (h > 3)
4635 h -= 2; // keep some context
4636 return h;
4637 }
4638 return 1;
4639}
4640
4641/*
4642 * Return TRUE if completion with "c" should insert the match, FALSE if only
4643 * to change the currently selected completion.
4644 */
4645 static int
4646ins_compl_use_match(int c)
4647{
4648 switch (c)
4649 {
4650 case K_UP:
4651 case K_DOWN:
4652 case K_PAGEDOWN:
4653 case K_KPAGEDOWN:
4654 case K_S_DOWN:
4655 case K_PAGEUP:
4656 case K_KPAGEUP:
4657 case K_S_UP:
4658 return FALSE;
4659 }
4660 return TRUE;
4661}
4662
4663/*
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004664 * Get the pattern, column and length for normal completion (CTRL-N CTRL-P
4665 * completion)
John Marriott8c85a2a2024-05-20 19:18:26 +02004666 * Sets the global variables: compl_col, compl_length, compl_pattern and
4667 * compl_patternlen.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004668 * Uses the global variables: compl_cont_status and ctrl_x_mode
4669 */
4670 static int
4671get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
4672{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004673 if ((compl_cont_status & CONT_SOL) || ctrl_x_mode_path_defines())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004674 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004675 if (!compl_status_adding())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004676 {
4677 while (--startcol >= 0 && vim_isIDc(line[startcol]))
4678 ;
4679 compl_col += ++startcol;
4680 compl_length = curs_col - startcol;
4681 }
4682 if (p_ic)
4683 compl_pattern = str_foldcase(line + compl_col,
4684 compl_length, NULL, 0);
4685 else
4686 compl_pattern = vim_strnsave(line + compl_col, compl_length);
4687 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004688 {
4689 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004690 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004691 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004692 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004693 else if (compl_status_adding())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004694 {
4695 char_u *prefix = (char_u *)"\\<";
John Marriott8c85a2a2024-05-20 19:18:26 +02004696 size_t prefixlen = STRLEN_LITERAL("\\<");
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004697
4698 // we need up to 2 extra chars for the prefix
4699 compl_pattern = alloc(quote_meta(NULL, line + compl_col,
John Marriott8c85a2a2024-05-20 19:18:26 +02004700 compl_length) + prefixlen);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004701 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004702 {
4703 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004704 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004705 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004706 if (!vim_iswordp(line + compl_col)
4707 || (compl_col > 0
4708 && (vim_iswordp(mb_prevptr(line, line + compl_col)))))
John Marriott8c85a2a2024-05-20 19:18:26 +02004709 {
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004710 prefix = (char_u *)"";
John Marriott8c85a2a2024-05-20 19:18:26 +02004711 prefixlen = 0;
4712 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004713 STRCPY((char *)compl_pattern, prefix);
John Marriott8c85a2a2024-05-20 19:18:26 +02004714 (void)quote_meta(compl_pattern + prefixlen,
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004715 line + compl_col, compl_length);
4716 }
4717 else if (--startcol < 0
4718 || !vim_iswordp(mb_prevptr(line, line + startcol + 1)))
4719 {
4720 // Match any word of at least two chars
John Marriott8c85a2a2024-05-20 19:18:26 +02004721 compl_pattern = vim_strnsave((char_u *)"\\<\\k\\k", STRLEN_LITERAL("\\<\\k\\k"));
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004722 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004723 {
4724 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004725 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004726 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004727 compl_col += curs_col;
4728 compl_length = 0;
4729 }
4730 else
4731 {
4732 // Search the point of change class of multibyte character
4733 // or not a word single byte character backward.
4734 if (has_mbyte)
4735 {
4736 int base_class;
4737 int head_off;
4738
4739 startcol -= (*mb_head_off)(line, line + startcol);
4740 base_class = mb_get_class(line + startcol);
4741 while (--startcol >= 0)
4742 {
4743 head_off = (*mb_head_off)(line, line + startcol);
4744 if (base_class != mb_get_class(line + startcol
4745 - head_off))
4746 break;
4747 startcol -= head_off;
4748 }
4749 }
4750 else
4751 while (--startcol >= 0 && vim_iswordc(line[startcol]))
4752 ;
4753 compl_col += ++startcol;
4754 compl_length = (int)curs_col - startcol;
4755 if (compl_length == 1)
4756 {
4757 // Only match word with at least two chars -- webb
4758 // there's no need to call quote_meta,
4759 // alloc(7) is enough -- Acevedo
4760 compl_pattern = alloc(7);
4761 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004762 {
4763 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004764 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004765 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004766 STRCPY((char *)compl_pattern, "\\<");
4767 (void)quote_meta(compl_pattern + 2, line + compl_col, 1);
4768 STRCAT((char *)compl_pattern, "\\k");
4769 }
4770 else
4771 {
4772 compl_pattern = alloc(quote_meta(NULL, line + compl_col,
4773 compl_length) + 2);
4774 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004775 {
4776 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004777 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004778 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004779 STRCPY((char *)compl_pattern, "\\<");
4780 (void)quote_meta(compl_pattern + 2, line + compl_col,
4781 compl_length);
4782 }
4783 }
4784
John Marriott8c85a2a2024-05-20 19:18:26 +02004785 compl_patternlen = STRLEN(compl_pattern);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004786 return OK;
4787}
4788
4789/*
4790 * Get the pattern, column and length for whole line completion or for the
4791 * complete() function.
4792 * Sets the global variables: compl_col, compl_length and compl_pattern.
4793 */
4794 static int
4795get_wholeline_compl_info(char_u *line, colnr_T curs_col)
4796{
4797 compl_col = (colnr_T)getwhitecols(line);
4798 compl_length = (int)curs_col - (int)compl_col;
4799 if (compl_length < 0) // cursor in indent: empty pattern
4800 compl_length = 0;
4801 if (p_ic)
4802 compl_pattern = str_foldcase(line + compl_col, compl_length,
4803 NULL, 0);
4804 else
4805 compl_pattern = vim_strnsave(line + compl_col, compl_length);
4806 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004807 {
4808 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004809 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004810 }
4811
4812 compl_patternlen = STRLEN(compl_pattern);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004813
4814 return OK;
4815}
4816
4817/*
4818 * Get the pattern, column and length for filename completion.
4819 * Sets the global variables: compl_col, compl_length and compl_pattern.
4820 */
4821 static int
4822get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col)
4823{
4824 // Go back to just before the first filename character.
4825 if (startcol > 0)
4826 {
4827 char_u *p = line + startcol;
4828
4829 MB_PTR_BACK(line, p);
4830 while (p > line && vim_isfilec(PTR2CHAR(p)))
4831 MB_PTR_BACK(line, p);
4832 if (p == line && vim_isfilec(PTR2CHAR(p)))
4833 startcol = 0;
4834 else
4835 startcol = (int)(p - line) + 1;
4836 }
4837
4838 compl_col += startcol;
4839 compl_length = (int)curs_col - startcol;
4840 compl_pattern = addstar(line + compl_col, compl_length, EXPAND_FILES);
4841 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004842 {
4843 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004844 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004845 }
4846
4847 compl_patternlen = STRLEN(compl_pattern);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004848
4849 return OK;
4850}
4851
4852/*
4853 * Get the pattern, column and length for command-line completion.
4854 * Sets the global variables: compl_col, compl_length and compl_pattern.
4855 */
4856 static int
4857get_cmdline_compl_info(char_u *line, colnr_T curs_col)
4858{
4859 compl_pattern = vim_strnsave(line, curs_col);
4860 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004861 {
4862 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004863 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004864 }
4865 compl_patternlen = curs_col;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004866 set_cmd_context(&compl_xp, compl_pattern,
Mike Williams16b63bd2024-06-01 11:33:40 +02004867 (int)compl_patternlen, curs_col, FALSE);
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004868 if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
4869 || compl_xp.xp_context == EXPAND_NOTHING)
4870 // No completion possible, use an empty pattern to get a
4871 // "pattern not found" message.
4872 compl_col = curs_col;
4873 else
4874 compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
4875 compl_length = curs_col - compl_col;
4876
4877 return OK;
4878}
4879
4880/*
4881 * Get the pattern, column and length for user defined completion ('omnifunc',
4882 * 'completefunc' and 'thesaurusfunc')
4883 * Sets the global variables: compl_col, compl_length and compl_pattern.
4884 * Uses the global variable: spell_bad_len
4885 */
4886 static int
4887get_userdefined_compl_info(colnr_T curs_col UNUSED)
4888{
4889 int ret = FAIL;
4890
4891#ifdef FEAT_COMPL_FUNC
4892 // Call user defined function 'completefunc' with "a:findstart"
4893 // set to 1 to obtain the length of text to use for completion.
4894 char_u *line;
4895 typval_T args[3];
4896 int col;
4897 char_u *funcname;
4898 pos_T pos;
4899 int save_State = State;
4900 callback_T *cb;
4901
4902 // Call 'completefunc' or 'omnifunc' or 'thesaurusfunc' and get pattern
4903 // length as a string
4904 funcname = get_complete_funcname(ctrl_x_mode);
4905 if (*funcname == NUL)
4906 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00004907 semsg(_(e_option_str_is_not_set), ctrl_x_mode_function()
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004908 ? "completefunc" : "omnifunc");
4909 return FAIL;
4910 }
4911
4912 args[0].v_type = VAR_NUMBER;
4913 args[0].vval.v_number = 1;
4914 args[1].v_type = VAR_STRING;
4915 args[1].vval.v_string = (char_u *)"";
4916 args[2].v_type = VAR_UNKNOWN;
4917 pos = curwin->w_cursor;
zeertzjqcfe45652022-05-27 17:26:55 +01004918 ++textlock;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004919 cb = get_insert_callback(ctrl_x_mode);
4920 col = call_callback_retnr(cb, 2, args);
zeertzjqcfe45652022-05-27 17:26:55 +01004921 --textlock;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004922
4923 State = save_State;
4924 curwin->w_cursor = pos; // restore the cursor position
zeertzjq0a419e02024-04-02 19:01:14 +02004925 check_cursor(); // make sure cursor position is valid, just in case
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004926 validate_cursor();
4927 if (!EQUAL_POS(curwin->w_cursor, pos))
4928 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004929 emsg(_(e_complete_function_deleted_text));
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004930 return FAIL;
4931 }
4932
LemonBoy9bcb9ca2022-05-26 15:23:26 +01004933 // Return value -2 means the user complete function wants to cancel the
4934 // complete without an error, do the same if the function did not execute
4935 // successfully.
4936 if (col == -2 || aborting())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004937 return FAIL;
LemonBoy9bcb9ca2022-05-26 15:23:26 +01004938 // Return value -3 does the same as -2 and leaves CTRL-X mode.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004939 if (col == -3)
4940 {
4941 ctrl_x_mode = CTRL_X_NORMAL;
4942 edit_submode = NULL;
4943 if (!shortmess(SHM_COMPLETIONMENU))
4944 msg_clr_cmdline();
4945 return FAIL;
4946 }
4947
Yegappan Lakshmanan37079142022-01-08 10:38:48 +00004948 // Reset extended parameters of completion, when starting new
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004949 // completion.
4950 compl_opt_refresh_always = FALSE;
4951 compl_opt_suppress_empty = FALSE;
4952
4953 if (col < 0)
4954 col = curs_col;
4955 compl_col = col;
4956 if (compl_col > curs_col)
4957 compl_col = curs_col;
4958
4959 // Setup variables for completion. Need to obtain "line" again,
4960 // it may have become invalid.
4961 line = ml_get(curwin->w_cursor.lnum);
4962 compl_length = curs_col - compl_col;
4963 compl_pattern = vim_strnsave(line + compl_col, compl_length);
4964 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02004965 {
4966 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004967 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02004968 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004969
John Marriott8c85a2a2024-05-20 19:18:26 +02004970 compl_patternlen = compl_length;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00004971 ret = OK;
4972#endif
4973
4974 return ret;
4975}
4976
4977/*
4978 * Get the pattern, column and length for spell completion.
4979 * Sets the global variables: compl_col, compl_length and compl_pattern.
4980 * Uses the global variable: spell_bad_len
4981 */
4982 static int
4983get_spell_compl_info(int startcol UNUSED, colnr_T curs_col UNUSED)
4984{
4985 int ret = FAIL;
4986#ifdef FEAT_SPELL
4987 char_u *line;
4988
4989 if (spell_bad_len > 0)
4990 compl_col = curs_col - spell_bad_len;
4991 else
4992 compl_col = spell_word_start(startcol);
4993 if (compl_col >= (colnr_T)startcol)
4994 {
4995 compl_length = 0;
4996 compl_col = curs_col;
4997 }
4998 else
4999 {
5000 spell_expand_check_cap(compl_col);
5001 compl_length = (int)curs_col - compl_col;
5002 }
5003 // Need to obtain "line" again, it may have become invalid.
5004 line = ml_get(curwin->w_cursor.lnum);
5005 compl_pattern = vim_strnsave(line + compl_col, compl_length);
5006 if (compl_pattern == NULL)
John Marriott8c85a2a2024-05-20 19:18:26 +02005007 {
5008 compl_patternlen = 0;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005009 return FAIL;
John Marriott8c85a2a2024-05-20 19:18:26 +02005010 }
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005011
John Marriott8c85a2a2024-05-20 19:18:26 +02005012 compl_patternlen = compl_length;
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005013 ret = OK;
5014#endif
5015
5016 return ret;
5017}
5018
5019/*
5020 * Get the completion pattern, column and length.
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005021 * "startcol" - start column number of the completion pattern/text
5022 * "cur_col" - current cursor column
5023 * On return, "line_invalid" is set to TRUE, if the current line may have
5024 * become invalid and needs to be fetched again.
5025 * Returns OK on success.
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005026 */
5027 static int
5028compl_get_info(char_u *line, int startcol, colnr_T curs_col, int *line_invalid)
5029{
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005030 if (ctrl_x_mode_normal()
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005031 || (ctrl_x_mode & CTRL_X_WANT_IDENT
5032 && !thesaurus_func_complete(ctrl_x_mode)))
5033 {
5034 return get_normal_compl_info(line, startcol, curs_col);
5035 }
5036 else if (ctrl_x_mode_line_or_eval())
5037 {
5038 return get_wholeline_compl_info(line, curs_col);
5039 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005040 else if (ctrl_x_mode_files())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005041 {
5042 return get_filename_compl_info(line, startcol, curs_col);
5043 }
5044 else if (ctrl_x_mode == CTRL_X_CMDLINE)
5045 {
5046 return get_cmdline_compl_info(line, curs_col);
5047 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005048 else if (ctrl_x_mode_function() || ctrl_x_mode_omni()
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005049 || thesaurus_func_complete(ctrl_x_mode))
5050 {
5051 if (get_userdefined_compl_info(curs_col) == FAIL)
5052 return FAIL;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005053 *line_invalid = TRUE; // "line" may have become invalid
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005054 }
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005055 else if (ctrl_x_mode_spell())
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005056 {
5057 if (get_spell_compl_info(startcol, curs_col) == FAIL)
5058 return FAIL;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005059 *line_invalid = TRUE; // "line" may have become invalid
Bram Moolenaarbf7ff612021-12-27 12:52:07 +00005060 }
5061 else
5062 {
5063 internal_error("ins_complete()");
5064 return FAIL;
5065 }
5066
5067 return OK;
5068}
5069
5070/*
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005071 * Continue an interrupted completion mode search in "line".
5072 *
5073 * If this same ctrl_x_mode has been interrupted use the text from
5074 * "compl_startpos" to the cursor as a pattern to add a new word instead of
5075 * expand the one before the cursor, in word-wise if "compl_startpos" is not in
5076 * the same line as the cursor then fix it (the line has been split because it
5077 * was longer than 'tw'). if SOL is set then skip the previous pattern, a word
5078 * at the beginning of the line has been inserted, we'll look for that.
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005079 */
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005080 static void
5081ins_compl_continue_search(char_u *line)
5082{
5083 // it is a continued search
5084 compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005085 if (ctrl_x_mode_normal() || ctrl_x_mode_path_patterns()
5086 || ctrl_x_mode_path_defines())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005087 {
5088 if (compl_startpos.lnum != curwin->w_cursor.lnum)
5089 {
5090 // line (probably) wrapped, set compl_startpos to the
5091 // first non_blank in the line, if it is not a wordchar
5092 // include it to get a better pattern, but then we don't
5093 // want the "\\<" prefix, check it below
5094 compl_col = (colnr_T)getwhitecols(line);
5095 compl_startpos.col = compl_col;
5096 compl_startpos.lnum = curwin->w_cursor.lnum;
5097 compl_cont_status &= ~CONT_SOL; // clear SOL if present
5098 }
5099 else
5100 {
5101 // S_IPOS was set when we inserted a word that was at the
5102 // beginning of the line, which means that we'll go to SOL
5103 // mode but first we need to redefine compl_startpos
5104 if (compl_cont_status & CONT_S_IPOS)
5105 {
5106 compl_cont_status |= CONT_SOL;
5107 compl_startpos.col = (colnr_T)(skipwhite(
5108 line + compl_length
5109 + compl_startpos.col) - line);
5110 }
5111 compl_col = compl_startpos.col;
5112 }
5113 compl_length = curwin->w_cursor.col - (int)compl_col;
5114 // IObuff is used to add a "word from the next line" would we
5115 // have enough space? just being paranoid
5116#define MIN_SPACE 75
5117 if (compl_length > (IOSIZE - MIN_SPACE))
5118 {
5119 compl_cont_status &= ~CONT_SOL;
5120 compl_length = (IOSIZE - MIN_SPACE);
5121 compl_col = curwin->w_cursor.col - compl_length;
5122 }
5123 compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
5124 if (compl_length < 1)
5125 compl_cont_status &= CONT_LOCAL;
5126 }
5127 else if (ctrl_x_mode_line_or_eval())
5128 compl_cont_status = CONT_ADDING | CONT_N_ADDS;
5129 else
5130 compl_cont_status = 0;
5131}
5132
5133/*
5134 * start insert mode completion
5135 */
5136 static int
5137ins_compl_start(void)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005138{
5139 char_u *line;
5140 int startcol = 0; // column where searched text starts
5141 colnr_T curs_col; // cursor column
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005142 int line_invalid = FALSE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005143 int save_did_ai = did_ai;
Bram Moolenaard9eefe32019-04-06 14:22:21 +02005144 int flags = CP_ORIGINAL_TEXT;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005145
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005146 // First time we hit ^N or ^P (in a row, I mean)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005147
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005148 did_ai = FALSE;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005149 did_si = FALSE;
5150 can_si = FALSE;
5151 can_si_back = FALSE;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005152 if (stop_arrow() == FAIL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005153 return FAIL;
5154
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005155 line = ml_get(curwin->w_cursor.lnum);
5156 curs_col = curwin->w_cursor.col;
5157 compl_pending = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005158
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005159 if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
5160 && compl_cont_mode == ctrl_x_mode)
5161 // this same ctrl-x_mode was interrupted previously. Continue the
5162 // completion.
5163 ins_compl_continue_search(line);
5164 else
5165 compl_cont_status &= CONT_LOCAL;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005166
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005167 if (!compl_status_adding()) // normal expansion
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005168 {
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005169 compl_cont_mode = ctrl_x_mode;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005170 if (ctrl_x_mode_not_default())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005171 // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
5172 compl_cont_status = 0;
5173 compl_cont_status |= CONT_N_ADDS;
5174 compl_startpos = curwin->w_cursor;
5175 startcol = (int)curs_col;
5176 compl_col = 0;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005177 }
5178
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005179 // Work out completion pattern and original text -- webb
5180 if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL)
5181 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005182 if (ctrl_x_mode_function() || ctrl_x_mode_omni()
5183 || thesaurus_func_complete(ctrl_x_mode))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005184 // restore did_ai, so that adding comment leader works
5185 did_ai = save_did_ai;
5186 return FAIL;
5187 }
5188 // If "line" was changed while getting completion info get it again.
5189 if (line_invalid)
5190 line = ml_get(curwin->w_cursor.lnum);
5191
glepnir7cfe6932024-09-15 20:06:28 +02005192 int in_fuzzy = get_cot_flags() & COT_FUZZY;
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005193 if (compl_status_adding())
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005194 {
5195 edit_submode_pre = (char_u *)_(" Adding");
5196 if (ctrl_x_mode_line_or_eval())
5197 {
5198 // Insert a new line, keep indentation but ignore 'comments'.
5199 char_u *old = curbuf->b_p_com;
5200
5201 curbuf->b_p_com = (char_u *)"";
5202 compl_startpos.lnum = curwin->w_cursor.lnum;
5203 compl_startpos.col = compl_col;
5204 ins_eol('\r');
5205 curbuf->b_p_com = old;
5206 compl_length = 0;
5207 compl_col = curwin->w_cursor.col;
5208 }
glepnir7cfe6932024-09-15 20:06:28 +02005209 else if (ctrl_x_mode_normal() && in_fuzzy)
5210 {
5211 compl_startpos = curwin->w_cursor;
5212 compl_cont_status &= CONT_S_IPOS;
5213 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005214 }
5215 else
5216 {
5217 edit_submode_pre = NULL;
5218 compl_startpos.col = compl_col;
5219 }
5220
5221 if (compl_cont_status & CONT_LOCAL)
5222 edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
5223 else
5224 edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
5225
5226 // If any of the original typed text has been changed we need to fix
5227 // the redo buffer.
5228 ins_compl_fixRedoBufForLeader(NULL);
5229
5230 // Always add completion for the original text.
5231 vim_free(compl_orig_text);
5232 compl_orig_text = vim_strnsave(line + compl_col, compl_length);
5233 if (p_ic)
5234 flags |= CP_ICASE;
5235 if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
glepnir38f99a12024-08-23 18:31:06 +02005236 -1, NULL, NULL, NULL, 0, flags, FALSE, -1, -1) != OK)
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005237 {
5238 VIM_CLEAR(compl_pattern);
John Marriott8c85a2a2024-05-20 19:18:26 +02005239 compl_patternlen = 0;
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005240 VIM_CLEAR(compl_orig_text);
5241 return FAIL;
5242 }
5243
5244 // showmode might reset the internal line pointers, so it must
5245 // be called before line = ml_get(), or when this address is no
5246 // longer needed. -- Acevedo.
5247 edit_submode_extra = (char_u *)_("-- Searching...");
5248 edit_submode_highl = HLF_COUNT;
5249 showmode();
5250 edit_submode_extra = NULL;
5251 out_flush();
5252
5253 return OK;
5254}
5255
5256/*
5257 * display the completion status message
5258 */
5259 static void
5260ins_compl_show_statusmsg(void)
5261{
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005262 // we found no match if the list has only the "compl_orig_text"-entry
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005263 if (is_first_match(compl_first_match->cp_next))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005264 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005265 edit_submode_extra = compl_status_adding() && compl_length > 1
Bram Moolenaarb53a1902022-11-15 13:57:38 +00005266 ? (char_u *)_("Hit end of paragraph")
5267 : (char_u *)_("Pattern not found");
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005268 edit_submode_highl = HLF_E;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005269 }
5270
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005271 if (edit_submode_extra == NULL)
5272 {
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005273 if (match_at_original_text(compl_curr_match))
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005274 {
5275 edit_submode_extra = (char_u *)_("Back at original");
5276 edit_submode_highl = HLF_W;
5277 }
5278 else if (compl_cont_status & CONT_S_IPOS)
5279 {
5280 edit_submode_extra = (char_u *)_("Word from other line");
5281 edit_submode_highl = HLF_COUNT;
5282 }
5283 else if (compl_curr_match->cp_next == compl_curr_match->cp_prev)
5284 {
5285 edit_submode_extra = (char_u *)_("The only match");
5286 edit_submode_highl = HLF_COUNT;
Bram Moolenaarf9d51352020-10-26 19:22:42 +01005287 compl_curr_match->cp_number = 1;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005288 }
5289 else
5290 {
Bram Moolenaar977fd0b2020-10-27 09:12:45 +01005291#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005292 // Update completion sequence number when needed.
5293 if (compl_curr_match->cp_number == -1)
Bram Moolenaarf9d51352020-10-26 19:22:42 +01005294 ins_compl_update_sequence_numbers();
Bram Moolenaar977fd0b2020-10-27 09:12:45 +01005295#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005296 // The match should always have a sequence number now, this is
5297 // just a safety check.
5298 if (compl_curr_match->cp_number != -1)
5299 {
5300 // Space for 10 text chars. + 2x10-digit no.s = 31.
5301 // Translations may need more than twice that.
5302 static char_u match_ref[81];
5303
5304 if (compl_matches > 0)
5305 vim_snprintf((char *)match_ref, sizeof(match_ref),
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005306 _("match %d of %d"),
5307 compl_curr_match->cp_number, compl_matches);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005308 else
5309 vim_snprintf((char *)match_ref, sizeof(match_ref),
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005310 _("match %d"),
5311 compl_curr_match->cp_number);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005312 edit_submode_extra = match_ref;
5313 edit_submode_highl = HLF_R;
5314 if (dollar_vcol >= 0)
5315 curs_columns(FALSE);
5316 }
5317 }
5318 }
5319
5320 // Show a message about what (completion) mode we're in.
5321 if (!compl_opt_suppress_empty)
5322 {
5323 showmode();
5324 if (!shortmess(SHM_COMPLETIONMENU))
5325 {
5326 if (edit_submode_extra != NULL)
5327 {
5328 if (!p_smd)
Bram Moolenaarcc233582020-12-12 13:32:07 +01005329 {
5330 msg_hist_off = TRUE;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005331 msg_attr((char *)edit_submode_extra,
5332 edit_submode_highl < HLF_COUNT
5333 ? HL_ATTR(edit_submode_highl) : 0);
Bram Moolenaarcc233582020-12-12 13:32:07 +01005334 msg_hist_off = FALSE;
5335 }
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005336 }
5337 else
5338 msg_clr_cmdline(); // necessary for "noshowmode"
5339 }
5340 }
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005341}
5342
5343/*
5344 * Do Insert mode completion.
5345 * Called when character "c" was typed, which has a meaning for completion.
5346 * Returns OK if completion was done, FAIL if something failed (out of mem).
5347 */
5348 int
5349ins_complete(int c, int enable_pum)
5350{
5351 int n;
5352 int save_w_wrow;
5353 int save_w_leftcol;
5354 int insert_match;
5355
5356 compl_direction = ins_compl_key2dir(c);
5357 insert_match = ins_compl_use_match(c);
5358
5359 if (!compl_started)
5360 {
5361 if (ins_compl_start() == FAIL)
5362 return FAIL;
5363 }
5364 else if (insert_match && stop_arrow() == FAIL)
5365 return FAIL;
5366
5367 compl_shown_match = compl_curr_match;
5368 compl_shows_dir = compl_direction;
5369
5370 // Find next match (and following matches).
5371 save_w_wrow = curwin->w_wrow;
5372 save_w_leftcol = curwin->w_leftcol;
5373 n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE);
5374
5375 // may undisplay the popup menu
5376 ins_compl_upd_pum();
5377
5378 if (n > 1) // all matches have been found
5379 compl_matches = n;
5380 compl_curr_match = compl_shown_match;
5381 compl_direction = compl_shows_dir;
5382
5383 // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
5384 // mode.
5385 if (got_int && !global_busy)
5386 {
5387 (void)vgetc();
5388 got_int = FALSE;
5389 }
5390
5391 // we found no match if the list has only the "compl_orig_text"-entry
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005392 if (is_first_match(compl_first_match->cp_next))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005393 {
5394 // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
5395 // because we couldn't expand anything at first place, but if we used
5396 // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
5397 // (such as M in M'exico) if not tried already. -- Acevedo
5398 if (compl_length > 1
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005399 || compl_status_adding()
5400 || (ctrl_x_mode_not_default()
5401 && !ctrl_x_mode_path_patterns()
5402 && !ctrl_x_mode_path_defines()))
Yegappan Lakshmanan5d2e0072021-12-30 11:40:53 +00005403 compl_cont_status &= ~CONT_N_ADDS;
5404 }
5405
5406 if (compl_curr_match->cp_flags & CP_CONT_S_IPOS)
5407 compl_cont_status |= CONT_S_IPOS;
5408 else
5409 compl_cont_status &= ~CONT_S_IPOS;
5410
5411 ins_compl_show_statusmsg();
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005412
5413 // Show the popup menu, unless we got interrupted.
5414 if (enable_pum && !compl_interrupted)
5415 show_pum(save_w_wrow, save_w_leftcol);
5416
5417 compl_was_interrupted = compl_interrupted;
5418 compl_interrupted = FALSE;
5419
5420 return OK;
5421}
5422
Yegappan Lakshmanane9825862022-01-03 11:03:48 +00005423/*
5424 * Remove (if needed) and show the popup menu
5425 */
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005426 static void
5427show_pum(int prev_w_wrow, int prev_w_leftcol)
5428{
5429 // RedrawingDisabled may be set when invoked through complete().
Bram Moolenaar79cdf022023-05-20 14:07:00 +01005430 int save_RedrawingDisabled = RedrawingDisabled;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005431 RedrawingDisabled = 0;
5432
5433 // If the cursor moved or the display scrolled we need to remove the pum
5434 // first.
5435 setcursor();
5436 if (prev_w_wrow != curwin->w_wrow || prev_w_leftcol != curwin->w_leftcol)
5437 ins_compl_del_pum();
5438
5439 ins_compl_show_pum();
5440 setcursor();
Bram Moolenaar79cdf022023-05-20 14:07:00 +01005441
5442 RedrawingDisabled = save_RedrawingDisabled;
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005443}
5444
5445/*
5446 * Looks in the first "len" chars. of "src" for search-metachars.
5447 * If dest is not NULL the chars. are copied there quoting (with
5448 * a backslash) the metachars, and dest would be NUL terminated.
5449 * Returns the length (needed) of dest
5450 */
5451 static unsigned
5452quote_meta(char_u *dest, char_u *src, int len)
5453{
5454 unsigned m = (unsigned)len + 1; // one extra for the NUL
5455
5456 for ( ; --len >= 0; src++)
5457 {
5458 switch (*src)
5459 {
5460 case '.':
5461 case '*':
5462 case '[':
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005463 if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005464 break;
5465 // FALLTHROUGH
5466 case '~':
Bram Moolenaarf4e20992020-12-21 19:59:08 +01005467 if (!magic_isset()) // quote these only if magic is set
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005468 break;
5469 // FALLTHROUGH
5470 case '\\':
Yegappan Lakshmanand94fbfc2022-01-04 17:01:44 +00005471 if (ctrl_x_mode_dictionary() || ctrl_x_mode_thesaurus())
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005472 break;
5473 // FALLTHROUGH
5474 case '^': // currently it's not needed.
5475 case '$':
5476 m++;
5477 if (dest != NULL)
5478 *dest++ = '\\';
5479 break;
5480 }
5481 if (dest != NULL)
5482 *dest++ = *src;
5483 // Copy remaining bytes of a multibyte character.
5484 if (has_mbyte)
5485 {
5486 int i, mb_len;
5487
5488 mb_len = (*mb_ptr2len)(src) - 1;
5489 if (mb_len > 0 && len >= mb_len)
5490 for (i = 0; i < mb_len; ++i)
5491 {
5492 --len;
5493 ++src;
5494 if (dest != NULL)
5495 *dest++ = *src;
5496 }
5497 }
5498 }
5499 if (dest != NULL)
5500 *dest = NUL;
5501
5502 return m;
5503}
5504
Bram Moolenaare2c453d2019-08-21 14:37:09 +02005505#if defined(EXITFREE) || defined(PROTO)
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005506 void
5507free_insexpand_stuff(void)
5508{
5509 VIM_CLEAR(compl_orig_text);
Yegappan Lakshmanan8658c752021-12-03 11:09:29 +00005510# ifdef FEAT_EVAL
5511 free_callback(&cfu_cb);
5512 free_callback(&ofu_cb);
5513 free_callback(&tsrfu_cb);
5514# endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005515}
Bram Moolenaare2c453d2019-08-21 14:37:09 +02005516#endif
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005517
Bram Moolenaare2c453d2019-08-21 14:37:09 +02005518#ifdef FEAT_SPELL
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005519/*
5520 * Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly
5521 * spelled word, if there is one.
5522 */
5523 static void
5524spell_back_to_badword(void)
5525{
5526 pos_T tpos = curwin->w_cursor;
5527
Christ van Willegen - van Noort8e4c4c72024-05-17 18:49:27 +02005528 spell_bad_len = spell_move_to(curwin, BACKWARD, SMT_ALL, TRUE, NULL);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01005529 if (curwin->w_cursor.col != tpos.col)
5530 start_arrow(&tpos);
5531}
Bram Moolenaare2c453d2019-08-21 14:37:09 +02005532#endif