# HG changeset patch # User Bram Moolenaar # Date 1640610004 -3600 # Node ID 84d56c5b7f2baa69bc94d9621d9527676c09dfd4 # Parent a91c9f5d52d9f491f525b639911c858d357b3d39 patch 8.2.3912: the ins_complete() function is much too long Commit: https://github.com/vim/vim/commit/bf7ff61af490a2fbc0d9c7d42f3bb7eb7d37127e Author: Bram Moolenaar 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) diff --git a/src/insexpand.c b/src/insexpand.c --- 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 *)""; diff --git a/src/version.c b/src/version.c --- 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,