changeset 26767:84d56c5b7f2b v8.2.3912

patch 8.2.3912: the ins_complete() function is much too long Commit: https://github.com/vim/vim/commit/bf7ff61af490a2fbc0d9c7d42f3bb7eb7d37127e Author: Bram Moolenaar <Bram@vim.org> Date: Mon Dec 27 12:52:07 2021 +0000 patch 8.2.3912: the ins_complete() function is much too long Problem: The ins_complete() function is much too long. Solution: Split it up into multiple functions. (Yegappan Lakshmanan, closes #9414)
author Bram Moolenaar <Bram@vim.org>
date Mon, 27 Dec 2021 14:00:04 +0100
parents a91c9f5d52d9
children cdea9da4aeb3
files src/insexpand.c src/version.c
diffstat 2 files changed, 368 insertions(+), 258 deletions(-) [+]
line wrap: on
line diff
--- 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 *)"";
--- a/src/version.c
+++ b/src/version.c
@@ -750,6 +750,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3912,
+/**/
     3911,
 /**/
     3910,