To: vim_dev@googlegroups.com Subject: Patch 8.2.3953 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3953 Problem: Insert completion code is too complicated. Solution: More refactoring. Move function arguments into a struct. (Yegappan Lakshmanan, closes #9437) Files: src/insexpand.c *** ../vim-8.2.3952/src/insexpand.c 2021-12-30 11:40:49.936654054 +0000 --- src/insexpand.c 2021-12-31 12:58:29.772742482 +0000 *************** *** 134,139 **** --- 134,140 ---- * "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,234 **** { if (!ctrl_x_mode_cmdline()) { ! // 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 --- 228,234 ---- { if (!ctrl_x_mode_cmdline()) { ! // 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,397 **** --- 392,407 ---- } /* + * 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,431 **** --- 436,546 ---- } /* + * 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,454 **** { 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) --- 557,565 ---- *************** *** 488,578 **** 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; } if (cont_s_ipos) flags |= CP_CONT_S_IPOS; --- 599,606 ---- min_len = actual_len < actual_compl_length ? actual_len : actual_compl_length; ! str = ins_compl_infercase_gettext(str, actual_len, actual_compl_length, ! min_len); } if (cont_s_ipos) flags |= CP_CONT_S_IPOS; *************** *** 618,624 **** match = compl_first_match; do { ! if ( !(match->cp_flags & CP_ORIGINAL_TEXT) && STRNCMP(match->cp_str, str, len) == 0 && match->cp_str[len] == NUL) return NOTDONE; --- 646,652 ---- match = compl_first_match; do { ! if (!ins_compl_at_original_text(match) && STRNCMP(match->cp_str, str, len) == 0 && match->cp_str[len] == NUL) return NOTDONE; *************** *** 730,798 **** { // 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); 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; ! } } ! else { ! // Reduce the text if this match differs from compl_leader. ! p = compl_leader; ! s = match->cp_str; ! while (*p != NUL) { ! 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; ! } } ! ! 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; } } /* --- 758,826 ---- { // First match, use it as a whole. compl_leader = vim_strsave(match->cp_str); ! 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(); ! compl_used_match = FALSE; ! return; } ! ! // Reduce the text if this match differs from compl_leader. ! p = compl_leader; ! s = match->cp_str; ! while (*p != NUL) { ! 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; + } + } ! 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,845 **** compl_T *match; int count = 0; ! if (compl_first_match != NULL) { ! // 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; } return count; } --- 855,874 ---- compl_T *match; int count = 0; ! 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) { ! match = match->cp_next; ! ++count; } + match->cp_next = compl_first_match; + compl_first_match->cp_prev = match; + return count; } *************** *** 892,905 **** { 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(); ! } } /* --- 921,934 ---- { int h; ! 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,918 **** static void ins_compl_del_pum(void) { ! if (compl_match_array != NULL) ! { ! pum_undisplay(); ! VIM_CLEAR(compl_match_array); ! } } /* --- 937,947 ---- static void ins_compl_del_pum(void) { ! if (compl_match_array == NULL) ! return; ! ! pum_undisplay(); ! VIM_CLEAR(compl_match_array); } /* *************** *** 952,958 **** do { if (compl == NULL ! || ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) break; compl = compl->cp_next; } while (compl != compl_first_match); --- 981,987 ---- do { if (compl == NULL ! || (!ins_compl_at_original_text(compl) && ++i == 2)) break; compl = compl->cp_next; } while (compl != compl_first_match); *************** *** 972,989 **** { 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); ! } return dict; } --- 1001,1019 ---- { dict_T *dict = dict_alloc_lock(VAR_FIXED); ! 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,1030 **** #endif /* ! * 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) { compl_T *compl; compl_T *shown_compl = NULL; --- 1050,1061 ---- #endif /* ! * 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. */ ! static int ! ins_compl_build_pum(void) { compl_T *compl; compl_T *shown_compl = NULL; *************** *** 1032,1139 **** int shown_match_ok = FALSE; int i; int cur = -1; - colnr_T col; int lead_len = 0; ! if (!pum_wanted() || !pum_enough_matches()) ! return; ! #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. ! compl_match_arraysize = 0; ! compl = compl_first_match; ! if (compl_leader != NULL) ! lead_len = (int)STRLEN(compl_leader); ! do { ! 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 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) shown_match_ok = TRUE; ! i = 0; ! compl = compl_first_match; ! do { ! 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; ! } ! 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) ! { ! did_find_shown_match = TRUE; ! // 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; ! 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 (!shown_match_ok) // no displayed match at all ! cur = -1; ! } ! } else { // popup menu already exists, only need to find the current item. --- 1063,1187 ---- int shown_match_ok = FALSE; int i; int cur = -1; int lead_len = 0; ! // 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))) ! ++compl_match_arraysize; ! compl = compl->cp_next; ! } while (compl != NULL && compl != compl_first_match); ! 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 { ! if (!ins_compl_at_original_text(compl) ! && (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; ! } ! ! 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) { ! 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; ! 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 (!shown_match_ok) // no displayed match at all ! cur = -1; ! return cur; ! } ! /* ! * 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 (!pum_wanted() || !pum_enough_matches()) ! return; ! #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,1170 **** } } ! 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; ! ! // 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); #endif - } } #define DICT_FIRST (1) // use just first element in "dict" --- 1195,1218 ---- } } ! 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; #ifdef FEAT_EVAL ! if (has_completechanged()) ! trigger_complete_changed_event(cur); #endif } #define DICT_FIRST (1) // use just first element in "dict" *************** *** 1324,1400 **** (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); } ! if (fp != NULL) { ! // 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 = 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) ! { ! // 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); } ! fclose(fp); } } } --- 1372,1447 ---- (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); } ! 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)) { ! 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) { ! 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); } } ! 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); } + fclose(fp); } } *************** *** 1757,1765 **** 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 { p = vim_strsave(str); if (p != NULL) --- 1804,1812 ---- 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 (ins_compl_at_original_text(compl_first_match)) // safety check { p = vim_strsave(str); if (p != NULL) *************** *** 1769,1775 **** } } else if (compl_first_match->cp_prev != NULL ! && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) { p = vim_strsave(str); if (p != NULL) --- 1816,1822 ---- } } else if (compl_first_match->cp_prev != NULL ! && ins_compl_at_original_text(compl_first_match->cp_prev)) { p = vim_strsave(str); if (p != NULL) *************** *** 1797,1820 **** { // When still at the original match use the first entry that matches // the leader. ! if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) ! { ! 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))) ! { ! p = cp->cp_str; ! break; ! } } - if (p == NULL || (int)STRLEN(p) <= len) - return; } ! else return; } p += len; --- 1844,1865 ---- { // When still at the original match use the first entry that matches // the leader. ! 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) ! { ! if (compl_leader == NULL ! || ins_compl_equal(cp, compl_leader, ! (int)STRLEN(compl_leader))) { ! p = cp->cp_str; ! break; } } ! if (p == NULL || (int)STRLEN(p) <= len) return; } p += len; *************** *** 2860,2866 **** match = compl_first_match; do { ! if (!(match->cp_flags & CP_ORIGINAL_TEXT)) { di = dict_alloc(); if (di == NULL) --- 2905,2911 ---- match = compl_first_match; do { ! if (!ins_compl_at_original_text(match)) { di = dict_alloc(); if (di == NULL) *************** *** 2949,3077 **** }; /* ! * Process the next 'complete' option value in "e_cpt_arg". * * If successful, the arguments are set as below: ! * e_cpt_arg - pointer to the next option value in 'e_cpt_arg' * 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 * * 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. */ static int process_next_cpt_value( ! char_u **e_cpt_arg, 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) { - 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++; ! if (*e_cpt == '.' && !curbuf->b_scanned) { ! buf = curbuf; ! *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) { // 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)); } ! *last_match_pos = *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; } ! else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL ! && (buf = ins_compl_next_buf(buf, *e_cpt)) != curbuf) { // Scan a buffer, but not the current one. ! if (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; compl_type = 0; } else // unloaded buffer, scan like dictionary { ! found_all = TRUE; ! if (buf->b_fname == NULL) { status = INS_COMPL_CPT_CONT; goto done; } compl_type = CTRL_X_DICTIONARY; ! dict = buf->b_fname; ! dict_f = DICT_EXACT; } msg_hist_off = TRUE; // reset in msg_trunc_attr() vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), ! buf->b_fname == NULL ! ? buf_spname(buf) ! : buf->b_sfname == NULL ! ? buf->b_fname ! : buf->b_sfname); (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); } ! else if (*e_cpt == NUL) status = INS_COMPL_CPT_END; else { if (ctrl_x_mode_line_or_eval()) compl_type = -1; ! else if (*e_cpt == 'k' || *e_cpt == 's') { ! if (*e_cpt == 'k') compl_type = CTRL_X_DICTIONARY; else compl_type = CTRL_X_THESAURUS; ! if (*++e_cpt != ',' && *e_cpt != NUL) { ! dict = e_cpt; ! dict_f = DICT_FIRST; } } #ifdef FEAT_FIND_ID ! else if (*e_cpt == 'i') compl_type = CTRL_X_PATH_PATTERNS; ! else if (*e_cpt == 'd') compl_type = CTRL_X_PATH_DEFINES; #endif ! else if (*e_cpt == ']' || *e_cpt == 't') { msg_hist_off = TRUE; // reset in msg_trunc_attr() compl_type = CTRL_X_TAGS; --- 2994,3130 ---- }; /* ! * 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: ! * st->cpt - pointer to the next option value in "st->cpt" * compl_type_arg - type of insert mode completion to use ! * 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 "st->e_cpt" are processed. */ static int process_next_cpt_value( ! ins_compl_next_state_T *st, int *compl_type_arg, ! pos_T *start_match_pos) { int compl_type = -1; int status = INS_COMPL_CPT_OK; ! st->found_all = FALSE; ! while (*st->e_cpt == ',' || *st->e_cpt == ' ') ! st->e_cpt++; ! ! if (*st->e_cpt == '.' && !curbuf->b_scanned) { ! 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(&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. ! 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)); } ! 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. ! st->set_match_pos = TRUE; } ! 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 (st->ins_buf->b_ml.ml_mfp != NULL) // loaded buffer { compl_started = TRUE; ! 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 { ! st->found_all = TRUE; ! if (st->ins_buf->b_fname == NULL) { status = INS_COMPL_CPT_CONT; goto done; } compl_type = CTRL_X_DICTIONARY; ! st->dict = st->ins_buf->b_fname; ! st->dict_f = DICT_EXACT; } msg_hist_off = TRUE; // reset in msg_trunc_attr() vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), ! st->ins_buf->b_fname == NULL ! ? buf_spname(st->ins_buf) ! : st->ins_buf->b_sfname == NULL ! ? st->ins_buf->b_fname ! : st->ins_buf->b_sfname); (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); } ! else if (*st->e_cpt == NUL) status = INS_COMPL_CPT_END; else { if (ctrl_x_mode_line_or_eval()) compl_type = -1; ! else if (*st->e_cpt == 'k' || *st->e_cpt == 's') { ! if (*st->e_cpt == 'k') compl_type = CTRL_X_DICTIONARY; else compl_type = CTRL_X_THESAURUS; ! if (*++st->e_cpt != ',' && *st->e_cpt != NUL) { ! st->dict = st->e_cpt; ! st->dict_f = DICT_FIRST; } } #ifdef FEAT_FIND_ID ! else if (*st->e_cpt == 'i') compl_type = CTRL_X_PATH_PATTERNS; ! else if (*st->e_cpt == 'd') compl_type = CTRL_X_PATH_DEFINES; #endif ! else if (*st->e_cpt == ']' || *st->e_cpt == 't') { msg_hist_off = TRUE; // reset in msg_trunc_attr() compl_type = CTRL_X_TAGS; *************** *** 3082,3101 **** compl_type = -1; // in any case e_cpt is advanced to the next entry ! (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ","); ! found_all = TRUE; if (compl_type == -1) status = INS_COMPL_CPT_CONT; } done: - *e_cpt_arg = e_cpt; *compl_type_arg = compl_type; - *found_all_arg = found_all; - *buf_arg = buf; - *dict_arg = dict; - *dict_flag = dict_f; return status; } --- 3135,3149 ---- compl_type = -1; // in any case e_cpt is advanced to the next entry ! (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ","); ! st->found_all = TRUE; if (compl_type == -1) status = INS_COMPL_CPT_CONT; } done: *compl_type_arg = compl_type; return status; } *************** *** 3121,3127 **** * thesaurus files. */ static void ! get_next_dict_tsr_completion(int compl_type, char_u **dict, int dict_f) { #ifdef FEAT_COMPL_FUNC if (thesaurus_func_complete(compl_type)) --- 3169,3175 ---- * thesaurus files. */ static void ! get_next_dict_tsr_completion(int compl_type, char_u *dict, int dict_f) { #ifdef FEAT_COMPL_FUNC if (thesaurus_func_complete(compl_type)) *************** *** 3129,3142 **** else #endif ins_compl_dictionaries( ! *dict != NULL ? *dict : (compl_type == CTRL_X_THESAURUS ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)), compl_pattern, ! *dict != NULL ? dict_f : 0, compl_type == CTRL_X_THESAURUS); - *dict = NULL; } /* --- 3177,3189 ---- else #endif ins_compl_dictionaries( ! dict != NULL ? dict : (compl_type == CTRL_X_THESAURUS ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)), compl_pattern, ! dict != NULL ? dict_f : 0, compl_type == CTRL_X_THESAURUS); } /* *************** *** 3335,3355 **** /* * Get the next set of words matching "compl_pattern" for default completion(s) * (normal ^P/^N and ^X^L). ! * Search for "compl_pattern" in the buffer "ins_buf" starting from the ! * position "start_pos" in the "compl_direction" direction. If "save_match_pos" ! * is TRUE, then set the "first_match_pos" and "last_match_pos". * Returns OK if a new next match is found, otherwise returns FAIL. */ static int ! get_next_default_completion( ! buf_T *ins_buf, // buffer being scanned ! pos_T *start_pos, // search start position ! pos_T *cur_match_pos, // current match position ! pos_T *prev_match_pos, // previous match position ! int *save_match_pos, // set first_match_pos/last_match_pos ! pos_T *first_match_pos, // first match position ! pos_T *last_match_pos, // last match position ! int scan_curbuf) // scan current buffer for completion { int found_new_match = FAIL; int save_p_scs; --- 3382,3395 ---- /* * Get the next set of words matching "compl_pattern" for default completion(s) * (normal ^P/^N and ^X^L). ! * Search for "compl_pattern" in the buffer "st->ins_buf" starting from the ! * position "st->start_pos" in the "compl_direction" direction. If ! * "st->set_match_pos" is TRUE, then set the "st->first_match_pos" and ! * "st->last_match_pos". * Returns OK if a new next match is found, otherwise returns FAIL. */ static int ! get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos) { int found_new_match = FAIL; int save_p_scs; *************** *** 3360,3366 **** // If 'infercase' is set, don't use 'smartcase' here save_p_scs = p_scs; ! if (ins_buf->b_p_inf) p_scs = FALSE; // Buffers other than curbuf are scanned from the beginning or the --- 3400,3406 ---- // If 'infercase' is set, don't use 'smartcase' here save_p_scs = p_scs; ! if (st->ins_buf->b_p_inf) p_scs = FALSE; // Buffers other than curbuf are scanned from the beginning or the *************** *** 3368,3376 **** // buffer is a good idea, on the other hand, we always set // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb save_p_ws = p_ws; ! if (ins_buf != curbuf) p_ws = FALSE; ! else if (scan_curbuf) p_ws = TRUE; looped_around = FALSE; for (;;) --- 3408,3416 ---- // buffer is a good idea, on the other hand, we always set // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb save_p_ws = p_ws; ! if (st->ins_buf != curbuf) p_ws = FALSE; ! else if (*st->e_cpt == '.') p_ws = TRUE; looped_around = FALSE; for (;;) *************** *** 3383,3413 **** // has added a word that was at the beginning of the line if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL)) ! found_new_match = search_for_exact_line(ins_buf, cur_match_pos, ! compl_direction, compl_pattern); else ! found_new_match = searchit(NULL, ins_buf, cur_match_pos, NULL, ! compl_direction, ! compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, ! RE_LAST, NULL); --msg_silent; ! if (!compl_started || *save_match_pos) { // set "compl_started" even on fail compl_started = TRUE; ! *first_match_pos = *cur_match_pos; ! *last_match_pos = *cur_match_pos; ! *save_match_pos = FALSE; } ! else if (first_match_pos->lnum == last_match_pos->lnum ! && first_match_pos->col == last_match_pos->col) { found_new_match = FAIL; } else if ((compl_direction == FORWARD) ! && (prev_match_pos->lnum > cur_match_pos->lnum ! || (prev_match_pos->lnum == cur_match_pos->lnum ! && prev_match_pos->col >= cur_match_pos->col))) { if (looped_around) found_new_match = FAIL; --- 3423,3452 ---- // has added a word that was at the beginning of the line if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL)) ! found_new_match = search_for_exact_line(st->ins_buf, ! st->cur_match_pos, compl_direction, compl_pattern); else ! found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos, ! NULL, compl_direction, compl_pattern, 1L, ! SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL); --msg_silent; ! if (!compl_started || st->set_match_pos) { // set "compl_started" even on fail compl_started = TRUE; ! st->first_match_pos = *st->cur_match_pos; ! st->last_match_pos = *st->cur_match_pos; ! st->set_match_pos = FALSE; } ! else if (st->first_match_pos.lnum == st->last_match_pos.lnum ! && st->first_match_pos.col == st->last_match_pos.col) { found_new_match = FAIL; } else if ((compl_direction == FORWARD) ! && (st->prev_match_pos.lnum > st->cur_match_pos->lnum ! || (st->prev_match_pos.lnum == st->cur_match_pos->lnum ! && st->prev_match_pos.col >= st->cur_match_pos->col))) { if (looped_around) found_new_match = FAIL; *************** *** 3415,3446 **** looped_around = TRUE; } else if ((compl_direction != FORWARD) ! && (prev_match_pos->lnum < cur_match_pos->lnum ! || (prev_match_pos->lnum == cur_match_pos->lnum ! && prev_match_pos->col <= cur_match_pos->col))) { if (looped_around) found_new_match = FAIL; else looped_around = TRUE; } ! *prev_match_pos = *cur_match_pos; if (found_new_match == FAIL) break; // when ADDING, the text before the cursor matches, skip it ! if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf ! && start_pos->lnum == cur_match_pos->lnum ! && start_pos->col == cur_match_pos->col) continue; ! ptr = ins_comp_get_next_word_or_line(ins_buf, cur_match_pos, &len, ! &cont_s_ipos); if (ptr == NULL) continue; if (ins_compl_add_infercase(ptr, len, p_ic, ! ins_buf == curbuf ? NULL : ins_buf->b_sfname, 0, cont_s_ipos) != NOTDONE) { found_new_match = OK; --- 3454,3485 ---- looped_around = TRUE; } else if ((compl_direction != FORWARD) ! && (st->prev_match_pos.lnum < st->cur_match_pos->lnum ! || (st->prev_match_pos.lnum == st->cur_match_pos->lnum ! && st->prev_match_pos.col <= st->cur_match_pos->col))) { if (looped_around) found_new_match = FAIL; else looped_around = TRUE; } ! st->prev_match_pos = *st->cur_match_pos; if (found_new_match == FAIL) break; // when ADDING, the text before the cursor matches, skip it ! if ((compl_cont_status & CONT_ADDING) && st->ins_buf == curbuf ! && start_pos->lnum == st->cur_match_pos->lnum ! && start_pos->col == st->cur_match_pos->col) continue; ! ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->cur_match_pos, ! &len, &cont_s_ipos); if (ptr == NULL) continue; if (ins_compl_add_infercase(ptr, len, p_ic, ! st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname, 0, cont_s_ipos) != NOTDONE) { found_new_match = OK; *************** *** 3454,3459 **** --- 3493,3562 ---- } /* + * get the next set of completion matches for 'type'. + * Returns TRUE if a new match is found. Otherwise returns FALSE. + */ + static int + get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini) + { + int found_new_match = FALSE; + + switch (type) + { + case -1: + break; + #ifdef FEAT_FIND_ID + case CTRL_X_PATH_PATTERNS: + case CTRL_X_PATH_DEFINES: + get_next_include_file_completion(type); + break; + #endif + + case CTRL_X_DICTIONARY: + case CTRL_X_THESAURUS: + get_next_dict_tsr_completion(type, st->dict, st->dict_f); + st->dict = NULL; + break; + + case CTRL_X_TAGS: + get_next_tag_completion(); + break; + + case CTRL_X_FILES: + get_next_filename_completion(); + break; + + case CTRL_X_CMDLINE: + case CTRL_X_CMDLINE_CTRL_X: + get_next_cmdline_completion(); + break; + + #ifdef FEAT_COMPL_FUNC + case CTRL_X_FUNCTION: + case CTRL_X_OMNI: + expand_by_function(type, compl_pattern); + break; + #endif + + case CTRL_X_SPELL: + get_next_spell_completion(st->first_match_pos.lnum); + break; + + default: // normal ^P/^N and ^X^L + found_new_match = get_next_default_completion(st, ini); + if (found_new_match == FAIL && st->ins_buf == curbuf) + st->found_all = TRUE; + } + + // check if compl_curr_match has changed, (e.g. other type of + // expansion added something) + if (type != 0 && compl_curr_match != compl_old_match) + found_new_match = OK; + + return found_new_match; + } + + /* * Get the next expansion(s), using "compl_pattern". * The search starts at position "ini" in curbuf and in the direction * compl_direction. *************** *** 3465,3518 **** static int ins_compl_get_exp(pos_T *ini) { ! static pos_T first_match_pos; ! static pos_T last_match_pos; ! static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' ! static int found_all = FALSE; // Found all matches of a ! // certain type. ! static buf_T *ins_buf = NULL; // buffer being scanned ! ! pos_T *pos; int i; int found_new_match; int type = ctrl_x_mode; - char_u *dict = NULL; - int dict_f = 0; - int set_match_pos; - pos_T prev_pos = {0, 0, 0}; if (!compl_started) { ! FOR_ALL_BUFFERS(ins_buf) ! ins_buf->b_scanned = 0; ! found_all = FALSE; ! ins_buf = curbuf; ! e_cpt = (compl_cont_status & CONT_LOCAL) ? (char_u *)"." : curbuf->b_p_cpt; ! last_match_pos = first_match_pos = *ini; } ! else if (ins_buf != curbuf && !buf_valid(ins_buf)) ! ins_buf = curbuf; // In case the buffer was wiped out. compl_old_match = compl_curr_match; // remember the last current match ! pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; // For ^N/^P loop over all the flags/windows/buffers in 'complete'. for (;;) { found_new_match = FAIL; ! set_match_pos = FALSE; // For ^N/^P pick a new entry from e_cpt if compl_started is off, // or if found_all says this entry is done. For ^X^L only use the // entries from 'complete' that look in loaded buffers. if ((ctrl_x_mode == CTRL_X_NORMAL || ctrl_x_mode_line_or_eval()) ! && (!compl_started || found_all)) { ! int status = process_next_cpt_value(&e_cpt, &type, &found_all, ! &ins_buf, ini, &first_match_pos, &last_match_pos, ! &set_match_pos, &dict, &dict_f); if (status == INS_COMPL_CPT_END) break; --- 3568,3609 ---- static int ins_compl_get_exp(pos_T *ini) { ! static ins_compl_next_state_T st; int i; int found_new_match; int type = ctrl_x_mode; if (!compl_started) { ! FOR_ALL_BUFFERS(st.ins_buf) ! st.ins_buf->b_scanned = 0; ! st.found_all = FALSE; ! st.ins_buf = curbuf; ! st.e_cpt = (compl_cont_status & CONT_LOCAL) ? (char_u *)"." : curbuf->b_p_cpt; ! st.last_match_pos = st.first_match_pos = *ini; } ! else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf)) ! st.ins_buf = curbuf; // In case the buffer was wiped out. compl_old_match = compl_curr_match; // remember the last current match ! st.cur_match_pos = (compl_direction == FORWARD) ! ? &st.last_match_pos : &st.first_match_pos; // For ^N/^P loop over all the flags/windows/buffers in 'complete'. for (;;) { found_new_match = FAIL; ! st.set_match_pos = FALSE; // For ^N/^P pick a new entry from e_cpt if compl_started is off, // or if found_all says this entry is done. For ^X^L only use the // entries from 'complete' that look in loaded buffers. if ((ctrl_x_mode == CTRL_X_NORMAL || ctrl_x_mode_line_or_eval()) ! && (!compl_started || st.found_all)) { ! int status = process_next_cpt_value(&st, &type, ini); if (status == INS_COMPL_CPT_END) break; *************** *** 3525,3582 **** if (compl_pattern == NULL) break; ! switch (type) ! { ! case -1: ! break; ! #ifdef FEAT_FIND_ID ! case CTRL_X_PATH_PATTERNS: ! case CTRL_X_PATH_DEFINES: ! get_next_include_file_completion(type); ! break; ! #endif ! ! case CTRL_X_DICTIONARY: ! case CTRL_X_THESAURUS: ! get_next_dict_tsr_completion(type, &dict, dict_f); ! break; ! ! case CTRL_X_TAGS: ! get_next_tag_completion(); ! break; ! ! case CTRL_X_FILES: ! get_next_filename_completion(); ! break; ! ! case CTRL_X_CMDLINE: ! case CTRL_X_CMDLINE_CTRL_X: ! get_next_cmdline_completion(); ! break; ! ! #ifdef FEAT_COMPL_FUNC ! case CTRL_X_FUNCTION: ! case CTRL_X_OMNI: ! expand_by_function(type, compl_pattern); ! break; ! #endif ! ! case CTRL_X_SPELL: ! get_next_spell_completion(first_match_pos.lnum); ! break; ! ! default: // normal ^P/^N and ^X^L ! found_new_match = get_next_default_completion(ins_buf, ini, pos, ! &prev_pos, &set_match_pos, &first_match_pos, ! &last_match_pos, (*e_cpt == '.')); ! if (found_new_match == FAIL && ins_buf == curbuf) ! found_all = TRUE; ! } ! ! // check if compl_curr_match has changed, (e.g. other type of ! // expansion added something) ! if (type != 0 && compl_curr_match != compl_old_match) ! found_new_match = OK; // break the loop for specialized modes (use 'complete' just for the // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new --- 3616,3623 ---- if (compl_pattern == NULL) break; ! // get the next set of completion matches ! found_new_match = get_next_completion_match(type, &st, ini); // break the loop for specialized modes (use 'complete' just for the // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new *************** *** 3599,3605 **** { // Mark a buffer scanned when it has been scanned completely if (type == 0 || type == CTRL_X_PATH_PATTERNS) ! ins_buf->b_scanned = TRUE; compl_started = FALSE; } --- 3640,3646 ---- { // Mark a buffer scanned when it has been scanned completely if (type == 0 || type == CTRL_X_PATH_PATTERNS) ! st.ins_buf->b_scanned = TRUE; compl_started = FALSE; } *************** *** 3607,3613 **** compl_started = TRUE; if ((ctrl_x_mode == CTRL_X_NORMAL || ctrl_x_mode_line_or_eval()) ! && *e_cpt == NUL) // Got to end of 'complete' found_new_match = FAIL; i = -1; // total of matches, unknown --- 3648,3654 ---- compl_started = TRUE; if ((ctrl_x_mode == CTRL_X_NORMAL || ctrl_x_mode_line_or_eval()) ! && *st.e_cpt == NUL) // Got to end of 'complete' found_new_match = FAIL; i = -1; // total of matches, unknown *************** *** 3699,3705 **** // illegal bytes. if (compl_len < (int)STRLEN(compl_shown_match->cp_str)) ins_bytes(compl_shown_match->cp_str + compl_len); ! if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) compl_used_match = FALSE; else compl_used_match = TRUE; --- 3740,3746 ---- // illegal bytes. if (compl_len < (int)STRLEN(compl_shown_match->cp_str)) ins_bytes(compl_shown_match->cp_str + compl_len); ! if (ins_compl_at_original_text(compl_shown_match)) compl_used_match = FALSE; else compl_used_match = TRUE; *************** *** 3832,3838 **** } found_end = FALSE; } ! if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0 && compl_leader != NULL && !ins_compl_equal(compl_shown_match, compl_leader, (int)STRLEN(compl_leader))) --- 3873,3879 ---- } found_end = FALSE; } ! if (!ins_compl_at_original_text(compl_shown_match) && compl_leader != NULL && !ins_compl_equal(compl_shown_match, compl_leader, (int)STRLEN(compl_leader))) *************** *** 3891,3897 **** return -1; if (compl_leader != NULL ! && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) // Update "compl_shown_match" to the actually shown match ins_compl_update_shown_match(); --- 3932,3938 ---- return -1; if (compl_leader != NULL ! && !ins_compl_at_original_text(compl_shown_match)) // Update "compl_shown_match" to the actually shown match ins_compl_update_shown_match(); *************** *** 4661,4667 **** if (edit_submode_extra == NULL) { ! if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) { edit_submode_extra = (char_u *)_("Back at original"); edit_submode_highl = HLF_W; --- 4702,4708 ---- if (edit_submode_extra == NULL) { ! if (ins_compl_at_original_text(compl_curr_match)) { edit_submode_extra = (char_u *)_("Back at original"); edit_submode_highl = HLF_W; *** ../vim-8.2.3952/src/version.c 2021-12-31 12:19:18.739789858 +0000 --- src/version.c 2021-12-31 12:58:08.704766015 +0000 *************** *** 751,752 **** --- 751,754 ---- { /* Add new patch number below this line */ + /**/ + 3953, /**/ -- hundred-and-one symptoms of being an internet addict: 168. You have your own domain name. /// 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 ///