summaryrefslogtreecommitdiff
path: root/src/insexpand.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-12-27 12:52:07 +0000
committerBram Moolenaar <Bram@vim.org>2021-12-27 12:52:07 +0000
commitbf7ff61af490a2fbc0d9c7d42f3bb7eb7d37127e (patch)
treeb5163f9d91a7b0389ddc7cc7551b8f8bee213859 /src/insexpand.c
parentef8f04b1d1daf2e0be8fa38dedcae84ebfc5ffb4 (diff)
downloadvim-git-bf7ff61af490a2fbc0d9c7d42f3bb7eb7d37127e.tar.gz
patch 8.2.3912: the ins_complete() function is much too longv8.2.3912
Problem: The ins_complete() function is much too long. Solution: Split it up into multiple functions. (Yegappan Lakshmanan, closes #9414)
Diffstat (limited to 'src/insexpand.c')
-rw-r--r--src/insexpand.c624
1 files changed, 366 insertions, 258 deletions
diff --git a/src/insexpand.c b/src/insexpand.c
index bc490b964..b0319a26e 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -3832,6 +3832,364 @@ ins_compl_use_match(int c)
}
/*
+ * Get the pattern, column and length for normal completion (CTRL-N CTRL-P
+ * completion)
+ * Sets the global variables: compl_col, compl_length and compl_pattern.
+ * Uses the global variables: compl_cont_status and ctrl_x_mode
+ */
+ static int
+get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
+{
+ if ((compl_cont_status & CONT_SOL)
+ || ctrl_x_mode == CTRL_X_PATH_DEFINES)
+ {
+ if (!(compl_cont_status & CONT_ADDING))
+ {
+ while (--startcol >= 0 && vim_isIDc(line[startcol]))
+ ;
+ compl_col += ++startcol;
+ compl_length = curs_col - startcol;
+ }
+ if (p_ic)
+ compl_pattern = str_foldcase(line + compl_col,
+ compl_length, NULL, 0);
+ else
+ compl_pattern = vim_strnsave(line + compl_col, compl_length);
+ if (compl_pattern == NULL)
+ return FAIL;
+ }
+ else if (compl_cont_status & CONT_ADDING)
+ {
+ char_u *prefix = (char_u *)"\\<";
+
+ // we need up to 2 extra chars for the prefix
+ compl_pattern = alloc(quote_meta(NULL, line + compl_col,
+ compl_length) + 2);
+ if (compl_pattern == NULL)
+ return FAIL;
+ if (!vim_iswordp(line + compl_col)
+ || (compl_col > 0
+ && (vim_iswordp(mb_prevptr(line, line + compl_col)))))
+ prefix = (char_u *)"";
+ STRCPY((char *)compl_pattern, prefix);
+ (void)quote_meta(compl_pattern + STRLEN(prefix),
+ line + compl_col, compl_length);
+ }
+ else if (--startcol < 0
+ || !vim_iswordp(mb_prevptr(line, line + startcol + 1)))
+ {
+ // Match any word of at least two chars
+ compl_pattern = vim_strsave((char_u *)"\\<\\k\\k");
+ if (compl_pattern == NULL)
+ return FAIL;
+ compl_col += curs_col;
+ compl_length = 0;
+ }
+ else
+ {
+ // Search the point of change class of multibyte character
+ // or not a word single byte character backward.
+ if (has_mbyte)
+ {
+ int base_class;
+ int head_off;
+
+ startcol -= (*mb_head_off)(line, line + startcol);
+ base_class = mb_get_class(line + startcol);
+ while (--startcol >= 0)
+ {
+ head_off = (*mb_head_off)(line, line + startcol);
+ if (base_class != mb_get_class(line + startcol
+ - head_off))
+ break;
+ startcol -= head_off;
+ }
+ }
+ else
+ while (--startcol >= 0 && vim_iswordc(line[startcol]))
+ ;
+ compl_col += ++startcol;
+ compl_length = (int)curs_col - startcol;
+ if (compl_length == 1)
+ {
+ // Only match word with at least two chars -- webb
+ // there's no need to call quote_meta,
+ // alloc(7) is enough -- Acevedo
+ compl_pattern = alloc(7);
+ if (compl_pattern == NULL)
+ return FAIL;
+ STRCPY((char *)compl_pattern, "\\<");
+ (void)quote_meta(compl_pattern + 2, line + compl_col, 1);
+ STRCAT((char *)compl_pattern, "\\k");
+ }
+ else
+ {
+ compl_pattern = alloc(quote_meta(NULL, line + compl_col,
+ compl_length) + 2);
+ if (compl_pattern == NULL)
+ return FAIL;
+ STRCPY((char *)compl_pattern, "\\<");
+ (void)quote_meta(compl_pattern + 2, line + compl_col,
+ compl_length);
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * Get the pattern, column and length for whole line completion or for the
+ * complete() function.
+ * Sets the global variables: compl_col, compl_length and compl_pattern.
+ */
+ static int
+get_wholeline_compl_info(char_u *line, colnr_T curs_col)
+{
+ compl_col = (colnr_T)getwhitecols(line);
+ compl_length = (int)curs_col - (int)compl_col;
+ if (compl_length < 0) // cursor in indent: empty pattern
+ compl_length = 0;
+ if (p_ic)
+ compl_pattern = str_foldcase(line + compl_col, compl_length,
+ NULL, 0);
+ else
+ compl_pattern = vim_strnsave(line + compl_col, compl_length);
+ if (compl_pattern == NULL)
+ return FAIL;
+
+ return OK;
+}
+
+/*
+ * Get the pattern, column and length for filename completion.
+ * Sets the global variables: compl_col, compl_length and compl_pattern.
+ */
+ static int
+get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col)
+{
+ // Go back to just before the first filename character.
+ if (startcol > 0)
+ {
+ char_u *p = line + startcol;
+
+ MB_PTR_BACK(line, p);
+ while (p > line && vim_isfilec(PTR2CHAR(p)))
+ MB_PTR_BACK(line, p);
+ if (p == line && vim_isfilec(PTR2CHAR(p)))
+ startcol = 0;
+ else
+ startcol = (int)(p - line) + 1;
+ }
+
+ compl_col += startcol;
+ compl_length = (int)curs_col - startcol;
+ compl_pattern = addstar(line + compl_col, compl_length, EXPAND_FILES);
+ if (compl_pattern == NULL)
+ return FAIL;
+
+ return OK;
+}
+
+/*
+ * Get the pattern, column and length for command-line completion.
+ * Sets the global variables: compl_col, compl_length and compl_pattern.
+ */
+ static int
+get_cmdline_compl_info(char_u *line, colnr_T curs_col)
+{
+ compl_pattern = vim_strnsave(line, curs_col);
+ if (compl_pattern == NULL)
+ return FAIL;
+ set_cmd_context(&compl_xp, compl_pattern,
+ (int)STRLEN(compl_pattern), curs_col, FALSE);
+ if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
+ || compl_xp.xp_context == EXPAND_NOTHING)
+ // No completion possible, use an empty pattern to get a
+ // "pattern not found" message.
+ compl_col = curs_col;
+ else
+ compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
+ compl_length = curs_col - compl_col;
+
+ return OK;
+}
+
+/*
+ * Get the pattern, column and length for user defined completion ('omnifunc',
+ * 'completefunc' and 'thesaurusfunc')
+ * Sets the global variables: compl_col, compl_length and compl_pattern.
+ * Uses the global variable: spell_bad_len
+ */
+ static int
+get_userdefined_compl_info(colnr_T curs_col UNUSED)
+{
+ int ret = FAIL;
+
+#ifdef FEAT_COMPL_FUNC
+ // Call user defined function 'completefunc' with "a:findstart"
+ // set to 1 to obtain the length of text to use for completion.
+ char_u *line;
+ typval_T args[3];
+ int col;
+ char_u *funcname;
+ pos_T pos;
+ int save_State = State;
+ callback_T *cb;
+
+ // Call 'completefunc' or 'omnifunc' or 'thesaurusfunc' and get pattern
+ // length as a string
+ funcname = get_complete_funcname(ctrl_x_mode);
+ if (*funcname == NUL)
+ {
+ semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION
+ ? "completefunc" : "omnifunc");
+ return FAIL;
+ }
+
+ args[0].v_type = VAR_NUMBER;
+ args[0].vval.v_number = 1;
+ args[1].v_type = VAR_STRING;
+ args[1].vval.v_string = (char_u *)"";
+ args[2].v_type = VAR_UNKNOWN;
+ pos = curwin->w_cursor;
+ ++textwinlock;
+ cb = get_insert_callback(ctrl_x_mode);
+ col = call_callback_retnr(cb, 2, args);
+ --textwinlock;
+
+ State = save_State;
+ curwin->w_cursor = pos; // restore the cursor position
+ validate_cursor();
+ if (!EQUAL_POS(curwin->w_cursor, pos))
+ {
+ emsg(_(e_compldel));
+ return FAIL;
+ }
+
+ // Return value -2 means the user complete function wants to
+ // cancel the complete without an error.
+ // Return value -3 does the same as -2 and leaves CTRL-X mode.
+ if (col == -2)
+ return FAIL;
+ if (col == -3)
+ {
+ ctrl_x_mode = CTRL_X_NORMAL;
+ edit_submode = NULL;
+ if (!shortmess(SHM_COMPLETIONMENU))
+ msg_clr_cmdline();
+ return FAIL;
+ }
+
+ // Reset extended parameters of completion, when start new
+ // completion.
+ compl_opt_refresh_always = FALSE;
+ compl_opt_suppress_empty = FALSE;
+
+ if (col < 0)
+ col = curs_col;
+ compl_col = col;
+ if (compl_col > curs_col)
+ compl_col = curs_col;
+
+ // Setup variables for completion. Need to obtain "line" again,
+ // it may have become invalid.
+ line = ml_get(curwin->w_cursor.lnum);
+ compl_length = curs_col - compl_col;
+ compl_pattern = vim_strnsave(line + compl_col, compl_length);
+ if (compl_pattern == NULL)
+ return FAIL;
+
+ ret = OK;
+#endif
+
+ return ret;
+}
+
+/*
+ * Get the pattern, column and length for spell completion.
+ * Sets the global variables: compl_col, compl_length and compl_pattern.
+ * Uses the global variable: spell_bad_len
+ */
+ static int
+get_spell_compl_info(int startcol UNUSED, colnr_T curs_col UNUSED)
+{
+ int ret = FAIL;
+#ifdef FEAT_SPELL
+ char_u *line;
+
+ if (spell_bad_len > 0)
+ compl_col = curs_col - spell_bad_len;
+ else
+ compl_col = spell_word_start(startcol);
+ if (compl_col >= (colnr_T)startcol)
+ {
+ compl_length = 0;
+ compl_col = curs_col;
+ }
+ else
+ {
+ spell_expand_check_cap(compl_col);
+ compl_length = (int)curs_col - compl_col;
+ }
+ // Need to obtain "line" again, it may have become invalid.
+ line = ml_get(curwin->w_cursor.lnum);
+ compl_pattern = vim_strnsave(line + compl_col, compl_length);
+ if (compl_pattern == NULL)
+ return FAIL;
+
+ ret = OK;
+#endif
+
+ return ret;
+}
+
+/*
+ * Get the completion pattern, column and length.
+ */
+ static int
+compl_get_info(char_u *line, int startcol, colnr_T curs_col, int *line_invalid)
+{
+ if (ctrl_x_mode == CTRL_X_NORMAL
+ || (ctrl_x_mode & CTRL_X_WANT_IDENT
+ && !thesaurus_func_complete(ctrl_x_mode)))
+ {
+ return get_normal_compl_info(line, startcol, curs_col);
+ }
+ else if (ctrl_x_mode_line_or_eval())
+ {
+ return get_wholeline_compl_info(line, curs_col);
+ }
+ else if (ctrl_x_mode == CTRL_X_FILES)
+ {
+ return get_filename_compl_info(line, startcol, curs_col);
+ }
+ else if (ctrl_x_mode == CTRL_X_CMDLINE)
+ {
+ return get_cmdline_compl_info(line, curs_col);
+ }
+ else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI
+ || thesaurus_func_complete(ctrl_x_mode))
+ {
+ if (get_userdefined_compl_info(curs_col) == FAIL)
+ return FAIL;
+ *line_invalid = TRUE; // 'line' may have become invalid
+ }
+ else if (ctrl_x_mode == CTRL_X_SPELL)
+ {
+ if (get_spell_compl_info(startcol, curs_col) == FAIL)
+ return FAIL;
+ *line_invalid = TRUE; // 'line' may have become invalid
+ }
+ else
+ {
+ internal_error("ins_complete()");
+ return FAIL;
+ }
+
+ return OK;
+}
+
+/*
* Do Insert mode completion.
* Called when character "c" was typed, which has a meaning for completion.
* Returns OK if completion was done, FAIL if something failed (out of mem).
@@ -3846,10 +4204,9 @@ ins_complete(int c, int enable_pum)
int save_w_wrow;
int save_w_leftcol;
int insert_match;
-#ifdef FEAT_COMPL_FUNC
int save_did_ai = did_ai;
-#endif
int flags = CP_ORIGINAL_TEXT;
+ int line_invalid = FALSE;
compl_direction = ins_compl_key2dir(c);
insert_match = ins_compl_use_match(c);
@@ -3947,273 +4304,24 @@ ins_complete(int c, int enable_pum)
}
// Work out completion pattern and original text -- webb
- if (ctrl_x_mode == CTRL_X_NORMAL
- || (ctrl_x_mode & CTRL_X_WANT_IDENT
- && !thesaurus_func_complete(ctrl_x_mode)))
+ if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL)
{
- if ((compl_cont_status & CONT_SOL)
- || ctrl_x_mode == CTRL_X_PATH_DEFINES)
- {
- if (!(compl_cont_status & CONT_ADDING))
- {
- while (--startcol >= 0 && vim_isIDc(line[startcol]))
- ;
- compl_col += ++startcol;
- compl_length = curs_col - startcol;
- }
- if (p_ic)
- compl_pattern = str_foldcase(line + compl_col,
- compl_length, NULL, 0);
- else
- compl_pattern = vim_strnsave(line + compl_col,
- compl_length);
- if (compl_pattern == NULL)
- return FAIL;
- }
- else if (compl_cont_status & CONT_ADDING)
- {
- char_u *prefix = (char_u *)"\\<";
-
- // we need up to 2 extra chars for the prefix
- compl_pattern = alloc(quote_meta(NULL, line + compl_col,
- compl_length) + 2);
- if (compl_pattern == NULL)
- return FAIL;
- if (!vim_iswordp(line + compl_col)
- || (compl_col > 0
- && (vim_iswordp(mb_prevptr(line, line + compl_col)))))
- prefix = (char_u *)"";
- STRCPY((char *)compl_pattern, prefix);
- (void)quote_meta(compl_pattern + STRLEN(prefix),
- line + compl_col, compl_length);
- }
- else if (--startcol < 0
- || !vim_iswordp(mb_prevptr(line, line + startcol + 1)))
- {
- // Match any word of at least two chars
- compl_pattern = vim_strsave((char_u *)"\\<\\k\\k");
- if (compl_pattern == NULL)
- return FAIL;
- compl_col += curs_col;
- compl_length = 0;
- }
- else
- {
- // Search the point of change class of multibyte character
- // or not a word single byte character backward.
- if (has_mbyte)
- {
- int base_class;
- int head_off;
-
- startcol -= (*mb_head_off)(line, line + startcol);
- base_class = mb_get_class(line + startcol);
- while (--startcol >= 0)
- {
- head_off = (*mb_head_off)(line, line + startcol);
- if (base_class != mb_get_class(line + startcol
- - head_off))
- break;
- startcol -= head_off;
- }
- }
- else
- while (--startcol >= 0 && vim_iswordc(line[startcol]))
- ;
- compl_col += ++startcol;
- compl_length = (int)curs_col - startcol;
- if (compl_length == 1)
- {
- // Only match word with at least two chars -- webb
- // there's no need to call quote_meta,
- // alloc(7) is enough -- Acevedo
- compl_pattern = alloc(7);
- if (compl_pattern == NULL)
- return FAIL;
- STRCPY((char *)compl_pattern, "\\<");
- (void)quote_meta(compl_pattern + 2, line + compl_col, 1);
- STRCAT((char *)compl_pattern, "\\k");
- }
- else
- {
- compl_pattern = alloc(quote_meta(NULL, line + compl_col,
- compl_length) + 2);
- if (compl_pattern == NULL)
- return FAIL;
- STRCPY((char *)compl_pattern, "\\<");
- (void)quote_meta(compl_pattern + 2, line + compl_col,
- compl_length);
- }
- }
- }
- else if (ctrl_x_mode_line_or_eval())
- {
- compl_col = (colnr_T)getwhitecols(line);
- compl_length = (int)curs_col - (int)compl_col;
- if (compl_length < 0) // cursor in indent: empty pattern
- compl_length = 0;
- if (p_ic)
- compl_pattern = str_foldcase(line + compl_col, compl_length,
- NULL, 0);
- else
- compl_pattern = vim_strnsave(line + compl_col, compl_length);
- if (compl_pattern == NULL)
- return FAIL;
- }
- else if (ctrl_x_mode == CTRL_X_FILES)
- {
- // Go back to just before the first filename character.
- if (startcol > 0)
- {
- char_u *p = line + startcol;
-
- MB_PTR_BACK(line, p);
- while (p > line && vim_isfilec(PTR2CHAR(p)))
- MB_PTR_BACK(line, p);
- if (p == line && vim_isfilec(PTR2CHAR(p)))
- startcol = 0;
- else
- startcol = (int)(p - line) + 1;
- }
-
- compl_col += startcol;
- compl_length = (int)curs_col - startcol;
- compl_pattern = addstar(line + compl_col, compl_length,
- EXPAND_FILES);
- if (compl_pattern == NULL)
- return FAIL;
- }
- else if (ctrl_x_mode == CTRL_X_CMDLINE)
- {
- compl_pattern = vim_strnsave(line, curs_col);
- if (compl_pattern == NULL)
- return FAIL;
- set_cmd_context(&compl_xp, compl_pattern,
- (int)STRLEN(compl_pattern), curs_col, FALSE);
- if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
- || compl_xp.xp_context == EXPAND_NOTHING)
- // No completion possible, use an empty pattern to get a
- // "pattern not found" message.
- compl_col = curs_col;
- else
- compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
- compl_length = curs_col - compl_col;
- }
- else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI
- || thesaurus_func_complete(ctrl_x_mode))
- {
-#ifdef FEAT_COMPL_FUNC
- // Call user defined function 'completefunc' with "a:findstart"
- // set to 1 to obtain the length of text to use for completion.
- typval_T args[3];
- int col;
- char_u *funcname;
- pos_T pos;
- int save_State = State;
- callback_T *cb;
-
- // Call 'completefunc' or 'omnifunc' and get pattern length as a
- // string
- funcname = get_complete_funcname(ctrl_x_mode);
- if (*funcname == NUL)
- {
- semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION
- ? "completefunc" : "omnifunc");
+ if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI
+ || thesaurus_func_complete(ctrl_x_mode))
// restore did_ai, so that adding comment leader works
did_ai = save_did_ai;
- return FAIL;
- }
-
- args[0].v_type = VAR_NUMBER;
- args[0].vval.v_number = 1;
- args[1].v_type = VAR_STRING;
- args[1].vval.v_string = (char_u *)"";
- args[2].v_type = VAR_UNKNOWN;
- pos = curwin->w_cursor;
- ++textwinlock;
- cb = get_insert_callback(ctrl_x_mode);
- col = call_callback_retnr(cb, 2, args);
- --textwinlock;
-
- State = save_State;
- curwin->w_cursor = pos; // restore the cursor position
- validate_cursor();
- if (!EQUAL_POS(curwin->w_cursor, pos))
- {
- emsg(_(e_compldel));
- return FAIL;
- }
-
- // Return value -2 means the user complete function wants to
- // cancel the complete without an error.
- // Return value -3 does the same as -2 and leaves CTRL-X mode.
- if (col == -2)
- return FAIL;
- if (col == -3)
- {
- ctrl_x_mode = CTRL_X_NORMAL;
- edit_submode = NULL;
- if (!shortmess(SHM_COMPLETIONMENU))
- msg_clr_cmdline();
- return FAIL;
- }
-
- // Reset extended parameters of completion, when start new
- // completion.
- compl_opt_refresh_always = FALSE;
- compl_opt_suppress_empty = FALSE;
-
- if (col < 0)
- col = curs_col;
- compl_col = col;
- if (compl_col > curs_col)
- compl_col = curs_col;
-
- // Setup variables for completion. Need to obtain "line" again,
- // it may have become invalid.
- line = ml_get(curwin->w_cursor.lnum);
- compl_length = curs_col - compl_col;
- compl_pattern = vim_strnsave(line + compl_col, compl_length);
- if (compl_pattern == NULL)
-#endif
- return FAIL;
- }
- else if (ctrl_x_mode == CTRL_X_SPELL)
- {
-#ifdef FEAT_SPELL
- if (spell_bad_len > 0)
- compl_col = curs_col - spell_bad_len;
- else
- compl_col = spell_word_start(startcol);
- if (compl_col >= (colnr_T)startcol)
- {
- compl_length = 0;
- compl_col = curs_col;
- }
- else
- {
- spell_expand_check_cap(compl_col);
- compl_length = (int)curs_col - compl_col;
- }
- // Need to obtain "line" again, it may have become invalid.
- line = ml_get(curwin->w_cursor.lnum);
- compl_pattern = vim_strnsave(line + compl_col, compl_length);
- if (compl_pattern == NULL)
-#endif
- return FAIL;
- }
- else
- {
- internal_error("ins_complete()");
return FAIL;
}
+ // If "line" was changed while getting completion info get it again.
+ if (line_invalid)
+ line = ml_get(curwin->w_cursor.lnum);
if (compl_cont_status & CONT_ADDING)
{
edit_submode_pre = (char_u *)_(" Adding");
if (ctrl_x_mode_line_or_eval())
{
- // Insert a new line, keep indentation but ignore 'comments'
+ // Insert a new line, keep indentation but ignore 'comments'.
char_u *old = curbuf->b_p_com;
curbuf->b_p_com = (char_u *)"";