To: vim_dev@googlegroups.com Subject: Patch 8.2.3944 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3944 Problem: Insert mode completion functions are too long. Solution: Split up into multiple functions. (Yegappan Lakshmanan, closes #9431) Files: src/insexpand.c, src/testdir/test_ins_complete.vim *** ../vim-8.2.3943/src/insexpand.c 2021-12-30 10:32:21.156298119 +0000 --- src/insexpand.c 2021-12-30 11:37:46.568928853 +0000 *************** *** 1823,1828 **** --- 1823,2079 ---- } /* + * Set the CTRL-X completion mode based on the key 'c' typed after a CTRL-X. + * Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre, + * compl_cont_mode and compl_cont_status. + * Returns TRUE when the character is not to be inserted. + */ + static int + set_ctrl_x_mode(int c) + { + int retval = FALSE; + + switch (c) + { + case Ctrl_E: + case Ctrl_Y: + // scroll the window one line up or down + ctrl_x_mode = CTRL_X_SCROLL; + if (!(State & REPLACE_FLAG)) + edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); + else + edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); + edit_submode_pre = NULL; + showmode(); + break; + case Ctrl_L: + // complete whole line + ctrl_x_mode = CTRL_X_WHOLE_LINE; + break; + case Ctrl_F: + // complete filenames + ctrl_x_mode = CTRL_X_FILES; + break; + case Ctrl_K: + // complete words from a dictinoary + ctrl_x_mode = CTRL_X_DICTIONARY; + break; + case Ctrl_R: + // Register insertion without exiting CTRL-X mode + // Simply allow ^R to happen without affecting ^X mode + break; + case Ctrl_T: + // complete words from a thesaurus + ctrl_x_mode = CTRL_X_THESAURUS; + break; + #ifdef FEAT_COMPL_FUNC + case Ctrl_U: + // user defined completion + ctrl_x_mode = CTRL_X_FUNCTION; + break; + case Ctrl_O: + // omni completion + ctrl_x_mode = CTRL_X_OMNI; + break; + #endif + case 's': + case Ctrl_S: + // complete spelling suggestions + ctrl_x_mode = CTRL_X_SPELL; + #ifdef FEAT_SPELL + ++emsg_off; // Avoid getting the E756 error twice. + spell_back_to_badword(); + --emsg_off; + #endif + break; + case Ctrl_RSB: + // complete tag names + ctrl_x_mode = CTRL_X_TAGS; + break; + #ifdef FEAT_FIND_ID + case Ctrl_I: + case K_S_TAB: + // complete keywords from included files + ctrl_x_mode = CTRL_X_PATH_PATTERNS; + break; + case Ctrl_D: + // complete definitions from included files + ctrl_x_mode = CTRL_X_PATH_DEFINES; + break; + #endif + case Ctrl_V: + case Ctrl_Q: + // complete vim commands + ctrl_x_mode = CTRL_X_CMDLINE; + break; + case Ctrl_Z: + // stop completion + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + showmode(); + retval = TRUE; + break; + case Ctrl_P: + case Ctrl_N: + // ^X^P means LOCAL expansion if nothing interrupted (eg we + // just started ^X mode, or there were enough ^X's to cancel + // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) + // do normal expansion when interrupting a different mode (say + // ^X^F^X^P or ^P^X^X^P, see below) + // nothing changes if interrupting mode 0, (eg, the flag + // doesn't change when going to ADDING mode -- Acevedo + if (!(compl_cont_status & CONT_INTRPT)) + compl_cont_status |= CONT_LOCAL; + else if (compl_cont_mode != 0) + compl_cont_status &= ~CONT_LOCAL; + // FALLTHROUGH + default: + // If we have typed at least 2 ^X's... for modes != 0, we set + // compl_cont_status = 0 (eg, as if we had just started ^X + // mode). + // For mode 0, we set "compl_cont_mode" to an impossible + // value, in both cases ^X^X can be used to restart the same + // mode (avoiding ADDING mode). + // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start + // 'complete' and local ^P expansions respectively. + // In mode 0 an extra ^X is needed since ^X^P goes to ADDING + // mode -- Acevedo + if (c == Ctrl_X) + { + if (compl_cont_mode != 0) + compl_cont_status = 0; + else + compl_cont_mode = CTRL_X_NOT_DEFINED_YET; + } + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + showmode(); + break; + } + + return retval; + } + + /* + * Stop insert completion mode + */ + static int + ins_compl_stop(int c, int prev_mode, int retval) + { + char_u *ptr; + #ifdef FEAT_CINDENT + int want_cindent; + #endif + + // Get here when we have finished typing a sequence of ^N and + // ^P or other completion characters in CTRL-X mode. Free up + // memory that was used, and make sure we can redo the insert. + if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) + { + // If any of the original typed text has been changed, eg when + // ignorecase is set, we must add back-spaces to the redo + // buffer. We add as few as necessary to delete just the part + // of the original text that has changed. + // When using the longest match, edited the match or used + // CTRL-E then don't use the current match. + if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) + ptr = compl_curr_match->cp_str; + else + ptr = NULL; + ins_compl_fixRedoBufForLeader(ptr); + } + + #ifdef FEAT_CINDENT + want_cindent = (get_can_cindent() && cindent_on()); + #endif + // When completing whole lines: fix indent for 'cindent'. + // Otherwise, break line if it's too long. + if (compl_cont_mode == CTRL_X_WHOLE_LINE) + { + #ifdef FEAT_CINDENT + // re-indent the current line + if (want_cindent) + { + do_c_expr_indent(); + want_cindent = FALSE; // don't do it again + } + #endif + } + else + { + int prev_col = curwin->w_cursor.col; + + // put the cursor on the last char, for 'tw' formatting + if (prev_col > 0) + dec_cursor(); + // only format when something was inserted + if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) + insertchar(NUL, 0, -1); + if (prev_col > 0 + && ml_get_curline()[curwin->w_cursor.col] != NUL) + inc_cursor(); + } + + // If the popup menu is displayed pressing CTRL-Y means accepting + // the selection without inserting anything. When + // compl_enter_selects is set the Enter key does the same. + if ((c == Ctrl_Y || (compl_enter_selects + && (c == CAR || c == K_KENTER || c == NL))) + && pum_visible()) + retval = TRUE; + + // CTRL-E means completion is Ended, go back to the typed text. + // but only do this, if the Popup is still visible + if (c == Ctrl_E) + { + ins_compl_delete(); + if (compl_leader != NULL) + ins_bytes(compl_leader + ins_compl_len()); + else if (compl_first_match != NULL) + ins_bytes(compl_orig_text + ins_compl_len()); + retval = TRUE; + } + + auto_format(FALSE, TRUE); + + // Trigger the CompleteDonePre event to give scripts a chance to + // act upon the completion before clearing the info, and restore + // ctrl_x_mode, so that complete_info() can be used. + ctrl_x_mode = prev_mode; + ins_apply_autocmds(EVENT_COMPLETEDONEPRE); + + ins_compl_free(); + compl_started = FALSE; + compl_matches = 0; + if (!shortmess(SHM_COMPLETIONMENU)) + msg_clr_cmdline(); // necessary for "noshowmode" + ctrl_x_mode = CTRL_X_NORMAL; + compl_enter_selects = FALSE; + if (edit_submode != NULL) + { + edit_submode = NULL; + showmode(); + } + + #ifdef FEAT_CMDWIN + if (c == Ctrl_C && cmdwin_type != 0) + // Avoid the popup menu remains displayed when leaving the + // command line window. + update_screen(0); + #endif + #ifdef FEAT_CINDENT + // Indent now if a key was typed that is in 'cinkeys'. + if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) + do_c_expr_indent(); + #endif + // Trigger the CompleteDone event to give scripts a chance to act + // upon the end of completion. + ins_apply_autocmds(EVENT_COMPLETEDONE); + + return retval; + } + + /* * Prepare for Insert mode completion, or stop it. * Called just after typing a character in Insert mode. * Returns TRUE when the character is not to be inserted; *************** *** 1830,1839 **** int ins_compl_prep(int c) { - char_u *ptr; - #ifdef FEAT_CINDENT - int want_cindent; - #endif int retval = FALSE; int prev_mode = ctrl_x_mode; --- 2081,2086 ---- *************** *** 1910,2022 **** } if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) - { // We have just typed CTRL-X and aren't quite sure which CTRL-X mode // it will be yet. Now we decide. ! switch (c) ! { ! case Ctrl_E: ! case Ctrl_Y: ! ctrl_x_mode = CTRL_X_SCROLL; ! if (!(State & REPLACE_FLAG)) ! edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); ! else ! edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); ! edit_submode_pre = NULL; ! showmode(); ! break; ! case Ctrl_L: ! ctrl_x_mode = CTRL_X_WHOLE_LINE; ! break; ! case Ctrl_F: ! ctrl_x_mode = CTRL_X_FILES; ! break; ! case Ctrl_K: ! ctrl_x_mode = CTRL_X_DICTIONARY; ! break; ! case Ctrl_R: ! // Simply allow ^R to happen without affecting ^X mode ! break; ! case Ctrl_T: ! ctrl_x_mode = CTRL_X_THESAURUS; ! break; ! #ifdef FEAT_COMPL_FUNC ! case Ctrl_U: ! ctrl_x_mode = CTRL_X_FUNCTION; ! break; ! case Ctrl_O: ! ctrl_x_mode = CTRL_X_OMNI; ! break; ! #endif ! case 's': ! case Ctrl_S: ! ctrl_x_mode = CTRL_X_SPELL; ! #ifdef FEAT_SPELL ! ++emsg_off; // Avoid getting the E756 error twice. ! spell_back_to_badword(); ! --emsg_off; ! #endif ! break; ! case Ctrl_RSB: ! ctrl_x_mode = CTRL_X_TAGS; ! break; ! #ifdef FEAT_FIND_ID ! case Ctrl_I: ! case K_S_TAB: ! ctrl_x_mode = CTRL_X_PATH_PATTERNS; ! break; ! case Ctrl_D: ! ctrl_x_mode = CTRL_X_PATH_DEFINES; ! break; ! #endif ! case Ctrl_V: ! case Ctrl_Q: ! ctrl_x_mode = CTRL_X_CMDLINE; ! break; ! case Ctrl_Z: ! ctrl_x_mode = CTRL_X_NORMAL; ! edit_submode = NULL; ! showmode(); ! retval = TRUE; ! break; ! case Ctrl_P: ! case Ctrl_N: ! // ^X^P means LOCAL expansion if nothing interrupted (eg we ! // just started ^X mode, or there were enough ^X's to cancel ! // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) ! // do normal expansion when interrupting a different mode (say ! // ^X^F^X^P or ^P^X^X^P, see below) ! // nothing changes if interrupting mode 0, (eg, the flag ! // doesn't change when going to ADDING mode -- Acevedo ! if (!(compl_cont_status & CONT_INTRPT)) ! compl_cont_status |= CONT_LOCAL; ! else if (compl_cont_mode != 0) ! compl_cont_status &= ~CONT_LOCAL; ! // FALLTHROUGH ! default: ! // If we have typed at least 2 ^X's... for modes != 0, we set ! // compl_cont_status = 0 (eg, as if we had just started ^X ! // mode). ! // For mode 0, we set "compl_cont_mode" to an impossible ! // value, in both cases ^X^X can be used to restart the same ! // mode (avoiding ADDING mode). ! // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start ! // 'complete' and local ^P expansions respectively. ! // In mode 0 an extra ^X is needed since ^X^P goes to ADDING ! // mode -- Acevedo ! if (c == Ctrl_X) ! { ! if (compl_cont_mode != 0) ! compl_cont_status = 0; ! else ! compl_cont_mode = CTRL_X_NOT_DEFINED_YET; ! } ! ctrl_x_mode = CTRL_X_NORMAL; ! edit_submode = NULL; ! showmode(); ! break; ! } ! } else if (ctrl_x_mode != CTRL_X_NORMAL) { // We're already in CTRL-X mode, do we stay in it? --- 2157,2165 ---- } if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) // We have just typed CTRL-X and aren't quite sure which CTRL-X mode // it will be yet. Now we decide. ! retval = set_ctrl_x_mode(c); else if (ctrl_x_mode != CTRL_X_NORMAL) { // We're already in CTRL-X mode, do we stay in it? *************** *** 2040,2151 **** if ((ctrl_x_mode == CTRL_X_NORMAL && c != Ctrl_N && c != Ctrl_P && c != Ctrl_R && !ins_compl_pum_key(c)) || ctrl_x_mode == CTRL_X_FINISHED) ! { ! // Get here when we have finished typing a sequence of ^N and ! // ^P or other completion characters in CTRL-X mode. Free up ! // memory that was used, and make sure we can redo the insert. ! if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) ! { ! // If any of the original typed text has been changed, eg when ! // ignorecase is set, we must add back-spaces to the redo ! // buffer. We add as few as necessary to delete just the part ! // of the original text that has changed. ! // When using the longest match, edited the match or used ! // CTRL-E then don't use the current match. ! if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) ! ptr = compl_curr_match->cp_str; ! else ! ptr = NULL; ! ins_compl_fixRedoBufForLeader(ptr); ! } ! ! #ifdef FEAT_CINDENT ! want_cindent = (get_can_cindent() && cindent_on()); ! #endif ! // When completing whole lines: fix indent for 'cindent'. ! // Otherwise, break line if it's too long. ! if (compl_cont_mode == CTRL_X_WHOLE_LINE) ! { ! #ifdef FEAT_CINDENT ! // re-indent the current line ! if (want_cindent) ! { ! do_c_expr_indent(); ! want_cindent = FALSE; // don't do it again ! } ! #endif ! } ! else ! { ! int prev_col = curwin->w_cursor.col; ! ! // put the cursor on the last char, for 'tw' formatting ! if (prev_col > 0) ! dec_cursor(); ! // only format when something was inserted ! if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) ! insertchar(NUL, 0, -1); ! if (prev_col > 0 ! && ml_get_curline()[curwin->w_cursor.col] != NUL) ! inc_cursor(); ! } ! ! // If the popup menu is displayed pressing CTRL-Y means accepting ! // the selection without inserting anything. When ! // compl_enter_selects is set the Enter key does the same. ! if ((c == Ctrl_Y || (compl_enter_selects ! && (c == CAR || c == K_KENTER || c == NL))) ! && pum_visible()) ! retval = TRUE; ! ! // CTRL-E means completion is Ended, go back to the typed text. ! // but only do this, if the Popup is still visible ! if (c == Ctrl_E) ! { ! ins_compl_delete(); ! if (compl_leader != NULL) ! ins_bytes(compl_leader + ins_compl_len()); ! else if (compl_first_match != NULL) ! ins_bytes(compl_orig_text + ins_compl_len()); ! retval = TRUE; ! } ! ! auto_format(FALSE, TRUE); ! ! // Trigger the CompleteDonePre event to give scripts a chance to ! // act upon the completion before clearing the info, and restore ! // ctrl_x_mode, so that complete_info() can be used. ! ctrl_x_mode = prev_mode; ! ins_apply_autocmds(EVENT_COMPLETEDONEPRE); ! ! ins_compl_free(); ! compl_started = FALSE; ! compl_matches = 0; ! if (!shortmess(SHM_COMPLETIONMENU)) ! msg_clr_cmdline(); // necessary for "noshowmode" ! ctrl_x_mode = CTRL_X_NORMAL; ! compl_enter_selects = FALSE; ! if (edit_submode != NULL) ! { ! edit_submode = NULL; ! showmode(); ! } ! ! #ifdef FEAT_CMDWIN ! if (c == Ctrl_C && cmdwin_type != 0) ! // Avoid the popup menu remains displayed when leaving the ! // command line window. ! update_screen(0); ! #endif ! #ifdef FEAT_CINDENT ! // Indent now if a key was typed that is in 'cinkeys'. ! if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) ! do_c_expr_indent(); ! #endif ! // Trigger the CompleteDone event to give scripts a chance to act ! // upon the end of completion. ! ins_apply_autocmds(EVENT_COMPLETEDONE); ! } } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) // Trigger the CompleteDone event to give scripts a chance to act --- 2183,2189 ---- if ((ctrl_x_mode == CTRL_X_NORMAL && c != Ctrl_N && c != Ctrl_P && c != Ctrl_R && !ins_compl_pum_key(c)) || ctrl_x_mode == CTRL_X_FINISHED) ! retval = ins_compl_stop(c, prev_mode, retval); } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) // Trigger the CompleteDone event to give scripts a chance to act *************** *** 3199,3204 **** --- 3237,3338 ---- } /* + * Return the next word or line from buffer "ins_buf" at position + * "cur_match_pos" for completion. The length of the match is set in "len". + */ + static char_u * + ins_comp_get_next_word_or_line( + buf_T *ins_buf, // buffer being scanned + pos_T *cur_match_pos, // current match position + int *match_len, + int *cont_s_ipos) // next ^X<> will set initial_pos + { + char_u *ptr; + int len; + + *match_len = 0; + ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) + + cur_match_pos->col; + if (ctrl_x_mode_line_or_eval()) + { + if (compl_cont_status & CONT_ADDING) + { + if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) + return NULL; + ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE); + if (!p_paste) + ptr = skipwhite(ptr); + } + len = (int)STRLEN(ptr); + } + else + { + char_u *tmp_ptr = ptr; + + if (compl_cont_status & CONT_ADDING) + { + tmp_ptr += compl_length; + // Skip if already inside a word. + if (vim_iswordp(tmp_ptr)) + return NULL; + // Find start of next word. + tmp_ptr = find_word_start(tmp_ptr); + } + // Find end of this word. + tmp_ptr = find_word_end(tmp_ptr); + len = (int)(tmp_ptr - ptr); + + if ((compl_cont_status & CONT_ADDING) && len == compl_length) + { + if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count) + { + // Try next line, if any. the new word will be + // "join" as if the normal command "J" was used. + // IOSIZE is always greater than + // compl_length, so the next STRNCPY always + // works -- Acevedo + STRNCPY(IObuff, ptr, len); + ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE); + tmp_ptr = ptr = skipwhite(ptr); + // Find start of next word. + tmp_ptr = find_word_start(tmp_ptr); + // Find end of next word. + tmp_ptr = find_word_end(tmp_ptr); + if (tmp_ptr > ptr) + { + if (*ptr != ')' && IObuff[len - 1] != TAB) + { + if (IObuff[len - 1] != ' ') + IObuff[len++] = ' '; + // IObuf =~ "\k.* ", thus len >= 2 + if (p_js + && (IObuff[len - 2] == '.' + || (vim_strchr(p_cpo, CPO_JOINSP) + == NULL + && (IObuff[len - 2] == '?' + || IObuff[len - 2] == '!')))) + IObuff[len++] = ' '; + } + // copy as much as possible of the new word + if (tmp_ptr - ptr >= IOSIZE - len) + tmp_ptr = ptr + IOSIZE - len - 1; + STRNCPY(IObuff + len, ptr, tmp_ptr - ptr); + len += (int)(tmp_ptr - ptr); + *cont_s_ipos = TRUE; + } + IObuff[len] = NUL; + ptr = IObuff; + } + if (len == compl_length) + return NULL; + } + } + + *match_len = len; + return ptr; + } + + /* * 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 *************** *** 3299,3380 **** && start_pos->lnum == cur_match_pos->lnum && start_pos->col == cur_match_pos->col) continue; - ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) + - cur_match_pos->col; - if (ctrl_x_mode_line_or_eval()) - { - if (compl_cont_status & CONT_ADDING) - { - if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) - continue; - ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE); - if (!p_paste) - ptr = skipwhite(ptr); - } - len = (int)STRLEN(ptr); - } - else - { - char_u *tmp_ptr = ptr; ! if (compl_cont_status & CONT_ADDING) ! { ! tmp_ptr += compl_length; ! // Skip if already inside a word. ! if (vim_iswordp(tmp_ptr)) ! continue; ! // Find start of next word. ! tmp_ptr = find_word_start(tmp_ptr); ! } ! // Find end of this word. ! tmp_ptr = find_word_end(tmp_ptr); ! len = (int)(tmp_ptr - ptr); - if ((compl_cont_status & CONT_ADDING) && len == compl_length) - { - if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count) - { - // Try next line, if any. the new word will be - // "join" as if the normal command "J" was used. - // IOSIZE is always greater than - // compl_length, so the next STRNCPY always - // works -- Acevedo - STRNCPY(IObuff, ptr, len); - ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE); - tmp_ptr = ptr = skipwhite(ptr); - // Find start of next word. - tmp_ptr = find_word_start(tmp_ptr); - // Find end of next word. - tmp_ptr = find_word_end(tmp_ptr); - if (tmp_ptr > ptr) - { - if (*ptr != ')' && IObuff[len - 1] != TAB) - { - if (IObuff[len - 1] != ' ') - IObuff[len++] = ' '; - // IObuf =~ "\k.* ", thus len >= 2 - if (p_js - && (IObuff[len - 2] == '.' - || (vim_strchr(p_cpo, CPO_JOINSP) - == NULL - && (IObuff[len - 2] == '?' - || IObuff[len - 2] == '!')))) - IObuff[len++] = ' '; - } - // copy as much as possible of the new word - if (tmp_ptr - ptr >= IOSIZE - len) - tmp_ptr = ptr + IOSIZE - len - 1; - STRNCPY(IObuff + len, ptr, tmp_ptr - ptr); - len += (int)(tmp_ptr - ptr); - cont_s_ipos = TRUE; - } - IObuff[len] = NUL; - ptr = IObuff; - } - if (len == compl_length) - continue; - } - } if (ins_compl_add_infercase(ptr, len, p_ic, ins_buf == curbuf ? NULL : ins_buf->b_sfname, 0, cont_s_ipos) != NOTDONE) --- 3433,3444 ---- && 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) *************** *** 3567,3572 **** --- 3631,3665 ---- } /* + * Update "compl_shown_match" to the actually shown match, it may differ when + * "compl_leader" is used to omit some of the matches. + */ + static void + ins_compl_update_shown_match(void) + { + while (!ins_compl_equal(compl_shown_match, + compl_leader, (int)STRLEN(compl_leader)) + && compl_shown_match->cp_next != NULL + && compl_shown_match->cp_next != compl_first_match) + compl_shown_match = compl_shown_match->cp_next; + + // If we didn't find it searching forward, and compl_shows_dir is + // backward, find the last match. + if (compl_shows_dir == BACKWARD + && !ins_compl_equal(compl_shown_match, + compl_leader, (int)STRLEN(compl_leader)) + && (compl_shown_match->cp_next == NULL + || compl_shown_match->cp_next == compl_first_match)) + { + while (!ins_compl_equal(compl_shown_match, + compl_leader, (int)STRLEN(compl_leader)) + && compl_shown_match->cp_prev != NULL + && compl_shown_match->cp_prev != compl_first_match) + compl_shown_match = compl_shown_match->cp_prev; + } + } + + /* * Delete the old text being completed. */ void *************** *** 3622,3718 **** } /* ! * Fill in the next completion in the current direction. ! * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to ! * get more completions. If it is FALSE, then we just do nothing when there ! * are no more completions in a given direction. The latter case is used when ! * we are still in the middle of finding completions, to allow browsing ! * through the ones found so far. ! * Return the total number of matches, or -1 if still unknown -- webb. ! * ! * compl_curr_match is currently being used by ins_compl_get_exp(), so we use ! * compl_shown_match here. ! * ! * Note that this function may be called recursively once only. First with ! * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn ! * calls this function with "allow_get_expansion" FALSE. */ ! static int ! ins_compl_next( ! int allow_get_expansion, ! int count, // repeat completion this many times; should ! // be at least 1 ! int insert_match, // Insert the newly selected match ! int in_compl_func) // called from complete_check() { ! int num_matches = -1; ! int todo = count; ! compl_T *found_compl = NULL; ! int found_end = FALSE; ! int advance; ! int started = compl_started; ! // When user complete function return -1 for findstart which is next ! // time of 'always', compl_shown_match become NULL. ! if (compl_shown_match == NULL) ! return -1; ! if (compl_leader != NULL ! && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) { ! // Set "compl_shown_match" to the actually shown match, it may differ ! // when "compl_leader" is used to omit some of the matches. ! while (!ins_compl_equal(compl_shown_match, ! compl_leader, (int)STRLEN(compl_leader)) ! && compl_shown_match->cp_next != NULL ! && compl_shown_match->cp_next != compl_first_match) ! compl_shown_match = compl_shown_match->cp_next; ! ! // If we didn't find it searching forward, and compl_shows_dir is ! // backward, find the last match. ! if (compl_shows_dir == BACKWARD ! && !ins_compl_equal(compl_shown_match, ! compl_leader, (int)STRLEN(compl_leader)) ! && (compl_shown_match->cp_next == NULL ! || compl_shown_match->cp_next == compl_first_match)) ! { ! while (!ins_compl_equal(compl_shown_match, ! compl_leader, (int)STRLEN(compl_leader)) ! && compl_shown_match->cp_prev != NULL ! && compl_shown_match->cp_prev != compl_first_match) ! compl_shown_match = compl_shown_match->cp_prev; } } ! if (allow_get_expansion && insert_match ! && (!(compl_get_longest || compl_restarting) || compl_used_match)) ! // Delete old text to be replaced ! ins_compl_delete(); ! ! // When finding the longest common text we stick at the original text, ! // don't let CTRL-N or CTRL-P move to the first match. ! advance = count != 1 || !allow_get_expansion || !compl_get_longest; ! ! // When restarting the search don't insert the first match either. ! if (compl_restarting) ! { ! advance = FALSE; ! compl_restarting = FALSE; ! } - // Repeat this for when or is typed. But don't wrap - // around. while (--todo >= 0) { if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) { compl_shown_match = compl_shown_match->cp_next; found_end = (compl_first_match != NULL ! && (compl_shown_match->cp_next == compl_first_match ! || compl_shown_match == compl_first_match)); } else if (compl_shows_dir == BACKWARD ! && compl_shown_match->cp_prev != NULL) { found_end = (compl_shown_match == compl_first_match); compl_shown_match = compl_shown_match->cp_prev; --- 3715,3788 ---- } /* ! * show the file name for the completion match (if any). Truncate the file ! * name to avoid a wait for return. */ ! static void ! ins_compl_show_filename(void) { ! char *lead = _("match in file"); ! int space = sc_col - vim_strsize((char_u *)lead) - 2; ! char_u *s; ! char_u *e; ! if (space <= 0) ! return; ! // We need the tail that fits. With double-byte encoding going ! // back from the end is very slow, thus go from the start and keep ! // the text that fits in "space" between "s" and "e". ! for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) { ! space -= ptr2cells(e); ! while (space < 0) ! { ! space += ptr2cells(s); ! MB_PTR_ADV(s); } } + msg_hist_off = TRUE; + vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, + s > compl_shown_match->cp_fname ? "<" : "", s); + msg((char *)IObuff); + msg_hist_off = FALSE; + redraw_cmdline = FALSE; // don't overwrite! + } ! /* ! * Find the next set of matches for completion. Repeat the completion 'todo' ! * times. The number of matches found is returned in 'num_matches'. ! * ! * If "allow_get_expansion" is TRUE, then ins_compl_get_exp() may be called to ! * get more completions. If it is FALSE, then do nothing when there are no more ! * completions in the given direction. ! * ! * If "advance" is TRUE, then completion will move to the first match. ! * Otherwise, the original text will be shown. ! * ! * Returns OK on success and -1 if the number of matches are unknown. ! */ ! static int ! find_next_completion_match( ! int allow_get_expansion, ! int todo, // repeat completion this many times ! int advance, ! int *num_matches) ! { ! int found_end = FALSE; ! compl_T *found_compl = NULL; while (--todo >= 0) { if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) { compl_shown_match = compl_shown_match->cp_next; found_end = (compl_first_match != NULL ! && (compl_shown_match->cp_next == compl_first_match ! || compl_shown_match == compl_first_match)); } else if (compl_shows_dir == BACKWARD ! && compl_shown_match->cp_prev != NULL) { found_end = (compl_shown_match == compl_first_match); compl_shown_match = compl_shown_match->cp_prev; *************** *** 3741,3751 **** } // Find matches. ! num_matches = ins_compl_get_exp(&compl_startpos); // handle any pending completions while (compl_pending != 0 && compl_direction == compl_shows_dir ! && advance) { if (compl_pending > 0 && compl_shown_match->cp_next != NULL) { --- 3811,3821 ---- } // Find matches. ! *num_matches = ins_compl_get_exp(&compl_startpos); // handle any pending completions while (compl_pending != 0 && compl_direction == compl_shows_dir ! && advance) { if (compl_pending > 0 && compl_shown_match->cp_next != NULL) { *************** *** 3765,3771 **** 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))) ++todo; else // Remember a matching item. --- 3835,3841 ---- 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))) ++todo; else // Remember a matching item. *************** *** 3783,3788 **** --- 3853,3922 ---- } } + return OK; + } + + /* + * Fill in the next completion in the current direction. + * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to + * get more completions. If it is FALSE, then we just do nothing when there + * are no more completions in a given direction. The latter case is used when + * we are still in the middle of finding completions, to allow browsing + * through the ones found so far. + * Return the total number of matches, or -1 if still unknown -- webb. + * + * compl_curr_match is currently being used by ins_compl_get_exp(), so we use + * compl_shown_match here. + * + * Note that this function may be called recursively once only. First with + * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn + * calls this function with "allow_get_expansion" FALSE. + */ + static int + ins_compl_next( + int allow_get_expansion, + int count, // repeat completion this many times; should + // be at least 1 + int insert_match, // Insert the newly selected match + int in_compl_func) // called from complete_check() + { + int num_matches = -1; + int todo = count; + int advance; + int started = compl_started; + + // When user complete function return -1 for findstart which is next + // time of 'always', compl_shown_match become NULL. + if (compl_shown_match == NULL) + 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(); + + if (allow_get_expansion && insert_match + && (!(compl_get_longest || compl_restarting) || compl_used_match)) + // Delete old text to be replaced + ins_compl_delete(); + + // When finding the longest common text we stick at the original text, + // don't let CTRL-N or CTRL-P move to the first match. + advance = count != 1 || !allow_get_expansion || !compl_get_longest; + + // When restarting the search don't insert the first match either. + if (compl_restarting) + { + advance = FALSE; + compl_restarting = FALSE; + } + + // Repeat this for when or is typed. But don't wrap + // around. + if (find_next_completion_match(allow_get_expansion, todo, advance, + &num_matches) == -1) + return -1; + // Insert the text of the new completion, or the compl_leader. if (compl_no_insert && !started) { *************** *** 3836,3871 **** compl_enter_selects = !insert_match && compl_match_array != NULL; // Show the file name for the match (if any) - // Truncate the file name to avoid a wait for return. if (compl_shown_match->cp_fname != NULL) ! { ! char *lead = _("match in file"); ! int space = sc_col - vim_strsize((char_u *)lead) - 2; ! char_u *s; ! char_u *e; ! ! if (space > 0) ! { ! // We need the tail that fits. With double-byte encoding going ! // back from the end is very slow, thus go from the start and keep ! // the text that fits in "space" between "s" and "e". ! for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) ! { ! space -= ptr2cells(e); ! while (space < 0) ! { ! space += ptr2cells(s); ! MB_PTR_ADV(s); ! } ! } ! msg_hist_off = TRUE; ! vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, ! s > compl_shown_match->cp_fname ? "<" : "", s); ! msg((char *)IObuff); ! msg_hist_off = FALSE; ! redraw_cmdline = FALSE; // don't overwrite! ! } ! } return num_matches; } --- 3970,3977 ---- compl_enter_selects = !insert_match && compl_match_array != NULL; // Show the file name for the match (if any) if (compl_shown_match->cp_fname != NULL) ! ins_compl_show_filename(); return num_matches; } *************** *** 4357,4569 **** } /* ! * 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). */ ! int ! ins_complete(int c, int enable_pum) { char_u *line; int startcol = 0; // column where searched text starts colnr_T curs_col; // cursor column ! int n; ! 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); ! ! if (!compl_started) ! { ! // First time we hit ^N or ^P (in a row, I mean) ! did_ai = FALSE; #ifdef FEAT_SMARTINDENT ! did_si = FALSE; ! can_si = FALSE; ! can_si_back = FALSE; #endif ! if (stop_arrow() == FAIL) ! return FAIL; ! line = ml_get(curwin->w_cursor.lnum); ! curs_col = curwin->w_cursor.col; ! compl_pending = 0; ! // If this same ctrl_x_mode has been interrupted use the text from ! // "compl_startpos" to the cursor as a pattern to add a new word ! // instead of expand the one before the cursor, in word-wise if ! // "compl_startpos" is not in the same line as the cursor then fix it ! // (the line has been split because it was longer than 'tw'). if SOL ! // is set then skip the previous pattern, a word at the beginning of ! // the line has been inserted, we'll look for that -- Acevedo. ! if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT ! && compl_cont_mode == ctrl_x_mode) ! { ! // it is a continued search ! compl_cont_status &= ~CONT_INTRPT; // remove INTRPT ! if (ctrl_x_mode == CTRL_X_NORMAL ! || ctrl_x_mode == CTRL_X_PATH_PATTERNS ! || ctrl_x_mode == CTRL_X_PATH_DEFINES) ! { ! if (compl_startpos.lnum != curwin->w_cursor.lnum) ! { ! // line (probably) wrapped, set compl_startpos to the ! // first non_blank in the line, if it is not a wordchar ! // include it to get a better pattern, but then we don't ! // want the "\\<" prefix, check it below ! compl_col = (colnr_T)getwhitecols(line); ! compl_startpos.col = compl_col; ! compl_startpos.lnum = curwin->w_cursor.lnum; ! compl_cont_status &= ~CONT_SOL; // clear SOL if present ! } ! else ! { ! // S_IPOS was set when we inserted a word that was at the ! // beginning of the line, which means that we'll go to SOL ! // mode but first we need to redefine compl_startpos ! if (compl_cont_status & CONT_S_IPOS) ! { ! compl_cont_status |= CONT_SOL; ! compl_startpos.col = (colnr_T)(skipwhite( ! line + compl_length ! + compl_startpos.col) - line); ! } ! compl_col = compl_startpos.col; ! } ! compl_length = curwin->w_cursor.col - (int)compl_col; ! // IObuff is used to add a "word from the next line" would we ! // have enough space? just being paranoid ! #define MIN_SPACE 75 ! if (compl_length > (IOSIZE - MIN_SPACE)) ! { ! compl_cont_status &= ~CONT_SOL; ! compl_length = (IOSIZE - MIN_SPACE); ! compl_col = curwin->w_cursor.col - compl_length; ! } ! compl_cont_status |= CONT_ADDING | CONT_N_ADDS; ! if (compl_length < 1) ! compl_cont_status &= CONT_LOCAL; ! } ! else if (ctrl_x_mode_line_or_eval()) ! compl_cont_status = CONT_ADDING | CONT_N_ADDS; ! else ! compl_cont_status = 0; ! } ! else ! compl_cont_status &= CONT_LOCAL; ! if (!(compl_cont_status & CONT_ADDING)) // normal expansion ! { ! compl_cont_mode = ctrl_x_mode; ! if (ctrl_x_mode != CTRL_X_NORMAL) ! // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL ! compl_cont_status = 0; ! compl_cont_status |= CONT_N_ADDS; ! compl_startpos = curwin->w_cursor; ! startcol = (int)curs_col; ! compl_col = 0; ! } ! ! // 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 *)""; ! compl_startpos.lnum = curwin->w_cursor.lnum; ! compl_startpos.col = compl_col; ! ins_eol('\r'); ! curbuf->b_p_com = old; ! compl_length = 0; ! compl_col = curwin->w_cursor.col; ! } ! } ! else ! { ! edit_submode_pre = NULL; compl_startpos.col = compl_col; } - - if (compl_cont_status & CONT_LOCAL) - edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); - else - edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); - - // If any of the original typed text has been changed we need to fix - // the redo buffer. - ins_compl_fixRedoBufForLeader(NULL); - - // Always add completion for the original text. - vim_free(compl_orig_text); - compl_orig_text = vim_strnsave(line + compl_col, compl_length); - if (p_ic) - flags |= CP_ICASE; - if (compl_orig_text == NULL || ins_compl_add(compl_orig_text, - -1, NULL, NULL, NULL, 0, flags, FALSE) != OK) - { - VIM_CLEAR(compl_pattern); - VIM_CLEAR(compl_orig_text); - return FAIL; - } - - // showmode might reset the internal line pointers, so it must - // be called before line = ml_get(), or when this address is no - // longer needed. -- Acevedo. - edit_submode_extra = (char_u *)_("-- Searching..."); - edit_submode_highl = HLF_COUNT; - showmode(); - edit_submode_extra = NULL; - out_flush(); } ! else if (insert_match && stop_arrow() == FAIL) ! return FAIL; ! ! compl_shown_match = compl_curr_match; ! compl_shows_dir = compl_direction; ! ! // Find next match (and following matches). ! save_w_wrow = curwin->w_wrow; ! save_w_leftcol = curwin->w_leftcol; ! n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE); ! ! // may undisplay the popup menu ! ins_compl_upd_pum(); ! if (n > 1) // all matches have been found ! compl_matches = n; ! compl_curr_match = compl_shown_match; ! compl_direction = compl_shows_dir; ! // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert ! // mode. ! if (got_int && !global_busy) { ! (void)vgetc(); ! got_int = FALSE; } // we found no match if the list has only the "compl_orig_text"-entry if (compl_first_match == compl_first_match->cp_next) { --- 4463,4655 ---- } /* ! * Continue an interrupted completion mode search in "line". ! * ! * If this same ctrl_x_mode has been interrupted use the text from ! * "compl_startpos" to the cursor as a pattern to add a new word instead of ! * expand the one before the cursor, in word-wise if "compl_startpos" is not in ! * the same line as the cursor then fix it (the line has been split because it ! * was longer than 'tw'). if SOL is set then skip the previous pattern, a word ! * at the beginning of the line has been inserted, we'll look for that. */ ! static void ! ins_compl_continue_search(char_u *line) ! { ! // it is a continued search ! compl_cont_status &= ~CONT_INTRPT; // remove INTRPT ! if (ctrl_x_mode == CTRL_X_NORMAL ! || ctrl_x_mode == CTRL_X_PATH_PATTERNS ! || ctrl_x_mode == CTRL_X_PATH_DEFINES) ! { ! if (compl_startpos.lnum != curwin->w_cursor.lnum) ! { ! // line (probably) wrapped, set compl_startpos to the ! // first non_blank in the line, if it is not a wordchar ! // include it to get a better pattern, but then we don't ! // want the "\\<" prefix, check it below ! compl_col = (colnr_T)getwhitecols(line); ! compl_startpos.col = compl_col; ! compl_startpos.lnum = curwin->w_cursor.lnum; ! compl_cont_status &= ~CONT_SOL; // clear SOL if present ! } ! else ! { ! // S_IPOS was set when we inserted a word that was at the ! // beginning of the line, which means that we'll go to SOL ! // mode but first we need to redefine compl_startpos ! if (compl_cont_status & CONT_S_IPOS) ! { ! compl_cont_status |= CONT_SOL; ! compl_startpos.col = (colnr_T)(skipwhite( ! line + compl_length ! + compl_startpos.col) - line); ! } ! compl_col = compl_startpos.col; ! } ! compl_length = curwin->w_cursor.col - (int)compl_col; ! // IObuff is used to add a "word from the next line" would we ! // have enough space? just being paranoid ! #define MIN_SPACE 75 ! if (compl_length > (IOSIZE - MIN_SPACE)) ! { ! compl_cont_status &= ~CONT_SOL; ! compl_length = (IOSIZE - MIN_SPACE); ! compl_col = curwin->w_cursor.col - compl_length; ! } ! compl_cont_status |= CONT_ADDING | CONT_N_ADDS; ! if (compl_length < 1) ! compl_cont_status &= CONT_LOCAL; ! } ! else if (ctrl_x_mode_line_or_eval()) ! compl_cont_status = CONT_ADDING | CONT_N_ADDS; ! else ! compl_cont_status = 0; ! } ! ! /* ! * start insert mode completion ! */ ! static int ! ins_compl_start(void) { char_u *line; int startcol = 0; // column where searched text starts colnr_T curs_col; // cursor column ! int line_invalid = FALSE; int save_did_ai = did_ai; int flags = CP_ORIGINAL_TEXT; ! // First time we hit ^N or ^P (in a row, I mean) ! did_ai = FALSE; #ifdef FEAT_SMARTINDENT ! did_si = FALSE; ! can_si = FALSE; ! can_si_back = FALSE; #endif ! if (stop_arrow() == FAIL) ! return FAIL; ! line = ml_get(curwin->w_cursor.lnum); ! curs_col = curwin->w_cursor.col; ! compl_pending = 0; ! if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT ! && compl_cont_mode == ctrl_x_mode) ! // this same ctrl-x_mode was interrupted previously. Continue the ! // completion. ! ins_compl_continue_search(line); ! else ! compl_cont_status &= CONT_LOCAL; ! if (!(compl_cont_status & CONT_ADDING)) // normal expansion ! { ! compl_cont_mode = ctrl_x_mode; ! if (ctrl_x_mode != CTRL_X_NORMAL) ! // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL ! compl_cont_status = 0; ! compl_cont_status |= CONT_N_ADDS; ! compl_startpos = curwin->w_cursor; ! startcol = (int)curs_col; ! compl_col = 0; ! } ! ! // 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 *)""; ! compl_startpos.lnum = curwin->w_cursor.lnum; compl_startpos.col = compl_col; + ins_eol('\r'); + curbuf->b_p_com = old; + compl_length = 0; + compl_col = curwin->w_cursor.col; } } ! else ! { ! edit_submode_pre = NULL; ! compl_startpos.col = compl_col; ! } ! if (compl_cont_status & CONT_LOCAL) ! edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); ! else ! edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); ! // If any of the original typed text has been changed we need to fix ! // the redo buffer. ! ins_compl_fixRedoBufForLeader(NULL); ! ! // Always add completion for the original text. ! vim_free(compl_orig_text); ! compl_orig_text = vim_strnsave(line + compl_col, compl_length); ! if (p_ic) ! flags |= CP_ICASE; ! if (compl_orig_text == NULL || ins_compl_add(compl_orig_text, ! -1, NULL, NULL, NULL, 0, flags, FALSE) != OK) { ! VIM_CLEAR(compl_pattern); ! VIM_CLEAR(compl_orig_text); ! return FAIL; } + // showmode might reset the internal line pointers, so it must + // be called before line = ml_get(), or when this address is no + // longer needed. -- Acevedo. + edit_submode_extra = (char_u *)_("-- Searching..."); + edit_submode_highl = HLF_COUNT; + showmode(); + edit_submode_extra = NULL; + out_flush(); + + return OK; + } + + /* + * display the completion status message + */ + static void + ins_compl_show_statusmsg(void) + { // we found no match if the list has only the "compl_orig_text"-entry if (compl_first_match == compl_first_match->cp_next) { *************** *** 4571,4593 **** && compl_length > 1 ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); edit_submode_highl = HLF_E; - // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, - // because we couldn't expand anything at first place, but if we used - // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word - // (such as M in M'exico) if not tried already. -- Acevedo - if ( compl_length > 1 - || (compl_cont_status & CONT_ADDING) - || (ctrl_x_mode != CTRL_X_NORMAL - && ctrl_x_mode != CTRL_X_PATH_PATTERNS - && ctrl_x_mode != CTRL_X_PATH_DEFINES)) - compl_cont_status &= ~CONT_N_ADDS; } - if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) - compl_cont_status |= CONT_S_IPOS; - else - compl_cont_status &= ~CONT_S_IPOS; - if (edit_submode_extra == NULL) { if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) --- 4657,4664 ---- *************** *** 4623,4634 **** if (compl_matches > 0) vim_snprintf((char *)match_ref, sizeof(match_ref), ! _("match %d of %d"), ! compl_curr_match->cp_number, compl_matches); else vim_snprintf((char *)match_ref, sizeof(match_ref), ! _("match %d"), ! compl_curr_match->cp_number); edit_submode_extra = match_ref; edit_submode_highl = HLF_R; if (dollar_vcol >= 0) --- 4694,4705 ---- if (compl_matches > 0) vim_snprintf((char *)match_ref, sizeof(match_ref), ! _("match %d of %d"), ! compl_curr_match->cp_number, compl_matches); else vim_snprintf((char *)match_ref, sizeof(match_ref), ! _("match %d"), ! compl_curr_match->cp_number); edit_submode_extra = match_ref; edit_submode_highl = HLF_R; if (dollar_vcol >= 0) *************** *** 4658,4663 **** --- 4729,4805 ---- msg_clr_cmdline(); // necessary for "noshowmode" } } + } + + /* + * 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). + */ + int + ins_complete(int c, int enable_pum) + { + int n; + int save_w_wrow; + int save_w_leftcol; + int insert_match; + + compl_direction = ins_compl_key2dir(c); + insert_match = ins_compl_use_match(c); + + if (!compl_started) + { + if (ins_compl_start() == FAIL) + return FAIL; + } + else if (insert_match && stop_arrow() == FAIL) + return FAIL; + + compl_shown_match = compl_curr_match; + compl_shows_dir = compl_direction; + + // Find next match (and following matches). + save_w_wrow = curwin->w_wrow; + save_w_leftcol = curwin->w_leftcol; + n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE); + + // may undisplay the popup menu + ins_compl_upd_pum(); + + if (n > 1) // all matches have been found + compl_matches = n; + compl_curr_match = compl_shown_match; + compl_direction = compl_shows_dir; + + // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert + // mode. + if (got_int && !global_busy) + { + (void)vgetc(); + got_int = FALSE; + } + + // we found no match if the list has only the "compl_orig_text"-entry + if (compl_first_match == compl_first_match->cp_next) + { + // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, + // because we couldn't expand anything at first place, but if we used + // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word + // (such as M in M'exico) if not tried already. -- Acevedo + if (compl_length > 1 + || (compl_cont_status & CONT_ADDING) + || (ctrl_x_mode != CTRL_X_NORMAL + && ctrl_x_mode != CTRL_X_PATH_PATTERNS + && ctrl_x_mode != CTRL_X_PATH_DEFINES)) + compl_cont_status &= ~CONT_N_ADDS; + } + + if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) + compl_cont_status |= CONT_S_IPOS; + else + compl_cont_status &= ~CONT_S_IPOS; + + ins_compl_show_statusmsg(); // Show the popup menu, unless we got interrupted. if (enable_pum && !compl_interrupted) *** ../vim-8.2.3943/src/testdir/test_ins_complete.vim 2021-12-29 17:38:42.301517624 +0000 --- src/testdir/test_ins_complete.vim 2021-12-30 11:37:46.568928853 +0000 *************** *** 900,905 **** --- 900,924 ---- close! endfunc + " Test for typing CTRL-R in insert completion mode to insert a register + " content. + func Test_complete_reginsert() + new + call setline(1, ['a1', 'a12', 'a123', 'a1234']) + + " if a valid CTRL-X mode key is returned from =, then it should be + " processed. Otherwise, CTRL-X mode should be stopped and the key should be + " inserted. + exe "normal Goa\\=\"\\\"\" + call assert_equal('a123', getline(5)) + let @r = "\\" + exe "normal GCa\\r" + call assert_equal('a12', getline(5)) + exe "normal GCa\\=\"x\"\" + call assert_equal('a1234x', getline(5)) + bw! + endfunc + func Test_issue_7021() CheckMSWindows *** ../vim-8.2.3943/src/version.c 2021-12-30 10:51:41.168860630 +0000 --- src/version.c 2021-12-30 11:39:10.808802978 +0000 *************** *** 751,752 **** --- 751,754 ---- { /* Add new patch number below this line */ + /**/ + 3944, /**/ -- hundred-and-one symptoms of being an internet addict: 150. You find yourself counting emoticons to get to sleep. /// 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 ///