diff options
author | Yegappan Lakshmanan <yegappan@yahoo.com> | 2021-12-31 12:59:53 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-12-31 12:59:53 +0000 |
commit | 6ad84ab3e48d9490e4139df04f2c55b136f5501d (patch) | |
tree | 18ed17aaca9b747046507d308bc16bebffe0b877 /src | |
parent | 1fa3de1ce806ba18ebcc00c6d9a0678a84735463 (diff) |
patch 8.2.3953: insert completion code is too complicatedv8.2.3953
Problem: Insert completion code is too complicated.
Solution: More refactoring. Move function arguments into a struct.
(Yegappan Lakshmanan, closes #9437)
Diffstat (limited to 'src')
-rw-r--r-- | src/insexpand.c | 1165 | ||||
-rw-r--r-- | src/version.c | 2 |
2 files changed, 605 insertions, 562 deletions
diff --git a/src/insexpand.c b/src/insexpand.c index aecbf77b46..3cefb39748 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -134,6 +134,7 @@ static char e_compldel[] = N_("E840: Completion function deleted text"); * "compl_curr_match" points to the currently selected entry. * "compl_shown_match" is different from compl_curr_match during * ins_compl_get_exp(). + * "compl_old_match" points to previous "compl_curr_match". */ static compl_T *compl_first_match = NULL; static compl_T *compl_curr_match = NULL; @@ -227,8 +228,7 @@ ins_ctrl_x(void) { if (!ctrl_x_mode_cmdline()) { - // if the next ^X<> won't ADD nothing, then reset - // compl_cont_status + // if the next ^X<> won't ADD nothing, then reset compl_cont_status if (compl_cont_status & CONT_N_ADDS) compl_cont_status |= CONT_INTRPT; else @@ -392,6 +392,16 @@ vim_is_ctrl_x_key(int c) } /* + * Returns TRUE if the currently shown text is the original text when the + * completion began. + */ + static int +ins_compl_at_original_text(compl_T *match) +{ + return match->cp_flags & CP_ORIGINAL_TEXT; +} + +/* * Return TRUE when character "c" is part of the item currently being * completed. Used to decide whether to abandon complete mode when the menu * is visible. @@ -426,6 +436,111 @@ ins_compl_accept_char(int c) } /* + * Get the completed text by inferring the case of the originally typed text. + */ + static char_u * +ins_compl_infercase_gettext( + char_u *str, + int actual_len, + int actual_compl_length, + int min_len) +{ + int *wca; // Wide character array. + char_u *p; + int i, c; + int has_lower = FALSE; + int was_letter = FALSE; + + IObuff[0] = NUL; + + // Allocate wide character array for the completion and fill it. + wca = ALLOC_MULT(int, actual_len); + if (wca == NULL) + return IObuff; + + p = str; + for (i = 0; i < actual_len; ++i) + if (has_mbyte) + wca[i] = mb_ptr2char_adv(&p); + else + wca[i] = *(p++); + + // Rule 1: Were any chars converted to lower? + p = compl_orig_text; + for (i = 0; i < min_len; ++i) + { + if (has_mbyte) + c = mb_ptr2char_adv(&p); + else + c = *(p++); + if (MB_ISLOWER(c)) + { + has_lower = TRUE; + if (MB_ISUPPER(wca[i])) + { + // Rule 1 is satisfied. + for (i = actual_compl_length; i < actual_len; ++i) + wca[i] = MB_TOLOWER(wca[i]); + break; + } + } + } + + // Rule 2: No lower case, 2nd consecutive letter converted to + // upper case. + if (!has_lower) + { + p = compl_orig_text; + for (i = 0; i < min_len; ++i) + { + if (has_mbyte) + c = mb_ptr2char_adv(&p); + else + c = *(p++); + if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i])) + { + // Rule 2 is satisfied. + for (i = actual_compl_length; i < actual_len; ++i) + wca[i] = MB_TOUPPER(wca[i]); + break; + } + was_letter = MB_ISLOWER(c) || MB_ISUPPER(c); + } + } + + // Copy the original case of the part we typed. + p = compl_orig_text; + for (i = 0; i < min_len; ++i) + { + if (has_mbyte) + c = mb_ptr2char_adv(&p); + else + c = *(p++); + if (MB_ISLOWER(c)) + wca[i] = MB_TOLOWER(wca[i]); + else if (MB_ISUPPER(c)) + wca[i] = MB_TOUPPER(wca[i]); + } + + // Generate encoding specific output from wide character array. + // Multi-byte characters can occupy up to five bytes more than + // ASCII characters, and we also need one byte for NUL, so stay + // six bytes away from the edge of IObuff. + p = IObuff; + i = 0; + while (i < actual_len && (p - IObuff + 6) < IOSIZE) + if (has_mbyte) + p += (*mb_char2bytes)(wca[i++], p); + else + *(p++) = wca[i++]; + *p = NUL; + + vim_free(wca); + + return IObuff; +} + +/* * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the * case of the originally typed text is used, and the case of the completed * text is inferred, ie this tries to work out what case you probably wanted @@ -442,13 +557,9 @@ ins_compl_add_infercase( { char_u *str = str_arg; char_u *p; - int i, c; int actual_len; // Take multi-byte characters int actual_compl_length; // into account. int min_len; - int *wca; // Wide character array. - int has_lower = FALSE; - int was_letter = FALSE; int flags = 0; if (p_ic && curbuf->b_p_inf && len > 0) @@ -488,91 +599,8 @@ ins_compl_add_infercase( min_len = actual_len < actual_compl_length ? actual_len : actual_compl_length; - // Allocate wide character array for the completion and fill it. - wca = ALLOC_MULT(int, actual_len); - if (wca != NULL) - { - p = str; - for (i = 0; i < actual_len; ++i) - if (has_mbyte) - wca[i] = mb_ptr2char_adv(&p); - else - wca[i] = *(p++); - - // Rule 1: Were any chars converted to lower? - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (MB_ISLOWER(c)) - { - has_lower = TRUE; - if (MB_ISUPPER(wca[i])) - { - // Rule 1 is satisfied. - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = MB_TOLOWER(wca[i]); - break; - } - } - } - - // Rule 2: No lower case, 2nd consecutive letter converted to - // upper case. - if (!has_lower) - { - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i])) - { - // Rule 2 is satisfied. - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = MB_TOUPPER(wca[i]); - break; - } - was_letter = MB_ISLOWER(c) || MB_ISUPPER(c); - } - } - - // Copy the original case of the part we typed. - p = compl_orig_text; - for (i = 0; i < min_len; ++i) - { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (MB_ISLOWER(c)) - wca[i] = MB_TOLOWER(wca[i]); - else if (MB_ISUPPER(c)) - wca[i] = MB_TOUPPER(wca[i]); - } - - // Generate encoding specific output from wide character array. - // Multi-byte characters can occupy up to five bytes more than - // ASCII characters, and we also need one byte for NUL, so stay - // six bytes away from the edge of IObuff. - p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) - if (has_mbyte) - p += (*mb_char2bytes)(wca[i++], p); - else - *(p++) = wca[i++]; - *p = NUL; - - vim_free(wca); - } - - str = IObuff; + str = ins_compl_infercase_gettext(str, actual_len, actual_compl_length, + min_len); } if (cont_s_ipos) flags |= CP_CONT_S_IPOS; @@ -618,7 +646,7 @@ ins_compl_add( match = compl_first_match; do { - if ( !(match->cp_flags & CP_ORIGINAL_TEXT) + if (!ins_compl_at_original_text(match) && STRNCMP(match->cp_str, str, len) == 0 && match->cp_str[len] == NUL) return NOTDONE; @@ -730,69 +758,69 @@ ins_compl_longest_match(compl_T *match) { // First match, use it as a whole. compl_leader = vim_strsave(match->cp_str); - if (compl_leader != NULL) - { - had_match = (curwin->w_cursor.col > compl_col); + if (compl_leader == NULL) + return; + + had_match = (curwin->w_cursor.col > compl_col); + ins_compl_delete(); + ins_bytes(compl_leader + ins_compl_len()); + ins_redraw(FALSE); + + // When the match isn't there (to avoid matching itself) remove it + // again after redrawing. + if (!had_match) ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); + compl_used_match = FALSE; - // When the match isn't there (to avoid matching itself) remove it - // again after redrawing. - if (!had_match) - ins_compl_delete(); - compl_used_match = FALSE; - } + return; } - else + + // Reduce the text if this match differs from compl_leader. + p = compl_leader; + s = match->cp_str; + while (*p != NUL) { - // Reduce the text if this match differs from compl_leader. - p = compl_leader; - s = match->cp_str; - while (*p != NUL) + if (has_mbyte) { - if (has_mbyte) - { - c1 = mb_ptr2char(p); - c2 = mb_ptr2char(s); - } - else - { - c1 = *p; - c2 = *s; - } - if ((match->cp_flags & CP_ICASE) - ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) : (c1 != c2)) - break; - if (has_mbyte) - { - MB_PTR_ADV(p); - MB_PTR_ADV(s); - } - else - { - ++p; - ++s; - } + c1 = mb_ptr2char(p); + c2 = mb_ptr2char(s); } - - if (*p != NUL) + else { - // Leader was shortened, need to change the inserted text. - *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - // When the match isn't there (to avoid matching itself) remove it - // again after redrawing. - if (!had_match) - ins_compl_delete(); + c1 = *p; + c2 = *s; } + if ((match->cp_flags & CP_ICASE) + ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) : (c1 != c2)) + break; + if (has_mbyte) + { + MB_PTR_ADV(p); + MB_PTR_ADV(s); + } + else + { + ++p; + ++s; + } + } - compl_used_match = FALSE; + if (*p != NUL) + { + // Leader was shortened, need to change the inserted text. + *p = NUL; + had_match = (curwin->w_cursor.col > compl_col); + ins_compl_delete(); + ins_bytes(compl_leader + ins_compl_len()); + ins_redraw(FALSE); + + // When the match isn't there (to avoid matching itself) remove it + // again after redrawing. + if (!had_match) + ins_compl_delete(); } + + compl_used_match = FALSE; } /* @@ -827,19 +855,20 @@ ins_compl_make_cyclic(void) compl_T *match; int count = 0; - if (compl_first_match != NULL) + if (compl_first_match == NULL) + return 0; + + // Find the end of the list. + match = compl_first_match; + // there's always an entry for the compl_orig_text, it doesn't count. + while (match->cp_next != NULL && match->cp_next != compl_first_match) { - // Find the end of the list. - match = compl_first_match; - // there's always an entry for the compl_orig_text, it doesn't count. - while (match->cp_next != NULL && match->cp_next != compl_first_match) - { - match = match->cp_next; - ++count; - } - match->cp_next = compl_first_match; - compl_first_match->cp_prev = match; + match = match->cp_next; + ++count; } + match->cp_next = compl_first_match; + compl_first_match->cp_prev = match; + return count; } @@ -892,14 +921,14 @@ ins_compl_upd_pum(void) { int h; - if (compl_match_array != NULL) - { - h = curwin->w_cline_height; - // Update the screen later, before drawing the popup menu over it. - pum_call_update_screen(); - if (h != curwin->w_cline_height) - ins_compl_del_pum(); - } + if (compl_match_array == NULL) + return; + + h = curwin->w_cline_height; + // Update the screen later, before drawing the popup menu over it. + pum_call_update_screen(); + if (h != curwin->w_cline_height) + ins_compl_del_pum(); } /* @@ -908,11 +937,11 @@ ins_compl_upd_pum(void) static void ins_compl_del_pum(void) { - if (compl_match_array != NULL) - { - pum_undisplay(); - VIM_CLEAR(compl_match_array); - } + if (compl_match_array == NULL) + return; + + pum_undisplay(); + VIM_CLEAR(compl_match_array); } /* @@ -952,7 +981,7 @@ pum_enough_matches(void) do { if (compl == NULL - || ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) + || (!ins_compl_at_original_text(compl) && ++i == 2)) break; compl = compl->cp_next; } while (compl != compl_first_match); @@ -972,18 +1001,19 @@ ins_compl_dict_alloc(compl_T *match) { dict_T *dict = dict_alloc_lock(VAR_FIXED); - if (dict != NULL) - { - dict_add_string(dict, "word", match->cp_str); - dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]); - dict_add_string(dict, "menu", match->cp_text[CPT_MENU]); - dict_add_string(dict, "kind", match->cp_text[CPT_KIND]); - dict_add_string(dict, "info", match->cp_text[CPT_INFO]); - if (match->cp_user_data.v_type == VAR_UNKNOWN) - dict_add_string(dict, "user_data", (char_u *)""); - else - dict_add_tv(dict, "user_data", &match->cp_user_data); - } + if (dict == NULL) + return NULL; + + dict_add_string(dict, "word", match->cp_str); + dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]); + dict_add_string(dict, "menu", match->cp_text[CPT_MENU]); + dict_add_string(dict, "kind", match->cp_text[CPT_KIND]); + dict_add_string(dict, "info", match->cp_text[CPT_INFO]); + if (match->cp_user_data.v_type == VAR_UNKNOWN) + dict_add_string(dict, "user_data", (char_u *)""); + else + dict_add_tv(dict, "user_data", &match->cp_user_data); + return dict; } @@ -1020,11 +1050,12 @@ trigger_complete_changed_event(int cur) #endif /* - * Show the popup menu for the list of matches. - * Also adjusts "compl_shown_match" to an entry that is actually displayed. + * Build a popup menu to show the completion matches. + * Returns the popup menu entry that should be selected. Returns -1 if nothing + * should be selected. */ - void -ins_compl_show_pum(void) + static int +ins_compl_build_pum(void) { compl_T *compl; compl_T *shown_compl = NULL; @@ -1032,108 +1063,125 @@ ins_compl_show_pum(void) int shown_match_ok = FALSE; int i; int cur = -1; - colnr_T col; int lead_len = 0; - if (!pum_wanted() || !pum_enough_matches()) - return; + // Need to build the popup menu list. + compl_match_arraysize = 0; + compl = compl_first_match; + if (compl_leader != NULL) + lead_len = (int)STRLEN(compl_leader); -#if defined(FEAT_EVAL) - // Dirty hard-coded hack: remove any matchparen highlighting. - do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|:3match none|endif"); -#endif + do + { + if (!ins_compl_at_original_text(compl) + && (compl_leader == NULL + || ins_compl_equal(compl, compl_leader, lead_len))) + ++compl_match_arraysize; + compl = compl->cp_next; + } while (compl != NULL && compl != compl_first_match); - // Update the screen later, before drawing the popup menu over it. - pum_call_update_screen(); + if (compl_match_arraysize == 0) + return -1; + compl_match_array = ALLOC_CLEAR_MULT(pumitem_T, compl_match_arraysize); if (compl_match_array == NULL) + return -1; + + // If the current match is the original text don't find the first + // match after it, don't highlight anything. + if (ins_compl_at_original_text(compl_shown_match)) + shown_match_ok = TRUE; + + i = 0; + compl = compl_first_match; + do { - // Need to build the popup menu list. - compl_match_arraysize = 0; - compl = compl_first_match; - if (compl_leader != NULL) - lead_len = (int)STRLEN(compl_leader); - do + if (!ins_compl_at_original_text(compl) + && (compl_leader == NULL + || ins_compl_equal(compl, compl_leader, lead_len))) { - if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) - ++compl_match_arraysize; - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - if (compl_match_arraysize == 0) - return; - compl_match_array = ALLOC_CLEAR_MULT(pumitem_T, compl_match_arraysize); - if (compl_match_array != NULL) + if (!shown_match_ok) + { + if (compl == compl_shown_match || did_find_shown_match) + { + // This item is the shown match or this is the + // first displayed item after the shown match. + compl_shown_match = compl; + did_find_shown_match = TRUE; + shown_match_ok = TRUE; + } + else + // Remember this displayed match for when the + // shown match is just below it. + shown_compl = compl; + cur = i; + } + + if (compl->cp_text[CPT_ABBR] != NULL) + compl_match_array[i].pum_text = + compl->cp_text[CPT_ABBR]; + else + compl_match_array[i].pum_text = compl->cp_str; + compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; + compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; + if (compl->cp_text[CPT_MENU] != NULL) + compl_match_array[i++].pum_extra = + compl->cp_text[CPT_MENU]; + else + compl_match_array[i++].pum_extra = compl->cp_fname; + } + + if (compl == compl_shown_match) { - // If the current match is the original text don't find the first - // match after it, don't highlight anything. - if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) + did_find_shown_match = TRUE; + + // When the original text is the shown match don't set + // compl_shown_match. + if (ins_compl_at_original_text(compl)) shown_match_ok = TRUE; - i = 0; - compl = compl_first_match; - do + if (!shown_match_ok && shown_compl != NULL) { - if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) - { - if (!shown_match_ok) - { - if (compl == compl_shown_match || did_find_shown_match) - { - // This item is the shown match or this is the - // first displayed item after the shown match. - compl_shown_match = compl; - did_find_shown_match = TRUE; - shown_match_ok = TRUE; - } - else - // Remember this displayed match for when the - // shown match is just below it. - shown_compl = compl; - cur = i; - } + // The shown match isn't displayed, set it to the + // previously displayed match. + compl_shown_match = shown_compl; + shown_match_ok = TRUE; + } + } + compl = compl->cp_next; + } while (compl != NULL && compl != compl_first_match); - if (compl->cp_text[CPT_ABBR] != NULL) - compl_match_array[i].pum_text = - compl->cp_text[CPT_ABBR]; - else - compl_match_array[i].pum_text = compl->cp_str; - compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; - compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; - if (compl->cp_text[CPT_MENU] != NULL) - compl_match_array[i++].pum_extra = - compl->cp_text[CPT_MENU]; - else - compl_match_array[i++].pum_extra = compl->cp_fname; - } + if (!shown_match_ok) // no displayed match at all + cur = -1; - if (compl == compl_shown_match) - { - did_find_shown_match = TRUE; + return cur; +} - // When the original text is the shown match don't set - // compl_shown_match. - if (compl->cp_flags & CP_ORIGINAL_TEXT) - shown_match_ok = TRUE; +/* + * Show the popup menu for the list of matches. + * Also adjusts "compl_shown_match" to an entry that is actually displayed. + */ + void +ins_compl_show_pum(void) +{ + int i; + int cur = -1; + colnr_T col; - if (!shown_match_ok && shown_compl != NULL) - { - // The shown match isn't displayed, set it to the - // previously displayed match. - compl_shown_match = shown_compl; - shown_match_ok = TRUE; - } - } - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); + if (!pum_wanted() || !pum_enough_matches()) + return; - if (!shown_match_ok) // no displayed match at all - cur = -1; - } - } +#if defined(FEAT_EVAL) + // Dirty hard-coded hack: remove any matchparen highlighting. + do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|:3match none|endif"); +#endif + + // Update the screen later, before drawing the popup menu over it. + pum_call_update_screen(); + + if (compl_match_array == NULL) + // Need to build the popup menu list. + cur = ins_compl_build_pum(); else { // popup menu already exists, only need to find the current item. @@ -1147,24 +1195,24 @@ ins_compl_show_pum(void) } } - if (compl_match_array != NULL) - { - // In Replace mode when a $ is displayed at the end of the line only - // part of the screen would be updated. We do need to redraw here. - dollar_vcol = -1; + if (compl_match_array == NULL) + return; + + // In Replace mode when a $ is displayed at the end of the line only + // part of the screen would be updated. We do need to redraw here. + dollar_vcol = -1; - // Compute the screen column of the start of the completed text. - // Use the cursor to get all wrapping and other settings right. - col = curwin->w_cursor.col; - curwin->w_cursor.col = compl_col; - pum_display(compl_match_array, compl_match_arraysize, cur); - curwin->w_cursor.col = col; + // Compute the screen column of the start of the completed text. + // Use the cursor to get all wrapping and other settings right. + col = curwin->w_cursor.col; + curwin->w_cursor.col = compl_col; + pum_display(compl_match_array, compl_match_arraysize, cur); + curwin->w_cursor.col = col; #ifdef FEAT_EVAL - if (has_completechanged()) - trigger_complete_changed_event(cur); + if (has_completechanged()) + trigger_complete_changed_event(cur); #endif - } } #define DICT_FIRST (1) // use just first element in "dict" @@ -1324,77 +1372,76 @@ ins_compl_files( (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); } - if (fp != NULL) + if (fp == NULL) + continue; + + // Read dictionary file line by line. + // Check each line for a match. + while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp)) { - // Read dictionary file line by line. - // Check each line for a match. - while (!got_int && !compl_interrupted - && !vim_fgets(buf, LSIZE, fp)) + ptr = buf; + while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) { - ptr = buf; - while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) + ptr = regmatch->startp[0]; + if (ctrl_x_mode_line_or_eval()) + ptr = find_line_end(ptr); + else + ptr = find_word_end(ptr); + add_r = ins_compl_add_infercase(regmatch->startp[0], + (int)(ptr - regmatch->startp[0]), + p_ic, files[i], *dir, FALSE); + if (thesaurus) { - ptr = regmatch->startp[0]; - if (ctrl_x_mode_line_or_eval()) - ptr = find_line_end(ptr); - else - ptr = find_word_end(ptr); - add_r = ins_compl_add_infercase(regmatch->startp[0], - (int)(ptr - regmatch->startp[0]), - p_ic, files[i], *dir, FALSE); - if (thesaurus) + char_u *wstart; + + // Add the other matches on the line + ptr = buf; + while (!got_int) { - char_u *wstart; - - // Add the other matches on the line - ptr = buf; - while (!got_int) - { - // Find start of the next word. Skip white - // space and punctuation. - ptr = find_word_start(ptr); - if (*ptr == NUL || *ptr == NL) - break; - wstart = ptr; - - // Find end of the word. - if (has_mbyte) - // Japanese words may have characters in - // different classes, only separate words - // with single-byte non-word characters. - while (*ptr != NUL) - { - int l = (*mb_ptr2len)(ptr); - - if (l < 2 && !vim_iswordc(*ptr)) - break; - ptr += l; - } - else - ptr = find_word_end(ptr); - - // Add the word. Skip the regexp match. - if (wstart != regmatch->startp[0]) - add_r = ins_compl_add_infercase(wstart, - (int)(ptr - wstart), - p_ic, files[i], *dir, FALSE); - } + // Find start of the next word. Skip white + // space and punctuation. + ptr = find_word_start(ptr); + if (*ptr == NUL || *ptr == NL) + break; + wstart = ptr; + + // Find end of the word. + if (has_mbyte) + // Japanese words may have characters in + // different classes, only separate words + // with single-byte non-word characters. + while (*ptr != NUL) + { + int l = (*mb_ptr2len)(ptr); + + if (l < 2 && !vim_iswordc(*ptr)) + break; + ptr += l; + } + else + ptr = find_word_end(ptr); + + // Add the word. Skip the regexp match. + if (wstart != regmatch->startp[0]) + add_r = ins_compl_add_infercase(wstart, + (int)(ptr - wstart), + p_ic, files[i], *dir, FALSE); } - if (add_r == OK) - // if dir was BACKWARD then honor it just once - *dir = FORWARD; - else if (add_r == FAIL) - break; - // avoid expensive call to vim_regexec() when at end - // of line - if (*ptr == '\n' || got_int) - break; } - line_breakcheck(); - ins_compl_check_keys(50, FALSE); + if (add_r == OK) + // if dir was BACKWARD then honor it just once + *dir = FORWARD; + else if (add_r == FAIL) + break; + // avoid expensive call to vim_regexec() when at end + // of line + if (*ptr == '\n' || got_int) + break; } - fclose(fp); + line_breakcheck(); + ins_compl_check_keys(50, FALSE); } + fclose(fp); } } @@ -1757,9 +1804,9 @@ ins_compl_set_original_text(char_u *str) char_u *p; // Replace the original text entry. - // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be - // at the last item for backward completion - if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) // safety check + // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly + // be at the last item for backward completion + if (ins_compl_at_original_text(compl_first_match)) // safety check { p = vim_strsave(str); if (p != NULL) @@ -1769,7 +1816,7 @@ ins_compl_set_original_text(char_u *str) } } else if (compl_first_match->cp_prev != NULL - && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) + && ins_compl_at_original_text(compl_first_match->cp_prev)) { p = vim_strsave(str); if (p != NULL) @@ -1797,24 +1844,22 @@ ins_compl_addfrommatch(void) { // When still at the original match use the first entry that matches // the leader. - if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) + if (!ins_compl_at_original_text(compl_shown_match)) + return; + + p = NULL; + for (cp = compl_shown_match->cp_next; cp != NULL + && cp != compl_first_match; cp = cp->cp_next) { - p = NULL; - for (cp = compl_shown_match->cp_next; cp != NULL - && cp != compl_first_match; cp = cp->cp_next) + if (compl_leader == NULL + || ins_compl_equal(cp, compl_leader, + (int)STRLEN(compl_leader))) { - if (compl_leader == NULL - || ins_compl_equal(cp, compl_leader, - (int)STRLEN(compl_leader))) - { - p = cp->cp_str; - break; - } + p = cp->cp_str; + break; } - if (p == NULL || (int)STRLEN(p) <= len) - return; } - else + if (p == NULL || (int)STRLEN(p) <= len) return; } p += len; @@ -2860,7 +2905,7 @@ get_complete_info(list_T *what_list, dict_T *retdict) match = compl_first_match; do { - if (!(match->cp_flags & CP_ORIGINAL_TEXT)) + if (!ins_compl_at_original_text(match)) { di = dict_alloc(); if (di == NULL) @@ -2949,129 +2994,137 @@ enum }; /* - * Process the next 'complete' option value in "e_cpt_arg". + * state information used for getting the next set of insert completion + * matches. + */ +typedef struct +{ + char_u *e_cpt; // current entry in 'complete' + buf_T *ins_buf; // buffer being scanned + pos_T *cur_match_pos; // current match position + pos_T prev_match_pos; // previous match position + int set_match_pos; // save first_match_pos/last_match_pos + pos_T first_match_pos; // first match position + pos_T last_match_pos; // last match position + int found_all; // found all matches of a certain type. + char_u *dict; // dictionary file to search + int dict_f; // "dict" is an exact file name or not +} ins_compl_next_state_T; + +/* + * Process the next 'complete' option value in st->e_cpt. * * If successful, the arguments are set as below: - * e_cpt_arg - pointer to the next option value in 'e_cpt_arg' + * st->cpt - pointer to the next option value in "st->cpt" * compl_type_arg - type of insert mode completion to use - * found_all_arg - all matches of this type are found - * buf_arg - search for completions in this buffer - * first_match_pos - position of the first completion match - * last_match_pos - position of the last completion match - * set_match_pos - TRUE if the first match position should be saved to avoid - * loops after the search wraps around. - * dict - name of the dictionary or thesaurus file to search - * dict_f - flag specifying whether "dict" is an exact file name or not + * st->found_all - all matches of this type are found + * st->ins_buf - search for completions in this buffer + * st->first_match_pos - position of the first completion match + * st->last_match_pos - position of the last completion match + * st->set_match_pos - TRUE if the first match position should be saved to + * avoid loops after the search wraps around. + * st->dict - name of the dictionary or thesaurus file to search + * st->dict_f - flag specifying whether "dict" is an exact file name or not * * Returns INS_COMPL_CPT_OK if the next value is processed successfully. * Returns INS_COMPL_CPT_CONT to skip the current value and process the next * option value. - * Returns INS_COMPL_CPT_END if all the values in "e_cpt" are processed. + * Returns INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed. */ static int process_next_cpt_value( - char_u **e_cpt_arg, + ins_compl_next_state_T *st, int *compl_type_arg, - int *found_all_arg, - buf_T **buf_arg, - pos_T *start_match_pos, - pos_T *first_match_pos, - pos_T *last_match_pos, - int *set_match_pos, - char_u **dict_arg, - int *dict_flag) + pos_T *start_match_pos) { - char_u *e_cpt = *e_cpt_arg; int compl_type = -1; int status = INS_COMPL_CPT_OK; - buf_T *buf = *buf_arg; - int found_all = FALSE; - char_u *dict = NULL; - int dict_f = 0; - while (*e_cpt == ',' || *e_cpt == ' ') - e_cpt++; + st->found_all = FALSE; + + while (*st->e_cpt == ',' || *st->e_cpt == ' ') + st->e_cpt++; - if (*e_cpt == '.' && !curbuf->b_scanned) + if (*st->e_cpt == '.' && !curbuf->b_scanned) { - buf = curbuf; - *first_match_pos = *start_match_pos; + st->ins_buf = curbuf; + st->first_match_pos = *start_match_pos; // Move the cursor back one character so that ^N can match the // word immediately after the cursor. - if (ctrl_x_mode == CTRL_X_NORMAL && dec(first_match_pos) < 0) + if (ctrl_x_mode == CTRL_X_NORMAL && dec(&st->first_match_pos) < 0) { // Move the cursor to after the last character in the // buffer, so that word at start of buffer is found // correctly. - first_match_pos->lnum = buf->b_ml.ml_line_count; - first_match_pos->col = - (colnr_T)STRLEN(ml_get(first_match_pos->lnum)); + st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count; + st->first_match_pos.col = + (colnr_T)STRLEN(ml_get(st->first_match_pos.lnum)); } - *last_match_pos = *first_match_pos; + st->last_match_pos = st->first_match_pos; compl_type = 0; // Remember the first match so that the loop stops when we // wrap and come back there a second time. - *set_match_pos = TRUE; + st->set_match_pos = TRUE; } - else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL - && (buf = ins_compl_next_buf(buf, *e_cpt)) != curbuf) + else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL + && (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != curbuf) { // Scan a buffer, but not the current one. - if (buf->b_ml.ml_mfp != NULL) // loaded buffer + if (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer { compl_started = TRUE; - first_match_pos->col = last_match_pos->col = 0; - first_match_pos->lnum = buf->b_ml.ml_line_count + 1; - last_match_pos->lnum = 0; + st->first_match_pos.col = st->last_match_pos.col = 0; + st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1; + st->last_match_pos.lnum = 0; compl_type = 0; } else // unloaded buffer, scan like dictionary { - found_all = TRUE; - if (buf->b_fname == NULL) + st->found_all = TRUE; + if (st->ins_buf->b_fname == NULL) { status = INS_COMPL_CPT_CONT; goto done; } compl_type = CTRL_X_DICTIONARY; - dict = buf->b_fname; - dict_f = DICT_EXACT; + st->dict = st->ins_buf->b_fname; + st->dict_f = DICT_EXACT; |