mirror of
https://github.com/neovim/neovim.git
synced 2025-10-03 16:36:30 +00:00
vim-patch:9.1.1797: completion: autocompletion can be improved
Problem: completion: autocompletion can be improved
Solution: Add support for "longest" and "preinsert" in 'autocomplete';
add preinserted() (Girish Palya)
* Add support for "longest" in 'completeopt' when 'autocomplete'
is enabled. (Note: the cursor position does not change automatically
when 'autocomplete' is enabled.)
* Add support for "preinsert" when 'autocomplete' is enabled. Ensure
"preinsert" works the same with and without 'autocomplete'
* introduce the preinserted() Vim script function, useful for defining
custom key mappings.
fixes: vim/vim#18314
closes: vim/vim#18387
c05335082a
Co-authored-by: Girish Palya <girishji@gmail.com>
This commit is contained in:
@@ -1638,11 +1638,22 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
to gather more alternatives for your candidate list,
|
||||
see 'completefuzzycollect'.
|
||||
|
||||
longest Only insert the longest common text of the matches. If
|
||||
the menu is displayed you can use CTRL-L to add more
|
||||
characters. Whether case is ignored depends on the kind
|
||||
of completion. For buffer text the 'ignorecase' option is
|
||||
used.
|
||||
longest
|
||||
When 'autocomplete' is not active, only the longest
|
||||
common prefix of the matches is inserted. If the popup
|
||||
menu is displayed, you can use CTRL-L to add more
|
||||
characters. Whether case is ignored depends on the type
|
||||
of completion. For buffer text the 'ignorecase' option
|
||||
applies.
|
||||
|
||||
When 'autocomplete' is active and no completion item is
|
||||
selected, the longest common prefix of the matches is
|
||||
inserted after the cursor. The prefix is taken either
|
||||
from all displayed items or only from items in the current
|
||||
buffer. The inserted text is highlighted with
|
||||
|hl-PreInsert|, and the cursor position does not change
|
||||
(similar to `"preinsert"`). Press CTRL-Y to accept.
|
||||
See also |preinserted()|.
|
||||
|
||||
menu Use a popup menu to show the possible completions. The
|
||||
menu is only shown when there is more than one match and
|
||||
@@ -1675,22 +1686,21 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
with "menu" or "menuone". Overrides "preview".
|
||||
|
||||
preinsert
|
||||
When 'autocomplete' is not active, inserts the part of the
|
||||
first candidate word beyond the current completion leader,
|
||||
highlighted with |hl-PreInsert|. The cursor doesn't move.
|
||||
Requires "fuzzy" unset and "menuone" in 'completeopt'.
|
||||
|
||||
When 'autocomplete' is active, inserts the longest common
|
||||
prefix of matches (from all shown items or from the
|
||||
current buffer items). This occurs only when no menu item
|
||||
is selected. Press CTRL-Y to accept.
|
||||
Inserts the text of the first completion candidate
|
||||
beyond the current leader, highlighted with |hl-PreInsert|.
|
||||
The cursor does not move.
|
||||
Requires "fuzzy" to be unset, and either "menuone" in
|
||||
'completeopt' or 'autocomplete' enabled. When
|
||||
'autocomplete' is enabled, this does not work if
|
||||
'ignorecase' is set without 'infercase'.
|
||||
See also |preinserted()|.
|
||||
|
||||
preview Show extra information about the currently selected
|
||||
completion in the preview window. Only works in
|
||||
combination with "menu" or "menuone".
|
||||
|
||||
Only "fuzzy", "popup", "preinsert" and "preview" have an effect when
|
||||
'autocomplete' is enabled.
|
||||
Only "fuzzy", "longest", "popup", "preinsert" and "preview" have an
|
||||
effect when 'autocomplete' is enabled.
|
||||
|
||||
This option does not apply to |cmdline-completion|. See 'wildoptions'
|
||||
for that.
|
||||
|
@@ -940,6 +940,7 @@ Insert mode completion: *completion-functions*
|
||||
complete_info() get current completion information
|
||||
complete_match() get insert completion start match col and
|
||||
trigger text
|
||||
preinserted() check if text is inserted after cursor
|
||||
pumvisible() check if the popup menu is displayed
|
||||
pum_getpos() position and size of popup menu if visible
|
||||
|
||||
|
@@ -7232,6 +7232,15 @@ pow({x}, {y}) *pow()*
|
||||
Return: ~
|
||||
(`number`)
|
||||
|
||||
preinserted() *preinserted()*
|
||||
Returns non-zero if text has been inserted after the cursor
|
||||
because "preinsert" is present in 'completeopt', or if
|
||||
"longest" is present in 'completeopt' while 'autocomplete'
|
||||
is enabled. Otherwise returns zero.
|
||||
|
||||
Return: ~
|
||||
(`number`)
|
||||
|
||||
prevnonblank({lnum}) *prevnonblank()*
|
||||
Return the line number of the first line at or above {lnum}
|
||||
that is not blank. Example: >vim
|
||||
|
42
runtime/lua/vim/_meta/options.lua
generated
42
runtime/lua/vim/_meta/options.lua
generated
@@ -1183,11 +1183,22 @@ vim.go.cia = vim.go.completeitemalign
|
||||
--- to gather more alternatives for your candidate list,
|
||||
--- see 'completefuzzycollect'.
|
||||
---
|
||||
--- longest Only insert the longest common text of the matches. If
|
||||
--- the menu is displayed you can use CTRL-L to add more
|
||||
--- characters. Whether case is ignored depends on the kind
|
||||
--- of completion. For buffer text the 'ignorecase' option is
|
||||
--- used.
|
||||
--- longest
|
||||
--- When 'autocomplete' is not active, only the longest
|
||||
--- common prefix of the matches is inserted. If the popup
|
||||
--- menu is displayed, you can use CTRL-L to add more
|
||||
--- characters. Whether case is ignored depends on the type
|
||||
--- of completion. For buffer text the 'ignorecase' option
|
||||
--- applies.
|
||||
---
|
||||
--- When 'autocomplete' is active and no completion item is
|
||||
--- selected, the longest common prefix of the matches is
|
||||
--- inserted after the cursor. The prefix is taken either
|
||||
--- from all displayed items or only from items in the current
|
||||
--- buffer. The inserted text is highlighted with
|
||||
--- `hl-PreInsert`, and the cursor position does not change
|
||||
--- (similar to `"preinsert"`). Press CTRL-Y to accept.
|
||||
--- See also `preinserted()`.
|
||||
---
|
||||
--- menu Use a popup menu to show the possible completions. The
|
||||
--- menu is only shown when there is more than one match and
|
||||
@@ -1220,22 +1231,21 @@ vim.go.cia = vim.go.completeitemalign
|
||||
--- with "menu" or "menuone". Overrides "preview".
|
||||
---
|
||||
--- preinsert
|
||||
--- When 'autocomplete' is not active, inserts the part of the
|
||||
--- first candidate word beyond the current completion leader,
|
||||
--- highlighted with `hl-PreInsert`. The cursor doesn't move.
|
||||
--- Requires "fuzzy" unset and "menuone" in 'completeopt'.
|
||||
---
|
||||
--- When 'autocomplete' is active, inserts the longest common
|
||||
--- prefix of matches (from all shown items or from the
|
||||
--- current buffer items). This occurs only when no menu item
|
||||
--- is selected. Press CTRL-Y to accept.
|
||||
--- Inserts the text of the first completion candidate
|
||||
--- beyond the current leader, highlighted with `hl-PreInsert`.
|
||||
--- The cursor does not move.
|
||||
--- Requires "fuzzy" to be unset, and either "menuone" in
|
||||
--- 'completeopt' or 'autocomplete' enabled. When
|
||||
--- 'autocomplete' is enabled, this does not work if
|
||||
--- 'ignorecase' is set without 'infercase'.
|
||||
--- See also `preinserted()`.
|
||||
---
|
||||
--- preview Show extra information about the currently selected
|
||||
--- completion in the preview window. Only works in
|
||||
--- combination with "menu" or "menuone".
|
||||
---
|
||||
--- Only "fuzzy", "popup", "preinsert" and "preview" have an effect when
|
||||
--- 'autocomplete' is enabled.
|
||||
--- Only "fuzzy", "longest", "popup", "preinsert" and "preview" have an
|
||||
--- effect when 'autocomplete' is enabled.
|
||||
---
|
||||
--- This option does not apply to `cmdline-completion`. See 'wildoptions'
|
||||
--- for that.
|
||||
|
8
runtime/lua/vim/_meta/vimfn.lua
generated
8
runtime/lua/vim/_meta/vimfn.lua
generated
@@ -6554,6 +6554,14 @@ function vim.fn.perleval(expr) end
|
||||
--- @return number
|
||||
function vim.fn.pow(x, y) end
|
||||
|
||||
--- Returns non-zero if text has been inserted after the cursor
|
||||
--- because "preinsert" is present in 'completeopt', or if
|
||||
--- "longest" is present in 'completeopt' while 'autocomplete'
|
||||
--- is enabled. Otherwise returns zero.
|
||||
---
|
||||
--- @return number
|
||||
function vim.fn.preinserted() end
|
||||
|
||||
--- Return the line number of the first line at or above {lnum}
|
||||
--- that is not blank. Example: >vim
|
||||
--- let ind = indent(prevnonblank(v:lnum - 1))
|
||||
|
@@ -606,10 +606,10 @@ static int insert_execute(VimState *state, int key)
|
||||
&& (s->c == CAR || s->c == K_KENTER || s->c == NL)))
|
||||
&& stop_arrow() == OK) {
|
||||
ins_compl_delete(false);
|
||||
if (ins_compl_has_preinsert() && ins_compl_autocomplete_enabled()) {
|
||||
(void)ins_compl_insert(false, true);
|
||||
} else {
|
||||
(void)ins_compl_insert(false, false);
|
||||
ins_compl_insert(false, !ins_compl_has_preinsert());
|
||||
if (ins_compl_preinsert_longest()) {
|
||||
ins_compl_init_get_longest();
|
||||
return 1;
|
||||
}
|
||||
} else if (ascii_iswhite_nl_or_nul(s->c) && ins_compl_preinsert_effect()) {
|
||||
// Delete preinserted text when typing special chars
|
||||
|
@@ -8024,6 +8024,18 @@ M.funcs = {
|
||||
returns = 'number',
|
||||
signature = 'pow({x}, {y})',
|
||||
},
|
||||
preinserted = {
|
||||
desc = [=[
|
||||
Returns non-zero if text has been inserted after the cursor
|
||||
because "preinsert" is present in 'completeopt', or if
|
||||
"longest" is present in 'completeopt' while 'autocomplete'
|
||||
is enabled. Otherwise returns zero.
|
||||
]=],
|
||||
name = 'preinserted',
|
||||
params = {},
|
||||
returns = 'number',
|
||||
signature = 'preinserted()',
|
||||
},
|
||||
prevnonblank = {
|
||||
args = 1,
|
||||
base = 1,
|
||||
|
@@ -296,7 +296,7 @@ static bool compl_autocomplete = false; ///< whether autocompletion is ac
|
||||
static uint64_t compl_timeout_ms = COMPL_INITIAL_TIMEOUT_MS;
|
||||
static bool compl_time_slice_expired = false; ///< time budget exceeded for current source
|
||||
static bool compl_from_nonkeyword = false; ///< completion started from non-keyword
|
||||
static bool compl_autocomplete_preinsert = false; ///< apply preinsert highlight
|
||||
static bool compl_hi_on_autocompl_longest = false; ///< apply "PreInsert" highlight
|
||||
|
||||
// Halve the current completion timeout, simulating exponential decay.
|
||||
#define COMPL_MIN_TIMEOUT_MS 5
|
||||
@@ -889,6 +889,15 @@ static bool is_nearest_active(void)
|
||||
&& !(flags & kOptCotFlagFuzzy);
|
||||
}
|
||||
|
||||
/// Returns true if autocomplete is active and the pre-insert effect targets the
|
||||
/// longest prefix.
|
||||
bool ins_compl_preinsert_longest(void)
|
||||
{
|
||||
return compl_autocomplete
|
||||
&& (get_cot_flags() & (kOptCotFlagLongest | kOptCotFlagPreinsert | kOptCotFlagFuzzy))
|
||||
== kOptCotFlagLongest;
|
||||
}
|
||||
|
||||
/// Add a match to the list of matches
|
||||
///
|
||||
/// @param[in] str text of the match to add
|
||||
@@ -1053,7 +1062,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
|
||||
compl_curr_match = match;
|
||||
|
||||
// Find the longest common string if still doing that.
|
||||
if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0 && !cfc_has_mode()) {
|
||||
if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0 && !cfc_has_mode()
|
||||
&& !ins_compl_preinsert_longest()) {
|
||||
ins_compl_longest_match(match);
|
||||
}
|
||||
|
||||
@@ -1107,17 +1117,12 @@ static size_t ins_compl_leader_len(void)
|
||||
/// -1 means normal item.
|
||||
int ins_compl_col_range_attr(linenr_T lnum, int col)
|
||||
{
|
||||
const bool has_preinsert = ins_compl_has_preinsert();
|
||||
const bool has_preinsert = ins_compl_has_preinsert() || ins_compl_preinsert_longest();
|
||||
|
||||
int attr;
|
||||
if ((get_cot_flags() & kOptCotFlagFuzzy)
|
||||
|| (!has_preinsert
|
||||
&& (attr = syn_name2attr("ComplMatchIns")) == 0)
|
||||
|| (!compl_autocomplete && has_preinsert
|
||||
&& (attr = syn_name2attr("PreInsert")) == 0)
|
||||
|| (compl_autocomplete
|
||||
&& (!compl_autocomplete_preinsert
|
||||
|| (attr = syn_name2attr("PreInsert")) == 0))) {
|
||||
|| (!compl_hi_on_autocompl_longest && ins_compl_preinsert_longest())
|
||||
|| (attr = syn_name2attr(has_preinsert ? "PreInsert" : "ComplMatchIns")) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1511,7 +1516,8 @@ static int ins_compl_build_pum(void)
|
||||
}
|
||||
|
||||
unsigned cur_cot_flags = get_cot_flags();
|
||||
bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0 || compl_autocomplete;
|
||||
bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0
|
||||
|| (compl_autocomplete && !ins_compl_has_preinsert());
|
||||
bool fuzzy_filter = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
|
||||
|
||||
compl_T *match_head = NULL, *match_tail = NULL;
|
||||
@@ -2132,6 +2138,9 @@ int ins_compl_len(void)
|
||||
bool ins_compl_has_preinsert(void)
|
||||
{
|
||||
unsigned cur_cot_flags = get_cot_flags();
|
||||
if (compl_autocomplete && p_ic && !p_inf) {
|
||||
return false;
|
||||
}
|
||||
return (!compl_autocomplete
|
||||
? (cur_cot_flags & (kOptCotFlagPreinsert|kOptCotFlagFuzzy|kOptCotFlagMenuone))
|
||||
== (kOptCotFlagPreinsert|kOptCotFlagMenuone)
|
||||
@@ -2143,19 +2152,13 @@ bool ins_compl_has_preinsert(void)
|
||||
/// the `compl_ins_end_col` range.
|
||||
bool ins_compl_preinsert_effect(void)
|
||||
{
|
||||
if (!ins_compl_has_preinsert()) {
|
||||
if (!ins_compl_has_preinsert() && !ins_compl_preinsert_longest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return curwin->w_cursor.col < compl_ins_end_col;
|
||||
}
|
||||
|
||||
/// Returns true if autocompletion is active.
|
||||
bool ins_compl_autocomplete_enabled(void)
|
||||
{
|
||||
return compl_autocomplete;
|
||||
}
|
||||
|
||||
/// Delete one character before the cursor and show the subset of the matches
|
||||
/// that match the word that is now before the cursor.
|
||||
/// Returns the character to be used, NUL if the work is done and another char
|
||||
@@ -2198,7 +2201,7 @@ int ins_compl_bs(void)
|
||||
(size_t)(p_off - (ptrdiff_t)compl_col));
|
||||
|
||||
// Clear selection if a menu item is currently selected in autocompletion
|
||||
if (compl_autocomplete && compl_first_match) {
|
||||
if (compl_autocomplete && compl_first_match && !ins_compl_has_preinsert()) {
|
||||
compl_shown_match = compl_first_match;
|
||||
}
|
||||
|
||||
@@ -2291,18 +2294,14 @@ static void ins_compl_new_leader(void)
|
||||
// Show the popup menu with a different set of matches.
|
||||
ins_compl_show_pum();
|
||||
|
||||
compl_autocomplete_preinsert = false;
|
||||
// Don't let Enter select the original text when there is no popup menu.
|
||||
if (compl_match_array == NULL) {
|
||||
compl_enter_selects = false;
|
||||
} else if (ins_compl_has_preinsert() && compl_leader.size > 0) {
|
||||
if (compl_started && compl_autocomplete && !ins_compl_preinsert_effect()) {
|
||||
if (ins_compl_insert(true, true) == OK) {
|
||||
compl_autocomplete_preinsert = true;
|
||||
}
|
||||
} else {
|
||||
(void)ins_compl_insert(true, false);
|
||||
}
|
||||
ins_compl_insert(true, false);
|
||||
} else if (compl_started && ins_compl_preinsert_longest()
|
||||
&& compl_leader.size > 0 && !ins_compl_preinsert_effect()) {
|
||||
ins_compl_insert(true, true);
|
||||
}
|
||||
// Don't let Enter select when use user function and refresh_always is set
|
||||
if (ins_compl_refresh_always()) {
|
||||
@@ -4388,7 +4387,7 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
|
||||
ptr = ins_compl_get_next_word_or_line(st->ins_buf,
|
||||
st->cur_match_pos, &len, &cont_s_ipos);
|
||||
}
|
||||
if (ptr == NULL || (!compl_autocomplete && ins_compl_has_preinsert()
|
||||
if (ptr == NULL || (ins_compl_has_preinsert()
|
||||
&& strcmp(ptr, compl_pattern.data) == 0)) {
|
||||
continue;
|
||||
}
|
||||
@@ -4895,7 +4894,7 @@ static int ins_compl_get_exp(pos_T *ini)
|
||||
}
|
||||
may_trigger_modechanged();
|
||||
|
||||
if (is_nearest_active()) {
|
||||
if (is_nearest_active() && !ins_compl_has_preinsert()) {
|
||||
sort_compl_match_list(cp_compare_nearest);
|
||||
}
|
||||
|
||||
@@ -5108,6 +5107,21 @@ static char *find_common_prefix(size_t *prefix_len, bool curbuf_only)
|
||||
xfree(match_count);
|
||||
|
||||
if (len > (int)ins_compl_leader_len()) {
|
||||
assert(first != NULL);
|
||||
// Avoid inserting text that duplicates the text already present
|
||||
// after the cursor.
|
||||
if (len == (int)strlen(first)) {
|
||||
char *line = get_cursor_line_ptr();
|
||||
char *p = line + curwin->w_cursor.col;
|
||||
if (p && !ascii_iswhite_or_nul(*p)) {
|
||||
char *end = find_word_end(p);
|
||||
int text_len = (int)(end - p);
|
||||
if (text_len > 0 && text_len < (len - (int)ins_compl_leader_len())
|
||||
&& strncmp(first + len - text_len, p, (size_t)text_len) == 0) {
|
||||
len -= text_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
*prefix_len = (size_t)len;
|
||||
return first;
|
||||
}
|
||||
@@ -5117,9 +5131,9 @@ static char *find_common_prefix(size_t *prefix_len, bool curbuf_only)
|
||||
/// Insert the new text being completed.
|
||||
/// "move_cursor" is used when 'completeopt' includes "preinsert" and when true
|
||||
/// cursor needs to move back from the inserted text to the compl_leader.
|
||||
/// When "preinsert_prefix" is true the longest common prefix is inserted
|
||||
/// instead of shown match.
|
||||
int ins_compl_insert(bool move_cursor, bool preinsert_prefix)
|
||||
/// When "insert_prefix" is true the longest common prefix is inserted instead
|
||||
/// of shown match.
|
||||
void ins_compl_insert(bool move_cursor, bool insert_prefix)
|
||||
{
|
||||
int compl_len = get_compl_len();
|
||||
bool preinsert = ins_compl_has_preinsert();
|
||||
@@ -5128,12 +5142,13 @@ int ins_compl_insert(bool move_cursor, bool preinsert_prefix)
|
||||
size_t leader_len = ins_compl_leader_len();
|
||||
char *has_multiple = strchr(cp_str, '\n');
|
||||
|
||||
if (preinsert_prefix) {
|
||||
if (insert_prefix) {
|
||||
cp_str = find_common_prefix(&cp_str_len, false);
|
||||
if (cp_str == NULL) {
|
||||
cp_str = find_common_prefix(&cp_str_len, true);
|
||||
if (cp_str == NULL) {
|
||||
return FAIL;
|
||||
cp_str = compl_shown_match->cp_str.data;
|
||||
cp_str_len = compl_shown_match->cp_str.size;
|
||||
}
|
||||
}
|
||||
} else if (cpt_sources_array != NULL) {
|
||||
@@ -5160,18 +5175,18 @@ int ins_compl_insert(bool move_cursor, bool preinsert_prefix)
|
||||
ins_compl_expand_multiple(cp_str + compl_len);
|
||||
} else {
|
||||
ins_compl_insert_bytes(cp_str + compl_len,
|
||||
preinsert_prefix ? (int)cp_str_len - compl_len : -1);
|
||||
if (preinsert && move_cursor) {
|
||||
insert_prefix ? (int)cp_str_len - compl_len : -1);
|
||||
if ((preinsert || insert_prefix) && move_cursor) {
|
||||
curwin->w_cursor.col -= (colnr_T)(cp_str_len - leader_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
compl_used_match = !(match_at_original_text(compl_shown_match)
|
||||
|| (preinsert && !compl_autocomplete));
|
||||
|| (preinsert && !insert_prefix));
|
||||
|
||||
dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
|
||||
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
|
||||
return OK;
|
||||
compl_hi_on_autocompl_longest = insert_prefix && move_cursor;
|
||||
}
|
||||
|
||||
/// show the file name for the completion match (if any). Truncate the file
|
||||
@@ -5240,7 +5255,8 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
|
||||
bool found_end = false;
|
||||
compl_T *found_compl = NULL;
|
||||
unsigned cur_cot_flags = get_cot_flags();
|
||||
bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0 || compl_autocomplete;
|
||||
bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0
|
||||
|| (compl_autocomplete && !ins_compl_has_preinsert());
|
||||
bool compl_fuzzy_match = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
|
||||
|
||||
while (--todo >= 0) {
|
||||
@@ -5351,7 +5367,8 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
|
||||
const bool started = compl_started;
|
||||
buf_T *const orig_curbuf = curbuf;
|
||||
unsigned cur_cot_flags = get_cot_flags();
|
||||
bool compl_no_insert = (cur_cot_flags & kOptCotFlagNoinsert) != 0 || compl_autocomplete;
|
||||
bool compl_no_insert = (cur_cot_flags & kOptCotFlagNoinsert) != 0
|
||||
|| (compl_autocomplete && !ins_compl_has_preinsert());
|
||||
bool compl_fuzzy_match = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
|
||||
bool compl_preinsert = ins_compl_has_preinsert();
|
||||
|
||||
@@ -5397,35 +5414,18 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
|
||||
return -1;
|
||||
}
|
||||
|
||||
compl_autocomplete_preinsert = false;
|
||||
|
||||
// Insert the text of the new completion, or the compl_leader.
|
||||
if (compl_no_insert && !started) {
|
||||
bool insert_orig = !compl_preinsert;
|
||||
if (compl_preinsert && compl_autocomplete) {
|
||||
if (ins_compl_insert(true, true) == OK) {
|
||||
compl_autocomplete_preinsert = true;
|
||||
} else {
|
||||
insert_orig = true;
|
||||
}
|
||||
}
|
||||
if (insert_orig) {
|
||||
ins_compl_insert_bytes(compl_orig_text.data + get_compl_len(), -1);
|
||||
}
|
||||
if (!started && ins_compl_preinsert_longest()) {
|
||||
ins_compl_insert(true, true);
|
||||
} else if (compl_no_insert && !started && !compl_preinsert) {
|
||||
ins_compl_insert_bytes(compl_orig_text.data + get_compl_len(), -1);
|
||||
compl_used_match = false;
|
||||
restore_orig_extmarks();
|
||||
} else if (insert_match) {
|
||||
if (!compl_get_longest || compl_used_match) {
|
||||
bool none_selected = match_at_original_text(compl_shown_match);
|
||||
if (compl_preinsert && compl_autocomplete && none_selected) {
|
||||
if (ins_compl_insert(none_selected, true) == OK) {
|
||||
compl_autocomplete_preinsert = none_selected;
|
||||
} else {
|
||||
(void)ins_compl_insert(false, false);
|
||||
}
|
||||
} else {
|
||||
(void)ins_compl_insert(!compl_autocomplete, false);
|
||||
}
|
||||
bool preinsert_longest = ins_compl_preinsert_longest()
|
||||
&& match_at_original_text(compl_shown_match); // none selected
|
||||
ins_compl_insert(compl_preinsert || preinsert_longest, preinsert_longest);
|
||||
} else {
|
||||
assert(compl_leader.data != NULL);
|
||||
ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
|
||||
@@ -5534,8 +5534,8 @@ void ins_compl_check_keys(int frequency, bool in_compl_func)
|
||||
}
|
||||
}
|
||||
|
||||
if (compl_pending != 0 && !got_int && !(cot_flags & kOptCotFlagNoinsert)
|
||||
&& !compl_autocomplete) {
|
||||
if (compl_pending && !got_int && !(cot_flags & kOptCotFlagNoinsert)
|
||||
&& (!compl_autocomplete || ins_compl_has_preinsert())) {
|
||||
// Insert the first match immediately and advance compl_shown_match,
|
||||
// before finding other matches.
|
||||
int todo = compl_pending > 0 ? compl_pending : -compl_pending;
|
||||
@@ -6599,3 +6599,11 @@ static void cpt_compl_refresh(void)
|
||||
// Make the list cyclic
|
||||
compl_matches = ins_compl_make_cyclic();
|
||||
}
|
||||
|
||||
/// "preinserted()" function
|
||||
void f_preinserted(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
if (ins_compl_preinsert_effect()) {
|
||||
rettv->vval.v_number = 1;
|
||||
}
|
||||
}
|
||||
|
@@ -1663,11 +1663,22 @@ local options = {
|
||||
to gather more alternatives for your candidate list,
|
||||
see 'completefuzzycollect'.
|
||||
|
||||
longest Only insert the longest common text of the matches. If
|
||||
the menu is displayed you can use CTRL-L to add more
|
||||
characters. Whether case is ignored depends on the kind
|
||||
of completion. For buffer text the 'ignorecase' option is
|
||||
used.
|
||||
longest
|
||||
When 'autocomplete' is not active, only the longest
|
||||
common prefix of the matches is inserted. If the popup
|
||||
menu is displayed, you can use CTRL-L to add more
|
||||
characters. Whether case is ignored depends on the type
|
||||
of completion. For buffer text the 'ignorecase' option
|
||||
applies.
|
||||
|
||||
When 'autocomplete' is active and no completion item is
|
||||
selected, the longest common prefix of the matches is
|
||||
inserted after the cursor. The prefix is taken either
|
||||
from all displayed items or only from items in the current
|
||||
buffer. The inserted text is highlighted with
|
||||
|hl-PreInsert|, and the cursor position does not change
|
||||
(similar to `"preinsert"`). Press CTRL-Y to accept.
|
||||
See also |preinserted()|.
|
||||
|
||||
menu Use a popup menu to show the possible completions. The
|
||||
menu is only shown when there is more than one match and
|
||||
@@ -1700,22 +1711,21 @@ local options = {
|
||||
with "menu" or "menuone". Overrides "preview".
|
||||
|
||||
preinsert
|
||||
When 'autocomplete' is not active, inserts the part of the
|
||||
first candidate word beyond the current completion leader,
|
||||
highlighted with |hl-PreInsert|. The cursor doesn't move.
|
||||
Requires "fuzzy" unset and "menuone" in 'completeopt'.
|
||||
|
||||
When 'autocomplete' is active, inserts the longest common
|
||||
prefix of matches (from all shown items or from the
|
||||
current buffer items). This occurs only when no menu item
|
||||
is selected. Press CTRL-Y to accept.
|
||||
Inserts the text of the first completion candidate
|
||||
beyond the current leader, highlighted with |hl-PreInsert|.
|
||||
The cursor does not move.
|
||||
Requires "fuzzy" to be unset, and either "menuone" in
|
||||
'completeopt' or 'autocomplete' enabled. When
|
||||
'autocomplete' is enabled, this does not work if
|
||||
'ignorecase' is set without 'infercase'.
|
||||
See also |preinserted()|.
|
||||
|
||||
preview Show extra information about the currently selected
|
||||
completion in the preview window. Only works in
|
||||
combination with "menu" or "menuone".
|
||||
|
||||
Only "fuzzy", "popup", "preinsert" and "preview" have an effect when
|
||||
'autocomplete' is enabled.
|
||||
Only "fuzzy", "longest", "popup", "preinsert" and "preview" have an
|
||||
effect when 'autocomplete' is enabled.
|
||||
|
||||
This option does not apply to |cmdline-completion|. See 'wildoptions'
|
||||
for that.
|
||||
|
@@ -1496,18 +1496,18 @@ describe('completion', function()
|
||||
|
||||
-- During delay wait, user can open menu using CTRL_N completion
|
||||
feed('<Esc>')
|
||||
command('set completeopt=menuone,preinsert')
|
||||
command('set completeopt=menuone,longest')
|
||||
feed('Sf<C-N>')
|
||||
screen:expect([[
|
||||
foo |
|
||||
foobar |
|
||||
foobarbaz |
|
||||
f{102:^oo} |
|
||||
{12:foo }{1: }|
|
||||
foo^ |
|
||||
{4:foo }{1: }|
|
||||
{4:foobar }{1: }|
|
||||
{4:foobarbaz }{1: }|
|
||||
{1:~ }|*2
|
||||
{5:-- Keyword completion (^N^P) }{6:match 1 of 3} |
|
||||
{5:-- Keyword completion (^N^P) }{19:Back at original} |
|
||||
]])
|
||||
|
||||
-- After the menu is open, ^N/^P and Up/Down should not delay
|
||||
|
@@ -124,10 +124,9 @@ endfunc
|
||||
func Test_omni_dash()
|
||||
func Omni(findstart, base)
|
||||
if a:findstart
|
||||
return 5
|
||||
return 5
|
||||
else
|
||||
echom a:base
|
||||
return ['-help', '-v']
|
||||
return ['-help', '-v']
|
||||
endif
|
||||
endfunc
|
||||
set omnifunc=Omni
|
||||
@@ -4231,6 +4230,145 @@ func Test_completeopt_preinsert()
|
||||
delfunc Omni_test
|
||||
endfunc
|
||||
|
||||
func Test_autocomplete_completeopt_preinsert()
|
||||
func Omni_test(findstart, base)
|
||||
if a:findstart
|
||||
return col(".") - 1
|
||||
endif
|
||||
return [#{word: "fobar"}, #{word: "foobar"}]
|
||||
endfunc
|
||||
set omnifunc=Omni_test complete+=o
|
||||
set completeopt=preinsert autocomplete
|
||||
" set completeopt=preinsert,menuone autocomplete
|
||||
func GetLine()
|
||||
let g:line = getline('.')
|
||||
let g:col = col('.')
|
||||
endfunc
|
||||
|
||||
call Ntest_override("char_avail", 1)
|
||||
new
|
||||
inoremap <buffer><F5> <C-R>=GetLine()<CR>
|
||||
call feedkeys("Sfo\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("fobar", g:line)
|
||||
call assert_equal(3, g:col)
|
||||
|
||||
call feedkeys("Sfoo\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("foobar", g:line)
|
||||
|
||||
call feedkeys("Sfoo\<BS>\<BS>\<BS>", 'tx')
|
||||
call assert_equal("", getline('.'))
|
||||
|
||||
" delete a character
|
||||
call feedkeys("Sfoo\<BS>b\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("fobar", g:line)
|
||||
call assert_equal(4, g:col)
|
||||
|
||||
set complete&
|
||||
%d
|
||||
call setline(1, ['fobar', 'foobar'])
|
||||
|
||||
call feedkeys("Gofoo\<BS>\<BS>\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("fobar", g:line)
|
||||
call assert_equal(2, g:col)
|
||||
|
||||
call feedkeys("Shello wo\<Left>\<Left>\<Left>f\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("hello fobar wo", g:line)
|
||||
call assert_equal(9, g:col)
|
||||
|
||||
call feedkeys("Shello wo\<Left>\<Left>\<Left>f\<BS>\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("hello wo", g:line)
|
||||
call assert_equal(8, g:col)
|
||||
|
||||
call feedkeys("Shello wo\<Left>\<Left>\<Left>foo\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("hello foobar wo", g:line)
|
||||
call assert_equal(11, g:col)
|
||||
|
||||
call feedkeys("Shello wo\<Left>\<Left>\<Left>foo\<BS>b\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("hello fobar wo", g:line)
|
||||
call assert_equal(11, g:col)
|
||||
|
||||
" confirm
|
||||
call feedkeys("Sf\<C-Y>", 'tx')
|
||||
call assert_equal("fobar", getline('.'))
|
||||
call assert_equal(5, col('.'))
|
||||
|
||||
" cancel
|
||||
call feedkeys("Sfo\<C-E>", 'tx')
|
||||
call assert_equal("fo", getline('.'))
|
||||
call assert_equal(2, col('.'))
|
||||
|
||||
" delete preinsert part
|
||||
call feedkeys("Sfo ", 'tx')
|
||||
call assert_equal("fo ", getline('.'))
|
||||
call assert_equal(3, col('.'))
|
||||
|
||||
" can not work with fuzzy
|
||||
set cot+=fuzzy
|
||||
call feedkeys("Sf", 'tx')
|
||||
call assert_equal("f", getline('.'))
|
||||
set cot-=fuzzy
|
||||
|
||||
" does not work with 'ignorecase' unless 'infercase' is also enabled
|
||||
%d
|
||||
call setline(1, ['FIX', 'fobar', 'foobar'])
|
||||
set ignorecase
|
||||
call feedkeys("Gof\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("f", g:line) " should not produce 'FIX'
|
||||
set infercase
|
||||
call feedkeys("Gof\<F5>\<ESC>", 'tx')
|
||||
call assert_equal("fix", g:line)
|
||||
set ignorecase& infercase&
|
||||
|
||||
%delete _
|
||||
let &l:undolevels = &l:undolevels
|
||||
normal! ifoo
|
||||
let &l:undolevels = &l:undolevels
|
||||
normal! obar
|
||||
let &l:undolevels = &l:undolevels
|
||||
normal! obaz
|
||||
let &l:undolevels = &l:undolevels
|
||||
|
||||
func CheckUndo()
|
||||
let g:errmsg = ''
|
||||
call assert_equal(['foo', 'bar', 'baz'], getline(1, '$'))
|
||||
undo
|
||||
call assert_equal(['foo', 'bar'], getline(1, '$'))
|
||||
undo
|
||||
call assert_equal(['foo'], getline(1, '$'))
|
||||
undo
|
||||
call assert_equal([''], getline(1, '$'))
|
||||
later 3
|
||||
call assert_equal(['foo', 'bar', 'baz'], getline(1, '$'))
|
||||
call assert_equal('', v:errmsg)
|
||||
endfunc
|
||||
|
||||
" Check that switching buffer with "preinsert" doesn't corrupt undo.
|
||||
new
|
||||
setlocal bufhidden=wipe
|
||||
inoremap <buffer> <F2> <Cmd>enew!<CR>
|
||||
call feedkeys("if\<F2>\<Esc>", 'tx')
|
||||
bwipe!
|
||||
call CheckUndo()
|
||||
|
||||
" Check that closing window with "preinsert" doesn't corrupt undo.
|
||||
new
|
||||
setlocal bufhidden=wipe
|
||||
inoremap <buffer> <F2> <Cmd>close!<CR>
|
||||
call feedkeys("if\<F2>\<Esc>", 'tx')
|
||||
call CheckUndo()
|
||||
|
||||
%delete _
|
||||
delfunc CheckUndo
|
||||
|
||||
bw!
|
||||
set cot&
|
||||
set omnifunc&
|
||||
set autocomplete&
|
||||
call Ntest_override("char_avail", 0)
|
||||
delfunc Omni_test
|
||||
delfunc GetLine
|
||||
endfunc
|
||||
|
||||
" Check that mark positions are correct after triggering multiline completion.
|
||||
func Test_complete_multiline_marks()
|
||||
func Omni_test(findstart, base)
|
||||
@@ -5696,7 +5834,7 @@ func Test_autocompletedelay()
|
||||
call VerifyScreenDump(buf, 'Test_autocompletedelay_6', {})
|
||||
|
||||
" During delay wait, user can open menu using CTRL_N completion
|
||||
call term_sendkeys(buf, "\<Esc>:set completeopt=menuone,preinsert\<CR>")
|
||||
call term_sendkeys(buf, "\<Esc>:set completeopt=menuone,longest\<CR>")
|
||||
call term_sendkeys(buf, "Sf\<C-N>")
|
||||
call VerifyScreenDump(buf, 'Test_autocompletedelay_7', {})
|
||||
|
||||
@@ -5720,16 +5858,18 @@ func Test_autocompletedelay()
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_autocomplete_completeopt_preinsert()
|
||||
" Preinsert longest prefix when autocomplete
|
||||
func Test_autocomplete_longest()
|
||||
func GetLine()
|
||||
let g:line = getline('.')
|
||||
let g:col = col('.')
|
||||
let g:preinserted = preinserted()
|
||||
endfunc
|
||||
|
||||
call Ntest_override("char_avail", 1)
|
||||
new
|
||||
inoremap <buffer><F5> <C-R>=GetLine()<CR>
|
||||
set completeopt=preinsert autocomplete
|
||||
set completeopt=longest autocomplete
|
||||
call setline(1, ["foobar", "foozbar"])
|
||||
call feedkeys("Go\<ESC>", 'tx')
|
||||
|
||||
@@ -5761,10 +5901,15 @@ func Test_autocomplete_completeopt_preinsert()
|
||||
call DoTest("f\<C-P>", 'foobar', 7)
|
||||
call DoTest("fo\<BS>\<C-P>", 'foobar', 7)
|
||||
|
||||
" <C-Y> to accept preinserted text
|
||||
inoremap <buffer><F6> <C-R>=pumvisible()<CR>
|
||||
call DoTest("zar\<C-O>0f", 'foozar', 2)
|
||||
call DoTest("zar\<C-O>0f\<C-Y>", 'foozar', 4)
|
||||
call DoTest("zar\<C-O>0f\<C-Y>\<F6>", 'foo1zar', 5)
|
||||
call DoTest("zar\<C-O>0f\<C-Y>\<BS>", 'foozar', 3)
|
||||
call DoTest("zar\<C-O>0f\<C-Y>\<BS>\<F6>", 'fo1zar', 4)
|
||||
|
||||
" Select items in menu
|
||||
call DoTest("zar\<C-O>0f\<C-N>", 'foozbarzar', 8)
|
||||
call DoTest("zar\<C-O>0f\<C-N>\<C-N>", 'foobarzar', 7)
|
||||
call DoTest("zar\<C-O>0f\<C-N>\<C-N>\<C-N>", 'foozar', 2)
|
||||
@@ -5778,6 +5923,34 @@ func Test_autocomplete_completeopt_preinsert()
|
||||
call DoTest("f", 'f', 2)
|
||||
set cot-=fuzzy
|
||||
|
||||
" preinserted()
|
||||
call DoTest("f", 'foo', 2)
|
||||
call assert_equal(1, g:preinserted)
|
||||
call assert_equal(0, preinserted())
|
||||
call DoTest("f\<BS>", '', 1)
|
||||
call assert_equal(0, g:preinserted)
|
||||
call DoTest("f ", 'f ', 3)
|
||||
call assert_equal(0, g:preinserted)
|
||||
call DoTest("foob", 'foobar', 5)
|
||||
call assert_equal(1, g:preinserted)
|
||||
call DoTest("foob\<BS>\<BS>x", 'fox', 4)
|
||||
call assert_equal(0, g:preinserted)
|
||||
|
||||
" Complete non-keyword
|
||||
func Omni(findstart, base)
|
||||
if a:findstart
|
||||
return 5
|
||||
else
|
||||
return ['xyz:']
|
||||
endif
|
||||
endfunc
|
||||
set omnifunc=Omni
|
||||
set cpt+=o
|
||||
call DoTest("xyz:", "xyz:xyz:", 5)
|
||||
call DoTest("xyz:\<BS>\<BS>", "xyz:", 3)
|
||||
set omnifunc& cpt&
|
||||
delfunc Omni
|
||||
|
||||
" leader should match prefix of inserted word
|
||||
%delete
|
||||
set smartcase ignorecase
|
||||
@@ -5788,6 +5961,16 @@ func Test_autocomplete_completeopt_preinsert()
|
||||
call assert_equal('FOO', g:line)
|
||||
set smartcase& ignorecase&
|
||||
|
||||
" avoid repeating text that is already present after the cursor
|
||||
%delete
|
||||
call setline(1, ["foobarbaz", ""])
|
||||
call feedkeys($"G", 'tx')
|
||||
call DoTest("baz\<C-O>0f", "foobarbaz", 2)
|
||||
call feedkeys($"Sxfoozar\<CR>\<Esc>", 'tx')
|
||||
call DoTest("baz\<C-O>0f", "foobarbaz", 2)
|
||||
call feedkeys($"Sfoozar\<CR>\<Esc>", 'tx')
|
||||
call DoTest("baz\<C-O>0f", "foobaz", 2)
|
||||
|
||||
" Verify that redo (dot) works
|
||||
%delete
|
||||
call setline(1, ["foobar", "foozbar", "foobaz", "changed", "change"])
|
||||
@@ -5824,7 +6007,7 @@ func Test_autocomplete_completeopt_preinsert()
|
||||
call assert_equal('', v:errmsg)
|
||||
endfunc
|
||||
|
||||
" Check that switching buffer with "preinsert" doesn't corrupt undo.
|
||||
" Check that switching buffer with "longest" doesn't corrupt undo.
|
||||
new
|
||||
setlocal bufhidden=wipe
|
||||
inoremap <buffer> <F2> <Cmd>enew!<CR>
|
||||
@@ -5832,7 +6015,7 @@ func Test_autocomplete_completeopt_preinsert()
|
||||
bwipe!
|
||||
call CheckUndo()
|
||||
|
||||
" Check that closing window with "preinsert" doesn't corrupt undo.
|
||||
" Check that closing window with "longest" doesn't corrupt undo.
|
||||
new
|
||||
setlocal bufhidden=wipe
|
||||
inoremap <buffer> <F2> <Cmd>close!<CR>
|
||||
|
Reference in New Issue
Block a user