mirror of
https://github.com/vim/vim
synced 2025-07-16 09:12:00 +00:00
patch 9.1.1056: Vim doesn't highlight to be inserted text when completing
Problem: Vim doesn't highlight to be inserted text when completing Solution: Add support for the "preinsert" 'completeopt' value (glepnir) Support automatically inserting the currently selected candidate word that does not belong to the latter part of the leader. fixes: #3433 closes: #16403 Signed-off-by: glepnir <glephunter@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
ec961b05dc
commit
edd4ac3e89
@ -1,4 +1,4 @@
|
||||
*options.txt* For Vim version 9.1. Last change: 2025 Jan 26
|
||||
*options.txt* For Vim version 9.1. Last change: 2025 Jan 29
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@ -2168,6 +2168,12 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
scores when "fuzzy" is enabled. Candidates will appear
|
||||
in their original order.
|
||||
|
||||
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. Does not work when
|
||||
"fuzzy" is also included.
|
||||
|
||||
*'completepopup'* *'cpp'*
|
||||
'completepopup' 'cpp' string (default empty)
|
||||
global
|
||||
|
@ -41625,7 +41625,9 @@ Changed~
|
||||
the "matches" key
|
||||
- |v:stacktrace| The stack trace of the exception most recently caught and
|
||||
not finished
|
||||
- New option value "nosort" for 'completeopt'
|
||||
- New option value for 'completeopt':
|
||||
"nosort" - do not sort completion results
|
||||
"preinsert" - highlight to be inserted values
|
||||
- add |dist#vim9#Launch()| and |dist#vim9#Open()| to the |vim-script-library|
|
||||
and decouple it from |netrw|
|
||||
- 'termguicolors' is automatically enabled if the terminal supports the RGB
|
||||
|
@ -690,8 +690,11 @@ edit(
|
||||
&& stop_arrow() == OK)
|
||||
{
|
||||
ins_compl_delete();
|
||||
ins_compl_insert(FALSE);
|
||||
ins_compl_insert(FALSE, FALSE);
|
||||
}
|
||||
// Delete preinserted text when typing special chars
|
||||
else if (IS_WHITE_NL_OR_NUL(c) && ins_compl_preinsert_effect())
|
||||
ins_compl_delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1946,6 +1946,28 @@ ins_compl_len(void)
|
||||
return compl_length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE when preinsert is set otherwise FALSE.
|
||||
*/
|
||||
static int
|
||||
ins_compl_has_preinsert(void)
|
||||
{
|
||||
return (get_cot_flags() & (COT_PREINSERT | COT_FUZZY)) == COT_PREINSERT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if the pre-insert effect is valid and the cursor is within
|
||||
* the `compl_ins_end_col` range.
|
||||
*/
|
||||
int
|
||||
ins_compl_preinsert_effect(void)
|
||||
{
|
||||
if (!ins_compl_has_preinsert())
|
||||
return FALSE;
|
||||
|
||||
return curwin->w_cursor.col < compl_ins_end_col;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete one character before the cursor and show the subset of the matches
|
||||
* that match the word that is now before the cursor.
|
||||
@ -1958,6 +1980,9 @@ ins_compl_bs(void)
|
||||
char_u *line;
|
||||
char_u *p;
|
||||
|
||||
if (ins_compl_preinsert_effect())
|
||||
ins_compl_delete();
|
||||
|
||||
line = ml_get_curline();
|
||||
p = line + curwin->w_cursor.col;
|
||||
MB_PTR_BACK(line, p);
|
||||
@ -2054,6 +2079,8 @@ ins_compl_new_leader(void)
|
||||
// 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.length > 0)
|
||||
ins_compl_insert(FALSE, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2079,6 +2106,9 @@ ins_compl_addleader(int c)
|
||||
{
|
||||
int cc;
|
||||
|
||||
if (ins_compl_preinsert_effect())
|
||||
ins_compl_delete();
|
||||
|
||||
if (stop_arrow() == FAIL)
|
||||
return;
|
||||
if (has_mbyte && (cc = (*mb_char2len)(c)) > 1)
|
||||
@ -3092,7 +3122,8 @@ set_completion(colnr_T startcol, list_T *list)
|
||||
compl_col = startcol;
|
||||
compl_length = (int)curwin->w_cursor.col - (int)startcol;
|
||||
// compl_pattern doesn't need to be set
|
||||
compl_orig_text.string = vim_strnsave(ml_get_curline() + compl_col, (size_t)compl_length);
|
||||
compl_orig_text.string = vim_strnsave(ml_get_curline() + compl_col,
|
||||
(size_t)compl_length);
|
||||
if (p_ic)
|
||||
flags |= CP_ICASE;
|
||||
if (compl_orig_text.string == NULL)
|
||||
@ -4305,11 +4336,16 @@ ins_compl_update_shown_match(void)
|
||||
void
|
||||
ins_compl_delete(void)
|
||||
{
|
||||
int col;
|
||||
|
||||
// In insert mode: Delete the typed part.
|
||||
// In replace mode: Put the old characters back, if any.
|
||||
col = compl_col + (compl_status_adding() ? compl_length : 0);
|
||||
int col = compl_col + (compl_status_adding() ? compl_length : 0);
|
||||
int has_preinsert = ins_compl_preinsert_effect();
|
||||
if (has_preinsert)
|
||||
{
|
||||
col = compl_col + ins_compl_leader_len() - compl_length;
|
||||
curwin->w_cursor.col = compl_ins_end_col;
|
||||
}
|
||||
|
||||
if ((int)curwin->w_cursor.col > col)
|
||||
{
|
||||
if (stop_arrow() == FAIL)
|
||||
@ -4330,17 +4366,26 @@ ins_compl_delete(void)
|
||||
/*
|
||||
* Insert the new text being completed.
|
||||
* "in_compl_func" is TRUE when called from complete_check().
|
||||
* "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(int in_compl_func)
|
||||
ins_compl_insert(int in_compl_func, int move_cursor)
|
||||
{
|
||||
int compl_len = get_compl_len();
|
||||
int compl_len = get_compl_len();
|
||||
int preinsert = ins_compl_has_preinsert();
|
||||
char_u *str = compl_shown_match->cp_str.string;
|
||||
int leader_len = ins_compl_leader_len();
|
||||
|
||||
// Make sure we don't go over the end of the string, this can happen with
|
||||
// illegal bytes.
|
||||
if (compl_len < (int)compl_shown_match->cp_str.length)
|
||||
ins_compl_insert_bytes(compl_shown_match->cp_str.string + compl_len, -1);
|
||||
if (match_at_original_text(compl_shown_match))
|
||||
{
|
||||
ins_compl_insert_bytes(str + compl_len, -1);
|
||||
if (preinsert && move_cursor)
|
||||
curwin->w_cursor.col -= ((size_t)STRLEN(str) - leader_len);
|
||||
}
|
||||
if (match_at_original_text(compl_shown_match) || preinsert)
|
||||
compl_used_match = FALSE;
|
||||
else
|
||||
compl_used_match = TRUE;
|
||||
@ -4572,6 +4617,7 @@ ins_compl_next(
|
||||
unsigned int cur_cot_flags = get_cot_flags();
|
||||
int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
|
||||
int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
|
||||
int compl_preinsert = ins_compl_has_preinsert();
|
||||
|
||||
// When user complete function return -1 for findstart which is next
|
||||
// time of 'always', compl_shown_match become NULL.
|
||||
@ -4614,7 +4660,7 @@ ins_compl_next(
|
||||
}
|
||||
|
||||
// Insert the text of the new completion, or the compl_leader.
|
||||
if (compl_no_insert && !started)
|
||||
if (compl_no_insert && !started && !compl_preinsert)
|
||||
{
|
||||
ins_compl_insert_bytes(compl_orig_text.string + get_compl_len(), -1);
|
||||
compl_used_match = FALSE;
|
||||
@ -4622,7 +4668,7 @@ ins_compl_next(
|
||||
else if (insert_match)
|
||||
{
|
||||
if (!compl_get_longest || compl_used_match)
|
||||
ins_compl_insert(in_compl_func);
|
||||
ins_compl_insert(in_compl_func, TRUE);
|
||||
else
|
||||
ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
|
||||
}
|
||||
|
@ -531,6 +531,7 @@ EXTERN unsigned cot_flags; // flags from 'completeopt'
|
||||
#define COT_NOSELECT 0x080 // FALSE: select & insert, TRUE: noselect
|
||||
#define COT_FUZZY 0x100 // TRUE: fuzzy match enabled
|
||||
#define COT_NOSORT 0x200 // TRUE: fuzzy match without qsort score
|
||||
#define COT_PREINSERT 0x400 // TRUE: preinsert
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
EXTERN char_u *p_csl; // 'completeslash'
|
||||
#endif
|
||||
|
@ -120,7 +120,7 @@ static char *(p_fdm_values[]) = {"manual", "expr", "marker", "indent", "syntax",
|
||||
NULL};
|
||||
static char *(p_fcl_values[]) = {"all", NULL};
|
||||
#endif
|
||||
static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview", "popup", "popuphidden", "noinsert", "noselect", "fuzzy", "nosort", NULL};
|
||||
static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview", "popup", "popuphidden", "noinsert", "noselect", "fuzzy", "nosort", "preinsert", NULL};
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
static char *(p_csl_values[]) = {"slash", "backslash", NULL};
|
||||
#endif
|
||||
|
@ -58,9 +58,10 @@ void f_complete_add(typval_T *argvars, typval_T *rettv);
|
||||
void f_complete_check(typval_T *argvars, typval_T *rettv);
|
||||
void f_complete_info(typval_T *argvars, typval_T *rettv);
|
||||
void ins_compl_delete(void);
|
||||
void ins_compl_insert(int in_compl_func);
|
||||
void ins_compl_insert(int in_compl_func, int move_cursor);
|
||||
void ins_compl_check_keys(int frequency, int in_compl_func);
|
||||
int ins_complete(int c, int enable_pum);
|
||||
int ins_compl_col_range_attr(int col);
|
||||
void free_insexpand_stuff(void);
|
||||
int ins_compl_preinsert_effect(void);
|
||||
/* vim: set ft=c : */
|
||||
|
@ -155,7 +155,7 @@ let test_values = {
|
||||
\ ['xxx']],
|
||||
\ 'concealcursor': [['', 'n', 'v', 'i', 'c', 'nvic'], ['xxx']],
|
||||
\ 'completeopt': [['', 'menu', 'menuone', 'longest', 'preview', 'popup',
|
||||
\ 'popuphidden', 'noinsert', 'noselect', 'fuzzy', 'menu,longest'],
|
||||
\ 'popuphidden', 'noinsert', 'noselect', 'fuzzy', "preinsert", 'menu,longest'],
|
||||
\ ['xxx', 'menu,,,longest,']],
|
||||
\ 'completeitemalign': [['abbr,kind,menu', 'menu,abbr,kind'],
|
||||
\ ['', 'xxx', 'abbr', 'abbr,menu', 'abbr,menu,kind,abbr',
|
||||
|
@ -3025,4 +3025,121 @@ func Test_complete_info_completed()
|
||||
set cot&
|
||||
endfunc
|
||||
|
||||
function Test_completeopt_preinsert()
|
||||
func Omni_test(findstart, base)
|
||||
if a:findstart
|
||||
return col(".")
|
||||
endif
|
||||
return [#{word: "fobar"}, #{word: "foobar"}, #{word: "你的"}, #{word: "你好世界"}]
|
||||
endfunc
|
||||
set omnifunc=Omni_test
|
||||
set completeopt=menu,menuone,preinsert
|
||||
|
||||
new
|
||||
call feedkeys("S\<C-X>\<C-O>f", 'tx')
|
||||
call assert_equal("fobar", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
call feedkeys("S\<C-X>\<C-O>foo", 'tx')
|
||||
call assert_equal("foobar", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
call feedkeys("S\<C-X>\<C-O>foo\<BS>\<BS>\<BS>", 'tx')
|
||||
call assert_equal("", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
" delete a character and input new leader
|
||||
call feedkeys("S\<C-X>\<C-O>foo\<BS>b", 'tx')
|
||||
call assert_equal("fobar", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
" delete preinsert when prepare completion
|
||||
call feedkeys("S\<C-X>\<C-O>f\<Space>", 'tx')
|
||||
call assert_equal("f ", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
call feedkeys("S\<C-X>\<C-O>你", 'tx')
|
||||
call assert_equal("你的", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
call feedkeys("S\<C-X>\<C-O>你好", 'tx')
|
||||
call assert_equal("你好世界", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
call feedkeys("Shello wo\<Left>\<Left>\<Left>\<C-X>\<C-O>f", 'tx')
|
||||
call assert_equal("hello fobar wo", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
call feedkeys("Shello wo\<Left>\<Left>\<Left>\<C-X>\<C-O>f\<BS>", 'tx')
|
||||
call assert_equal("hello wo", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
call feedkeys("Shello wo\<Left>\<Left>\<Left>\<C-X>\<C-O>foo", 'tx')
|
||||
call assert_equal("hello foobar wo", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
call feedkeys("Shello wo\<Left>\<Left>\<Left>\<C-X>\<C-O>foo\<BS>b", 'tx')
|
||||
call assert_equal("hello fobar wo", getline('.'))
|
||||
call feedkeys("\<C-E>\<ESC>", 'tx')
|
||||
|
||||
" confrim
|
||||
call feedkeys("S\<C-X>\<C-O>f\<C-Y>", 'tx')
|
||||
call assert_equal("fobar", getline('.'))
|
||||
call assert_equal(5, col('.'))
|
||||
|
||||
" cancel
|
||||
call feedkeys("S\<C-X>\<C-O>fo\<C-E>", 'tx')
|
||||
call assert_equal("fo", getline('.'))
|
||||
call assert_equal(2, col('.'))
|
||||
|
||||
call feedkeys("S hello hero\<CR>h\<C-X>\<C-N>", 'tx')
|
||||
call assert_equal("hello", getline('.'))
|
||||
call assert_equal(1, col('.'))
|
||||
|
||||
call feedkeys("Sh\<C-X>\<C-N>\<C-Y>", 'tx')
|
||||
call assert_equal("hello", getline('.'))
|
||||
call assert_equal(5, col('.'))
|
||||
|
||||
" delete preinsert part
|
||||
call feedkeys("S\<C-X>\<C-O>fo ", 'tx')
|
||||
call assert_equal("fo ", getline('.'))
|
||||
call assert_equal(3, col('.'))
|
||||
|
||||
" whole line
|
||||
call feedkeys("Shello hero\<CR>\<C-X>\<C-L>", 'tx')
|
||||
call assert_equal("hello hero", getline('.'))
|
||||
call assert_equal(1, col('.'))
|
||||
|
||||
call feedkeys("Shello hero\<CR>he\<C-X>\<C-L>", 'tx')
|
||||
call assert_equal("hello hero", getline('.'))
|
||||
call assert_equal(2, col('.'))
|
||||
|
||||
" can not work with fuzzy
|
||||
set cot+=fuzzy
|
||||
call feedkeys("S\<C-X>\<C-O>", 'tx')
|
||||
call assert_equal("fobar", getline('.'))
|
||||
call assert_equal(5, col('.'))
|
||||
|
||||
" test for fuzzy and noinsert
|
||||
set cot+=noinsert
|
||||
call feedkeys("S\<C-X>\<C-O>fb", 'tx')
|
||||
call assert_equal("fb", getline('.'))
|
||||
call assert_equal(2, col('.'))
|
||||
|
||||
call feedkeys("S\<C-X>\<C-O>你", 'tx')
|
||||
call assert_equal("你", getline('.'))
|
||||
call assert_equal(1, col('.'))
|
||||
|
||||
call feedkeys("S\<C-X>\<C-O>fb\<C-Y>", 'tx')
|
||||
call assert_equal("fobar", getline('.'))
|
||||
call assert_equal(5, col('.'))
|
||||
|
||||
bw!
|
||||
bw!
|
||||
set cot&
|
||||
set omnifunc&
|
||||
delfunc Omni_test
|
||||
autocmd! CompleteChanged
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
|
||||
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1056,
|
||||
/**/
|
||||
1055,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user