mirror of
https://github.com/neovim/neovim.git
synced 2025-09-26 04:58:33 +00:00
vim-patch:9.1.1742: complete: preinsert does not work well with 'autocomplete' (#35692)
Problem: complete: preinsert does not work well with preinsert
Solution: Make "preinsert" completeopt value work with autocompletion
(Girish Palya)
This change extends Insert mode autocompletion so that 'preinsert' also
works when 'autocomplete' is enabled.
Try: `:set ac cot=preinsert`
See `:help 'cot'` for more details.
closes: vim/vim#18213
fa6fd41a94
Co-authored-by: Girish Palya <girishji@gmail.com>
This commit is contained in:
@@ -61,6 +61,7 @@ hi('PmenuMatch', { link = 'Pmenu' })
|
||||
hi('PmenuMatchSel', { link = 'PmenuSel' })
|
||||
hi('PmenuExtra', { link = 'Pmenu' })
|
||||
hi('PmenuExtraSel', { link = 'PmenuSel' })
|
||||
hi('PreInsert', { link = 'Added' })
|
||||
hi('ComplMatchIns', {})
|
||||
hi('ComplHint', { link = 'NonText' })
|
||||
hi('ComplHintMore', { link = 'MoreMsg' })
|
||||
|
@@ -1133,7 +1133,26 @@ any of them at any time by typing |CTRL-X|, which temporarily suspends
|
||||
autocompletion. To use |i_CTRL-N| or |i_CTRL-X_CTRL-N| specifically, press
|
||||
|CTRL-E| first to dismiss the popup menu (see |complete_CTRL-E|).
|
||||
|
||||
See also 'autocomplete', 'autocompletetimeout' and 'autocompletedelay'.
|
||||
*ins-autocompletion-example*
|
||||
Example setup~
|
||||
A typical configuration for automatic completion with a popup menu: >
|
||||
set autocomplete
|
||||
set complete=.^5,w^5,b^5,u^5
|
||||
set completeopt=popup
|
||||
|
||||
inoremap <silent><expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"
|
||||
inoremap <silent><expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
|
||||
<
|
||||
This enables automatic completion with suggestions from the current buffer,
|
||||
other windows, and listed buffers, displayed in a popup menu. Each source is
|
||||
limited to 5 candidates. <Tab> and <S-Tab> move through the items when the
|
||||
menu is visible. Optionally, add "preinsert" to 'completeopt' to insert the
|
||||
longest common prefix automatically. You can also add other completion
|
||||
sources to 'complete' as needed.
|
||||
|
||||
See also 'autocomplete', 'autocompletedelay' and 'autocompletetimeout'.
|
||||
|
||||
For command-line autocompletion, see |cmdline-autocompletion|.
|
||||
|
||||
To get LSP-driven auto-completion, see |lsp-completion|.
|
||||
|
||||
|
@@ -1516,10 +1516,11 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
*'complete'* *'cpt'* *E535*
|
||||
'complete' 'cpt' string (default ".,w,b,u,t")
|
||||
local to buffer
|
||||
This option specifies how keyword completion |ins-completion| works
|
||||
when CTRL-P or CTRL-N are used. It is also used for whole-line
|
||||
completion |i_CTRL-X_CTRL-L|. It indicates the type of completion
|
||||
and the places to scan. It is a comma-separated list of flags:
|
||||
This option controls how completion |ins-completion| behaves when
|
||||
using CTRL-P, CTRL-N, or |ins-autocompletion|. It is also used for
|
||||
whole-line completion |i_CTRL-X_CTRL-L|. It indicates the type of
|
||||
completion and the places to scan. It is a comma-separated list of
|
||||
flags:
|
||||
. scan the current buffer ('wrapscan' is ignored)
|
||||
w scan buffers from other windows
|
||||
b scan other loaded buffers that are in the buffer list
|
||||
@@ -1564,9 +1565,11 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
(gzipped files for example). Unloaded buffers are not scanned for
|
||||
whole-line completion.
|
||||
|
||||
As you can see, CTRL-N and CTRL-P can be used to do any 'iskeyword'-
|
||||
based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns
|
||||
|i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions).
|
||||
CTRL-N, CTRL-P, and |ins-autocompletion| can be used for any
|
||||
'iskeyword'-based completion (dictionary |i_CTRL-X_CTRL-K|, included
|
||||
patterns |i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]|, and normal
|
||||
expansions). With the "F" and "o" flags in 'complete', non-keywords
|
||||
can also be completed.
|
||||
|
||||
An optional match limit can be specified for a completion source by
|
||||
appending a caret ("^") followed by a {count} to the source flag.
|
||||
@@ -1672,17 +1675,22 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
with "menu" or "menuone". Overrides "preview".
|
||||
|
||||
preinsert
|
||||
Preinsert the portion of the first candidate word that is
|
||||
not part of the current completion leader and using the
|
||||
|hl-ComplMatchIns| highlight group. In order for it to
|
||||
work, "fuzzy" must not be set and "menuone" must be set.
|
||||
When autocompletion is not enabled, inserts the part of the
|
||||
first candidate word beyond the current completion leader,
|
||||
highlighted with |hl-ComplMatchIns|. The cursor does not
|
||||
move. Requires "fuzzy" unset and "menuone" in 'completeopt'.
|
||||
|
||||
When 'autocomplete' is enabled, inserts the longest common
|
||||
prefix of matches (from all shown items or buffer-specific
|
||||
matches), highlighted with |hl-PreInsert|. This occurs only
|
||||
when no menu item is selected. Press CTRL-Y to accept.
|
||||
|
||||
preview Show extra information about the currently selected
|
||||
completion in the preview window. Only works in
|
||||
combination with "menu" or "menuone".
|
||||
|
||||
Only "fuzzy", "popup" and "preview" have an effect when 'autocomplete'
|
||||
is enabled.
|
||||
Only "fuzzy", "popup", "preinsert" and "preview" have an effect when
|
||||
'autocomplete' is enabled.
|
||||
|
||||
This option does not apply to |cmdline-completion|. See 'wildoptions'
|
||||
for that.
|
||||
|
@@ -5353,6 +5353,8 @@ PmenuMatchSel Popup menu: Matched text in selected item. Combined with
|
||||
|hl-PmenuMatch| and |hl-PmenuSel|.
|
||||
*hl-ComplMatchIns*
|
||||
ComplMatchIns Matched text of the currently inserted completion.
|
||||
*hl-PreInsert*
|
||||
PreInsert Text inserted during autocompletion when "preinsert".
|
||||
*hl-ComplHint*
|
||||
ComplHint Virtual text of the currently selected completion.
|
||||
*hl-ComplHintMore*
|
||||
|
34
runtime/lua/vim/_meta/options.lua
generated
34
runtime/lua/vim/_meta/options.lua
generated
@@ -1044,10 +1044,11 @@ vim.o.cms = vim.o.commentstring
|
||||
vim.bo.commentstring = vim.o.commentstring
|
||||
vim.bo.cms = vim.bo.commentstring
|
||||
|
||||
--- This option specifies how keyword completion `ins-completion` works
|
||||
--- when CTRL-P or CTRL-N are used. It is also used for whole-line
|
||||
--- completion `i_CTRL-X_CTRL-L`. It indicates the type of completion
|
||||
--- and the places to scan. It is a comma-separated list of flags:
|
||||
--- This option controls how completion `ins-completion` behaves when
|
||||
--- using CTRL-P, CTRL-N, or `ins-autocompletion`. It is also used for
|
||||
--- whole-line completion `i_CTRL-X_CTRL-L`. It indicates the type of
|
||||
--- completion and the places to scan. It is a comma-separated list of
|
||||
--- flags:
|
||||
--- . scan the current buffer ('wrapscan' is ignored)
|
||||
--- w scan buffers from other windows
|
||||
--- b scan other loaded buffers that are in the buffer list
|
||||
@@ -1095,9 +1096,11 @@ vim.bo.cms = vim.bo.commentstring
|
||||
--- (gzipped files for example). Unloaded buffers are not scanned for
|
||||
--- whole-line completion.
|
||||
---
|
||||
--- As you can see, CTRL-N and CTRL-P can be used to do any 'iskeyword'-
|
||||
--- based expansion (e.g., dictionary `i_CTRL-X_CTRL-K`, included patterns
|
||||
--- `i_CTRL-X_CTRL-I`, tags `i_CTRL-X_CTRL-]` and normal expansions).
|
||||
--- CTRL-N, CTRL-P, and `ins-autocompletion` can be used for any
|
||||
--- 'iskeyword'-based completion (dictionary `i_CTRL-X_CTRL-K`, included
|
||||
--- patterns `i_CTRL-X_CTRL-I`, tags `i_CTRL-X_CTRL-]`, and normal
|
||||
--- expansions). With the "F" and "o" flags in 'complete', non-keywords
|
||||
--- can also be completed.
|
||||
---
|
||||
--- An optional match limit can be specified for a completion source by
|
||||
--- appending a caret ("^") followed by a {count} to the source flag.
|
||||
@@ -1215,17 +1218,22 @@ vim.go.cia = vim.go.completeitemalign
|
||||
--- with "menu" or "menuone". Overrides "preview".
|
||||
---
|
||||
--- preinsert
|
||||
--- Preinsert the portion of the first candidate word that is
|
||||
--- not part of the current completion leader and using the
|
||||
--- `hl-ComplMatchIns` highlight group. In order for it to
|
||||
--- work, "fuzzy" must not be set and "menuone" must be set.
|
||||
--- When autocompletion is not enabled, inserts the part of the
|
||||
--- first candidate word beyond the current completion leader,
|
||||
--- highlighted with `hl-ComplMatchIns`. The cursor does not
|
||||
--- move. Requires "fuzzy" unset and "menuone" in 'completeopt'.
|
||||
---
|
||||
--- When 'autocomplete' is enabled, inserts the longest common
|
||||
--- prefix of matches (from all shown items or buffer-specific
|
||||
--- matches), highlighted with `hl-PreInsert`. This occurs only
|
||||
--- when no menu item is selected. Press CTRL-Y to accept.
|
||||
---
|
||||
--- preview Show extra information about the currently selected
|
||||
--- completion in the preview window. Only works in
|
||||
--- combination with "menu" or "menuone".
|
||||
---
|
||||
--- Only "fuzzy", "popup" and "preview" have an effect when 'autocomplete'
|
||||
--- is enabled.
|
||||
--- Only "fuzzy", "popup", "preinsert" and "preview" have an effect when
|
||||
--- 'autocomplete' is enabled.
|
||||
---
|
||||
--- This option does not apply to `cmdline-completion`. See 'wildoptions'
|
||||
--- for that.
|
||||
|
@@ -103,8 +103,8 @@ syn keyword vimGroup contained Added Bold BoldItalic Boolean Changed Character C
|
||||
|
||||
" Default highlighting groups {{{2
|
||||
" GEN_SYN_VIM: vimHLGroup, START_STR='syn keyword vimHLGroup contained', END_STR=''
|
||||
syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit DiffText DiffTextAdd PmenuSbar TabLineSel TabLineFill Cursor lCursor QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC CursorIM
|
||||
syn keyword vimHLGroup contained ComplMatchIns LineNrAbove LineNrBelow MsgArea User1 User2 User3 User4 User5 User6 User7 User8 User9
|
||||
syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit DiffText DiffTextAdd PmenuSbar TabLineSel TabLineFill Cursor lCursor QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel PreInsert Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC
|
||||
syn keyword vimHLGroup contained CursorIM ComplMatchIns LineNrAbove LineNrBelow MsgArea User1 User2 User3 User4 User5 User6 User7 User8 User9
|
||||
syn match vimHLGroup contained "\<Conceal\>"
|
||||
syn keyword vimOnlyHLGroup contained Menu PopupSelected MessageWindow PopupNotification Scrollbar Terminal ToolbarButton ToolbarLine Tooltip VisualNOS
|
||||
syn keyword nvimHLGroup contained FloatBorder FloatFooter FloatTitle MsgSeparator NormalFloat NormalNC Substitute TermCursor VisualNC Whitespace WinBar WinBarNC WinSeparator
|
||||
|
@@ -605,7 +605,11 @@ 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);
|
||||
ins_compl_insert(false);
|
||||
if (ins_compl_has_preinsert() && ins_compl_has_autocomplete()) {
|
||||
(void)ins_compl_insert(false, true);
|
||||
} else {
|
||||
(void)ins_compl_insert(false, false);
|
||||
}
|
||||
} else if (ascii_iswhite_nl_or_nul(s->c) && ins_compl_preinsert_effect()) {
|
||||
// Delete preinserted text when typing special chars
|
||||
ins_compl_delete(false);
|
||||
|
@@ -85,6 +85,7 @@ EXTERN const char *hlf_names[] INIT( = {
|
||||
[HLF_BFOOTER] = "FloatFooter",
|
||||
[HLF_TS] = "StatusLineTerm",
|
||||
[HLF_TSNC] = "StatusLineTermNC",
|
||||
[HLF_PRE] = "PreInsert",
|
||||
});
|
||||
|
||||
EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context.
|
||||
|
@@ -133,6 +133,7 @@ typedef enum {
|
||||
HLF_SE, ///< stderr messages (from shell)
|
||||
HLF_SO, ///< stdout messages (from shell)
|
||||
HLF_OK, ///< OK message
|
||||
HLF_PRE, ///< "preinsert" in 'completeopt'
|
||||
HLF_COUNT, ///< MUST be the last one
|
||||
} hlf_T;
|
||||
|
||||
|
@@ -174,6 +174,7 @@ static const char *highlight_init_both[] = {
|
||||
"default link PmenuKind Pmenu",
|
||||
"default link PmenuKindSel PmenuSel",
|
||||
"default link PmenuSbar Pmenu",
|
||||
"default link PreInsert Added",
|
||||
"default link ComplMatchIns NONE",
|
||||
"default link ComplHint NonText",
|
||||
"default link ComplHintMore MoreMsg",
|
||||
|
@@ -240,8 +240,9 @@ static String compl_leader = STRING_INIT;
|
||||
|
||||
static bool compl_get_longest = false; ///< put longest common string in compl_leader
|
||||
|
||||
/// Selected one of the matches. When false the match was edited or using the
|
||||
/// longest common string.
|
||||
/// This flag indicates that one of the items in the match list is currently
|
||||
/// selected. False when no match is selected or the match was edited or using
|
||||
/// the longest common string.
|
||||
static bool compl_used_match;
|
||||
|
||||
/// didn't finish finding completions.
|
||||
@@ -296,6 +297,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
|
||||
|
||||
// Halve the current completion timeout, simulating exponential decay.
|
||||
#define COMPL_MIN_TIMEOUT_MS 5
|
||||
@@ -333,10 +335,11 @@ static int *compl_fuzzy_scores;
|
||||
|
||||
/// Define the structure for completion source (in 'cpt' option) information
|
||||
typedef struct cpt_source_T {
|
||||
bool cs_refresh_always; ///< Whether 'refresh:always' is set for func
|
||||
int cs_startcol; ///< Start column returned by func
|
||||
int cs_max_matches; ///< Max items to display from this source
|
||||
bool cs_refresh_always; ///< Whether 'refresh:always' is set for func
|
||||
int cs_startcol; ///< Start column returned by func
|
||||
int cs_max_matches; ///< Max items to display from this source
|
||||
uint64_t compl_start_tv; ///< Timestamp when match collection starts
|
||||
char cs_flag; ///< Flag indicating the type of source
|
||||
} cpt_source_T;
|
||||
|
||||
/// Pointer to the array of completion sources
|
||||
@@ -1094,7 +1097,12 @@ static void ins_compl_insert_bytes(char *p, int len)
|
||||
int ins_compl_col_range_attr(linenr_T lnum, int col)
|
||||
{
|
||||
int attr;
|
||||
if ((get_cot_flags() & kOptCotFlagFuzzy) || (attr = syn_name2attr("ComplMatchIns")) == 0) {
|
||||
if ((get_cot_flags() & kOptCotFlagFuzzy)
|
||||
|| (!compl_autocomplete
|
||||
&& (attr = syn_name2attr("ComplMatchIns")) == 0)
|
||||
|| (compl_autocomplete
|
||||
&& (!compl_autocomplete_preinsert
|
||||
|| (attr = syn_name2attr("PreInsert")) == 0))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1520,14 +1528,14 @@ static int ins_compl_build_pum(void)
|
||||
do {
|
||||
comp->cp_in_match_array = false;
|
||||
|
||||
String *leader = get_leader_for_startcol(comp, true);
|
||||
|
||||
// Apply 'smartcase' behavior during normal mode
|
||||
if (ctrl_x_mode_normal() && !p_inf && compl_leader.data
|
||||
&& !ignorecase(compl_leader.data) && !fuzzy_filter) {
|
||||
if (ctrl_x_mode_normal() && !p_inf && leader->data
|
||||
&& !ignorecase(leader->data) && !fuzzy_filter) {
|
||||
comp->cp_flags &= ~CP_ICASE;
|
||||
}
|
||||
|
||||
String *leader = get_leader_for_startcol(comp, true);
|
||||
|
||||
if (!match_at_original_text(comp)
|
||||
&& (leader->data == NULL
|
||||
|| ins_compl_equal(comp, leader->data, leader->size)
|
||||
@@ -2118,10 +2126,14 @@ int ins_compl_len(void)
|
||||
|
||||
/// Return true when the 'completeopt' "preinsert" flag is in effect,
|
||||
/// otherwise return false.
|
||||
static bool ins_compl_has_preinsert(void)
|
||||
bool ins_compl_has_preinsert(void)
|
||||
{
|
||||
return (get_cot_flags() & (kOptCotFlagFuzzy|kOptCotFlagPreinsert|kOptCotFlagMenuone))
|
||||
== (kOptCotFlagPreinsert|kOptCotFlagMenuone) && !compl_autocomplete;
|
||||
unsigned cur_cot_flags = get_cot_flags();
|
||||
return (!compl_autocomplete
|
||||
? (cur_cot_flags & (kOptCotFlagPreinsert|kOptCotFlagFuzzy|kOptCotFlagMenuone))
|
||||
== (kOptCotFlagPreinsert|kOptCotFlagMenuone)
|
||||
: (cur_cot_flags & (kOptCotFlagPreinsert|kOptCotFlagFuzzy))
|
||||
== kOptCotFlagPreinsert);
|
||||
}
|
||||
|
||||
/// Returns true if the pre-insert effect is valid and the cursor is within
|
||||
@@ -2135,6 +2147,12 @@ bool ins_compl_preinsert_effect(void)
|
||||
return curwin->w_cursor.col < compl_ins_end_col;
|
||||
}
|
||||
|
||||
/// Returns true if autocompletion is active.
|
||||
bool ins_compl_has_autocomplete(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
|
||||
@@ -2260,11 +2278,20 @@ 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) {
|
||||
ins_compl_insert(true);
|
||||
if (compl_started && compl_autocomplete && !ins_compl_preinsert_effect()) {
|
||||
if (ins_compl_insert(true, true) != OK) {
|
||||
(void)ins_compl_insert(false, false);
|
||||
} else {
|
||||
compl_autocomplete_preinsert = true;
|
||||
}
|
||||
} else {
|
||||
(void)ins_compl_insert(true, false);
|
||||
}
|
||||
}
|
||||
// Don't let Enter select when use user function and refresh_always is set
|
||||
if (ins_compl_refresh_always()) {
|
||||
@@ -2626,6 +2653,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
|
||||
compl_autocomplete = false;
|
||||
compl_from_nonkeyword = false;
|
||||
compl_best_matches = 0;
|
||||
compl_ins_end_col = 0;
|
||||
|
||||
if (c == Ctrl_C && cmdwin_type != 0) {
|
||||
// Avoid the popup menu remains displayed when leaving the
|
||||
@@ -4345,11 +4373,11 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
|
||||
}
|
||||
|
||||
if (!in_fuzzy_collect) {
|
||||
ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
|
||||
&len, &cont_s_ipos);
|
||||
ptr = ins_compl_get_next_word_or_line(st->ins_buf,
|
||||
st->cur_match_pos, &len, &cont_s_ipos);
|
||||
}
|
||||
if (ptr == NULL
|
||||
|| (ins_compl_has_preinsert() && strcmp(ptr, compl_pattern.data) == 0)) {
|
||||
if (ptr == NULL || (!compl_autocomplete && ins_compl_has_preinsert()
|
||||
&& strcmp(ptr, compl_pattern.data) == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4910,8 +4938,8 @@ void ins_compl_delete(bool new_leader)
|
||||
// In insert mode: Delete the typed part.
|
||||
// In replace mode: Put the old characters back, if any.
|
||||
int col = compl_col + (compl_status_adding() ? compl_length : orig_col);
|
||||
bool has_preinsert = ins_compl_preinsert_effect();
|
||||
if (has_preinsert) {
|
||||
|
||||
if (ins_compl_preinsert_effect()) {
|
||||
col += (int)ins_compl_leader_len();
|
||||
curwin->w_cursor.col = compl_ins_end_col;
|
||||
}
|
||||
@@ -4990,10 +5018,95 @@ static void ins_compl_expand_multiple(char *str)
|
||||
compl_ins_end_col = curwin->w_cursor.col;
|
||||
}
|
||||
|
||||
/// Find the longest common prefix among the current completion matches.
|
||||
/// Returns a pointer to the first match string, with *prefix_len set to
|
||||
/// the length of the common prefix.
|
||||
/// If "curbuf_only" is true, restrict matches to the current buffer
|
||||
/// ('.' source in 'complete').
|
||||
static char *find_common_prefix(size_t *prefix_len, bool curbuf_only)
|
||||
{
|
||||
bool is_cpt_completion = (cpt_sources_array != NULL);
|
||||
|
||||
if (!is_cpt_completion) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int *match_count = xcalloc((size_t)cpt_sources_count, sizeof(int));
|
||||
|
||||
(void)get_leader_for_startcol(NULL, true); // Clear the cache
|
||||
|
||||
compl_T *compl = compl_first_match;
|
||||
char *first = NULL;
|
||||
int len = -1;
|
||||
do {
|
||||
String *leader = get_leader_for_startcol(compl, true);
|
||||
|
||||
// Apply 'smartcase' behavior during normal mode
|
||||
if (ctrl_x_mode_normal() && !p_inf && leader->data && !ignorecase(leader->data)) {
|
||||
compl->cp_flags &= ~CP_ICASE;
|
||||
}
|
||||
|
||||
if (!match_at_original_text(compl)
|
||||
&& (leader->data == NULL
|
||||
|| ins_compl_equal(compl, leader->data, leader->size))) {
|
||||
// Limit number of items from each source if max_items is set.
|
||||
bool match_limit_exceeded = false;
|
||||
int cur_source = compl->cp_cpt_source_idx;
|
||||
|
||||
if (cur_source != -1) {
|
||||
match_count[cur_source]++;
|
||||
int max_matches = cpt_sources_array[cur_source].cs_max_matches;
|
||||
if (max_matches > 0 && match_count[cur_source] > max_matches) {
|
||||
match_limit_exceeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match_limit_exceeded
|
||||
&& (!curbuf_only || cpt_sources_array[cur_source].cs_flag == '.')) {
|
||||
if (first == NULL) {
|
||||
first = compl->cp_str.data;
|
||||
len = (int)strlen(first);
|
||||
} else {
|
||||
int j = 0; // count in bytes
|
||||
char *s1 = first;
|
||||
char *s2 = compl->cp_str.data;
|
||||
|
||||
while (j < len && *s1 != NUL && *s2 != NUL) {
|
||||
if (MB_BYTE2LEN((uint8_t)(*s1)) != MB_BYTE2LEN((uint8_t)(*s2))
|
||||
|| memcmp(s1, s2, MB_BYTE2LEN((uint8_t)(*s1))) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
j += MB_BYTE2LEN((uint8_t)(*s1));
|
||||
MB_PTR_ADV(s1);
|
||||
MB_PTR_ADV(s2);
|
||||
}
|
||||
len = j;
|
||||
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
compl = compl->cp_next;
|
||||
} while (compl != NULL && !is_first_match(compl));
|
||||
|
||||
xfree(match_count);
|
||||
|
||||
if (len > get_compl_len()) {
|
||||
*prefix_len = (size_t)len;
|
||||
return first;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
void ins_compl_insert(bool move_cursor)
|
||||
/// 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)
|
||||
{
|
||||
int compl_len = get_compl_len();
|
||||
bool preinsert = ins_compl_has_preinsert();
|
||||
@@ -5002,10 +5115,18 @@ void ins_compl_insert(bool move_cursor)
|
||||
size_t leader_len = ins_compl_leader_len();
|
||||
char *has_multiple = strchr(cp_str, '\n');
|
||||
|
||||
// Since completion sources may provide matches with varying start
|
||||
// positions, insert only the portion of the match that corresponds to the
|
||||
// intended replacement range.
|
||||
if (cpt_sources_array != NULL) {
|
||||
if (preinsert_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;
|
||||
}
|
||||
}
|
||||
} else if (cpt_sources_array != NULL) {
|
||||
// Since completion sources may provide matches with varying start
|
||||
// positions, insert only the portion of the match that corresponds to the
|
||||
// intended replacement range.
|
||||
int cpt_idx = compl_shown_match->cp_cpt_source_idx;
|
||||
if (cpt_idx >= 0 && compl_col >= 0) {
|
||||
int startcol = cpt_sources_array[cpt_idx].cs_startcol;
|
||||
@@ -5025,7 +5146,8 @@ void ins_compl_insert(bool move_cursor)
|
||||
if (has_multiple) {
|
||||
ins_compl_expand_multiple(cp_str + compl_len);
|
||||
} else {
|
||||
ins_compl_insert_bytes(cp_str + compl_len, -1);
|
||||
ins_compl_insert_bytes(cp_str + compl_len,
|
||||
preinsert_prefix ? (int)cp_str_len - compl_len : -1);
|
||||
if (preinsert && move_cursor) {
|
||||
curwin->w_cursor.col -= (colnr_T)(cp_str_len - leader_len);
|
||||
}
|
||||
@@ -5035,6 +5157,7 @@ void ins_compl_insert(bool move_cursor)
|
||||
|
||||
dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
|
||||
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// show the file name for the completion match (if any). Truncate the file
|
||||
@@ -5258,14 +5381,35 @@ 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 && !compl_preinsert) {
|
||||
ins_compl_insert_bytes(compl_orig_text.data + get_compl_len(), -1);
|
||||
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);
|
||||
}
|
||||
compl_used_match = false;
|
||||
restore_orig_extmarks();
|
||||
} else if (insert_match) {
|
||||
if (!compl_get_longest || compl_used_match) {
|
||||
ins_compl_insert(true);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
assert(compl_leader.data != NULL);
|
||||
ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
|
||||
@@ -6263,6 +6407,7 @@ static void setup_cpt_sources(void)
|
||||
p++;
|
||||
}
|
||||
if (*p) { // If not end of string, count this segment
|
||||
cpt_sources_array[idx].cs_flag = *p;
|
||||
memset(buf, 0, LSIZE);
|
||||
size_t slen = copy_option_part(&p, buf, LSIZE, ","); // Advance p
|
||||
if (slen > 0) {
|
||||
|
@@ -19,7 +19,7 @@
|
||||
"R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind," \
|
||||
"]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel," \
|
||||
"_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm," \
|
||||
"Z:StatusLineTermNC,g:MsgArea,h:ComplMatchIns,0:Whitespace,I:NormalNC"
|
||||
"Z:StatusLineTermNC,g:MsgArea,h:ComplMatchIns,0:Whitespace,I:PreInsert"
|
||||
|
||||
// Default values for 'errorformat'.
|
||||
// The "%f|%l| %m" one is used for when the contents of the quickfix window is
|
||||
|
@@ -1483,10 +1483,11 @@ local options = {
|
||||
values = { '.', 'w', 'b', 'u', 'k', 'kspell', 's', 'i', 'd', ']', 't', 'U', 'f', 'F', 'o' },
|
||||
deny_duplicates = true,
|
||||
desc = [=[
|
||||
This option specifies how keyword completion |ins-completion| works
|
||||
when CTRL-P or CTRL-N are used. It is also used for whole-line
|
||||
completion |i_CTRL-X_CTRL-L|. It indicates the type of completion
|
||||
and the places to scan. It is a comma-separated list of flags:
|
||||
This option controls how completion |ins-completion| behaves when
|
||||
using CTRL-P, CTRL-N, or |ins-autocompletion|. It is also used for
|
||||
whole-line completion |i_CTRL-X_CTRL-L|. It indicates the type of
|
||||
completion and the places to scan. It is a comma-separated list of
|
||||
flags:
|
||||
. scan the current buffer ('wrapscan' is ignored)
|
||||
w scan buffers from other windows
|
||||
b scan other loaded buffers that are in the buffer list
|
||||
@@ -1531,9 +1532,11 @@ local options = {
|
||||
(gzipped files for example). Unloaded buffers are not scanned for
|
||||
whole-line completion.
|
||||
|
||||
As you can see, CTRL-N and CTRL-P can be used to do any 'iskeyword'-
|
||||
based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns
|
||||
|i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions).
|
||||
CTRL-N, CTRL-P, and |ins-autocompletion| can be used for any
|
||||
'iskeyword'-based completion (dictionary |i_CTRL-X_CTRL-K|, included
|
||||
patterns |i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]|, and normal
|
||||
expansions). With the "F" and "o" flags in 'complete', non-keywords
|
||||
can also be completed.
|
||||
|
||||
An optional match limit can be specified for a completion source by
|
||||
appending a caret ("^") followed by a {count} to the source flag.
|
||||
@@ -1697,17 +1700,22 @@ local options = {
|
||||
with "menu" or "menuone". Overrides "preview".
|
||||
|
||||
preinsert
|
||||
Preinsert the portion of the first candidate word that is
|
||||
not part of the current completion leader and using the
|
||||
|hl-ComplMatchIns| highlight group. In order for it to
|
||||
work, "fuzzy" must not be set and "menuone" must be set.
|
||||
When autocompletion is not enabled, inserts the part of the
|
||||
first candidate word beyond the current completion leader,
|
||||
highlighted with |hl-ComplMatchIns|. The cursor does not
|
||||
move. Requires "fuzzy" unset and "menuone" in 'completeopt'.
|
||||
|
||||
When 'autocomplete' is enabled, inserts the longest common
|
||||
prefix of matches (from all shown items or buffer-specific
|
||||
matches), highlighted with |hl-PreInsert|. This occurs only
|
||||
when no menu item is selected. Press CTRL-Y to accept.
|
||||
|
||||
preview Show extra information about the currently selected
|
||||
completion in the preview window. Only works in
|
||||
combination with "menu" or "menuone".
|
||||
|
||||
Only "fuzzy", "popup" and "preview" have an effect when 'autocomplete'
|
||||
is enabled.
|
||||
Only "fuzzy", "popup", "preinsert" and "preview" have an effect when
|
||||
'autocomplete' is enabled.
|
||||
|
||||
This option does not apply to |cmdline-completion|. See 'wildoptions'
|
||||
for that.
|
||||
|
@@ -262,7 +262,7 @@ describe('ui/cursor', function()
|
||||
m.attr = { background = Screen.colors.DarkGray }
|
||||
end
|
||||
if m.id_lm then
|
||||
m.id_lm = 76
|
||||
m.id_lm = 77
|
||||
m.attr_lm = {}
|
||||
end
|
||||
end
|
||||
|
@@ -475,12 +475,13 @@ func Test_highlight_completion()
|
||||
call assert_equal('"hi clear', getreg(':'))
|
||||
|
||||
" A cleared group does not show up in completions.
|
||||
hi clear Added
|
||||
hi Anders ctermfg=green
|
||||
call assert_equal(['Aardig', 'Added', 'Anders'], getcompletion('A', 'highlight'))
|
||||
call assert_equal(['Aardig', 'Anders'], getcompletion('A', 'highlight'))
|
||||
hi clear Aardig
|
||||
call assert_equal(['Added', 'Anders'], getcompletion('A', 'highlight'))
|
||||
call assert_equal(['Anders'], getcompletion('A', 'highlight'))
|
||||
hi clear Anders
|
||||
call assert_equal(['Added'], getcompletion('A', 'highlight'))
|
||||
call assert_equal([], getcompletion('A', 'highlight'))
|
||||
endfunc
|
||||
|
||||
func Test_getcompletion()
|
||||
|
@@ -5472,7 +5472,7 @@ func Test_autocomplete_timer()
|
||||
call assert_equal(-1, b:selected)
|
||||
|
||||
" Test 7: Following 'cot' option values have no effect
|
||||
set completeopt=menu,menuone,noselect,noinsert,longest,preinsert
|
||||
set completeopt=menu,menuone,noselect,noinsert,longest
|
||||
set complete=.,Ffunction('TestComplete'\\,\ [0\\,\ 0\\,\ 0])
|
||||
let g:CallCount = 0
|
||||
call feedkeys("Sab\<c-n>\<F2>\<F3>\<Esc>0", 'tx!')
|
||||
@@ -5691,4 +5691,109 @@ func Test_autocompletedelay()
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_autocomplete_completeopt_preinsert()
|
||||
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>
|
||||
set completeopt=preinsert autocomplete
|
||||
call setline(1, ["foobar", "foozbar"])
|
||||
call feedkeys("Go\<ESC>", 'tx')
|
||||
|
||||
func DoTest(typed, line, col)
|
||||
call feedkeys($"S{a:typed}\<F5>\<ESC>", 'tx')
|
||||
call assert_equal(a:line, g:line)
|
||||
call assert_equal(a:col, g:col)
|
||||
endfunc
|
||||
|
||||
call DoTest("f", 'foo', 2)
|
||||
call DoTest("fo", 'foo', 3)
|
||||
call DoTest("foo", 'foo', 4)
|
||||
call DoTest("foob", 'foobar', 5)
|
||||
call DoTest("foob\<BS>", 'foo', 4)
|
||||
call DoTest("foob\<BS>\<BS>", 'foo', 3)
|
||||
call DoTest("foo\<BS>", 'foo', 3)
|
||||
call DoTest("foo\<BS>\<BS>", 'foo', 2)
|
||||
call DoTest("foo\<BS>\<BS>\<BS>", '', 1)
|
||||
|
||||
call DoTest("foo \<BS>", 'foo', 4)
|
||||
call DoTest("foo \<BS>\<BS>", 'foo', 3)
|
||||
|
||||
call DoTest("f\<C-N>", 'foozbar', 8)
|
||||
call DoTest("f\<C-N>\<C-N>", 'foobar', 7)
|
||||
call DoTest("f\<C-N>\<C-N>\<C-N>", 'foo', 2)
|
||||
call DoTest("f\<C-N>\<C-N>\<C-N>\<C-N>", 'foozbar', 8)
|
||||
call DoTest("f\<C-N>\<C-N>\<C-N>\<C-N>\<C-P>", 'foo', 2)
|
||||
call DoTest("f\<C-N>\<C-N>\<C-N>\<C-N>\<C-P>\<C-P>", 'foobar', 7)
|
||||
call DoTest("f\<C-P>", 'foobar', 7)
|
||||
call DoTest("fo\<BS>\<C-P>", 'foobar', 7)
|
||||
|
||||
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>\<BS>", 'foozar', 3)
|
||||
|
||||
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)
|
||||
call DoTest("zar\<C-O>0f\<C-N>\<C-N>\<C-N>\<C-N>", 'foozbarzar', 8)
|
||||
call DoTest("zar\<C-O>0f\<C-N>\<C-N>\<C-N>\<C-N>\<C-P>", 'foozar', 2)
|
||||
call DoTest("zar\<C-O>0f\<C-N>\<C-N>\<C-N>\<C-N>\<C-P>\<C-P>", 'foobarzar', 7)
|
||||
call DoTest("zar\<C-O>0f\<C-P>", 'foobarzar', 7)
|
||||
|
||||
" Should not work with fuzzy
|
||||
set cot+=fuzzy
|
||||
call DoTest("f", 'f', 2)
|
||||
|
||||
%delete _
|
||||
let &l:undolevels = &l:undolevels
|
||||
normal! ifoo
|
||||
let &l:undolevels = &l:undolevels
|
||||
normal! ofoobar
|
||||
let &l:undolevels = &l:undolevels
|
||||
normal! ofoobaz
|
||||
let &l:undolevels = &l:undolevels
|
||||
|
||||
func CheckUndo()
|
||||
let g:errmsg = ''
|
||||
call assert_equal(['foo', 'foobar', 'foobaz'], getline(1, '$'))
|
||||
undo
|
||||
call assert_equal(['foo', 'foobar'], getline(1, '$'))
|
||||
undo
|
||||
call assert_equal(['foo'], getline(1, '$'))
|
||||
undo
|
||||
call assert_equal([''], getline(1, '$'))
|
||||
later 3
|
||||
call assert_equal(['foo', 'foobar', 'foobaz'], 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& autocomplete&
|
||||
delfunc GetLine
|
||||
delfunc DoTest
|
||||
call Ntest_override("char_avail", 0)
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
|
||||
|
Reference in New Issue
Block a user