To: vim_dev@googlegroups.com Subject: Patch 8.2.3912 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3912 Problem: The ins_complete() function is much too long. Solution: Split it up into multiple functions. (Yegappan Lakshmanan, closes #9414) Files: src/insexpand.c *** ../vim-8.2.3911/src/insexpand.c 2021-12-12 16:26:35.868627601 +0000 --- src/insexpand.c 2021-12-27 12:51:35.796611719 +0000 *************** *** 3832,3837 **** --- 3832,4195 ---- } /* + * 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,3855 **** 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; compl_direction = ins_compl_key2dir(c); insert_match = ins_compl_use_match(c); --- 4204,4212 ---- int save_w_wrow; int save_w_leftcol; int insert_match; int save_did_ai = did_ai; int flags = CP_ORIGINAL_TEXT; + int line_invalid = FALSE; compl_direction = ins_compl_key2dir(c); insert_match = ins_compl_use_match(c); *************** *** 3947,4219 **** } // 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_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"); // 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 (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' char_u *old = curbuf->b_p_com; curbuf->b_p_com = (char_u *)""; --- 4304,4327 ---- } // Work out completion pattern and original text -- webb ! if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) { ! 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; } + // 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'. char_u *old = curbuf->b_p_com; curbuf->b_p_com = (char_u *)""; *** ../vim-8.2.3911/src/version.c 2021-12-27 12:29:14.538581864 +0000 --- src/version.c 2021-12-27 12:46:16.049115596 +0000 *************** *** 751,752 **** --- 751,754 ---- { /* Add new patch number below this line */ + /**/ + 3912, /**/ -- "Computers in the future may weigh no more than 1.5 tons." Popular Mechanics, 1949 /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///