diff options
-rw-r--r-- | src/insexpand.c | 1330 | ||||
-rw-r--r-- | src/testdir/test_ins_complete.vim | 19 | ||||
-rw-r--r-- | src/version.c | 2 |
3 files changed, 757 insertions, 594 deletions
diff --git a/src/insexpand.c b/src/insexpand.c index 07ac71dc49..aecbf77b46 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -1823,6 +1823,257 @@ ins_compl_addfrommatch(void) } /* + * 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,10 +2081,6 @@ ins_compl_addfrommatch(void) 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; @@ -1910,113 +2157,9 @@ ins_compl_prep(int c) } 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; - } - } + 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,112 +2183,7 @@ ins_compl_prep(int c) 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); - } + 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,6 +3237,102 @@ get_next_spell_completion(linenr_T lnum UNUSED) } /* + * 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,82 +3433,12 @@ get_next_default_completion( && 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); + ptr = ins_comp_get_next_word_or_line(ins_buf, cur_match_pos, &len, + &cont_s_ipos); + if (ptr == NULL) + continue; - 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) @@ -3567,6 +3631,35 @@ ins_compl_get_exp(pos_T *ini) } /* + * 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,97 +3715,74 @@ ins_compl_insert(int in_compl_func) } /* - * 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. + * show the file name for the completion match (if any). Truncate the file + * name to avoid a wait for return. */ - 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() + static void +ins_compl_show_filename(void) { - int num_matches = -1; - int todo = count; - compl_T *found_compl = NULL; - int found_end = FALSE; - int advance; - int started = compl_started; + char *lead = _("match in file"); + int space = sc_col - vim_strsize((char_u *)lead) - 2; + char_u *s; + char_u *e; - // 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 (space <= 0) + return; - if (compl_leader != NULL - && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 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)) { - // 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)) + space -= ptr2cells(e); + while (space < 0) { - 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; + 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! +} - 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; - } +/* + * 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; - // Repeat this for when <PageUp> or <PageDown> 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)); + && (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) + && compl_shown_match->cp_prev != NULL) { found_end = (compl_shown_match == compl_first_match); compl_shown_match = compl_shown_match->cp_prev; @@ -3741,11 +3811,11 @@ ins_compl_next( } // Find matches. - num_matches = ins_compl_get_exp(&compl_startpos); + *num_matches = ins_compl_get_exp(&compl_startpos); // handle any pending completions while (compl_pending != 0 && compl_direction == compl_shows_dir - && advance) + && advance) { if (compl_pending > 0 && compl_shown_match->cp_next != NULL) { @@ -3765,7 +3835,7 @@ ins_compl_next( 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))) + compl_leader, (int)STRLEN(compl_leader))) ++todo; else // Remember a matching item. @@ -3783,6 +3853,70 @@ ins_compl_next( } } + 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 <PageUp> or <PageDown> 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,36 +3970,8 @@ ins_compl_next( 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! - } - } + ins_compl_show_filename(); return num_matches; } @@ -4357,213 +4463,193 @@ compl_get_info(char_u *line, int startcol, colnr_T curs_col, int *line_invalid) } /* - * 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). + * 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. */ - int -ins_complete(int c, int enable_pum) + static void +ins_compl_continue_search(char_u *line) { - 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) + // 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) { - // 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) + if (compl_startpos.lnum != curwin->w_cursor.lnum) { - // 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_ |