Compare commits

...

11 Commits

Author SHA1 Message Date
zeertzjq
d31ff7bfcd
Merge 28fa71d743 into 160cbd0ef4 2024-12-18 21:06:46 +02:00
luukvbaal
160cbd0ef4
test(cursor_spec): global highlight definitions (#31613) 2024-12-18 19:06:16 +00:00
Gregory Anders
3db3947b0e
fix(terminal): restore cursor from 'guicursor' on TermLeave (#31620)
Fixes: https://github.com/neovim/neovim/issues/31612
2024-12-18 11:41:05 -06:00
zeertzjq
28fa71d743 vim-patch:9.1.0733: keyword completion does not work with fuzzy
Problem:  keyword completion does not work with fuzzy
          (egesip)
Solution: handle ctrl_x_mode_normal() specifically
          (glepnir)

fixes: vim/vim#15412
closes: vim/vim#15424

7cfe693f9b

Co-authored-by: glepnir <glephunter@gmail.com>
2024-12-18 10:22:43 +08:00
zeertzjq
36fcc1d9ae vim-patch:26e4b00: runtime(doc): Revert outdated comment in completeopt's fuzzy documentation
Originally, `:set completeopt+=fuzzy` did not affect how the candidate
list is collected in default keyword completion. A comment was added to
documentation as part of vim/vim#14912 to clarify it. vim/vim#15193 later changed the
fuzzy behavior to now change the candidate collection behavior as well
so the clarification in docs is now wrong. Remove them here.

closes: vim/vim#15656

26e4b00002

Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
2024-12-18 10:22:43 +08:00
zeertzjq
b53ba9d9a7 vim-patch:9.1.0705: Sorting of fuzzy filename completion is not stable
Problem:  Sorting of fuzzy filename completion is not stable
Solution: Compare indexes when scores are equal.  Fix some typos.
          (zeertzjq)

closes: vim/vim#15593

58d705238c
2024-12-18 10:22:43 +08:00
zeertzjq
af199f9f02 vim-patch:9.1.0654: completion does not respect completeslash with fuzzy
Problem:  completion does not respect completeslash with fuzzy
          (egesip)
Solution: Change path separator on Windows, depending on 'completeslash'
          option value (glepnir)

fixes: vim/vim#15392
closes: vim/vim#15418

b9de1a057f

Co-authored-by: glepnir <glephunter@gmail.com>
2024-12-18 10:22:43 +08:00
zeertzjq
d75a36bc52 vim-patch:9.1.0634: Ctrl-P not working by default
Problem:  Ctrl-P not working by default
          (Jesse Pavel, after v9.1.0598)
Solution: Revert part of v9.1.0598 and set cur_match_pos
          correctly according to compl_dir_forward()

fixes: vim/vim#15370
closes: vim/vim#15379

13032a49b7

Co-authored-by: Christian Brabandt <cb@256bit.org>
2024-12-18 10:22:43 +08:00
zeertzjq
e340109bef vim-patch:9.1.0631: wrong completion list displayed with non-existing dir + fuzzy completion
Problem:  wrong completion list displayed with non-existing dir + fuzzy
          completion (kawarimidoll)
Solution: clear list of matches, if leader did not use fuzzy match
          (glepnir)

fixes: vim/vim#15357
closes: vim/vim#15365

6b6280c4a2

Co-authored-by: glepnir <glephunter@gmail.com>
2024-12-18 10:22:43 +08:00
zeertzjq
2a79de0a6f vim-patch:9.1.0605: internal error with fuzzy completion
Problem:  internal error with fuzzy completion
          (techntools)
Solution: only fuzzy complete the pattern after directory separator
          (glepnir)

fixes: vim/vim#15287
closes: vim/vim#15291

0be03e14b9

Co-authored-by: glepnir <glephunter@gmail.com>
2024-12-18 10:22:43 +08:00
zeertzjq
46d89ff9f8 vim-patch:9.1.0598: fuzzy completion does not work with default completion
Problem:  fuzzy completion does not work with default completion
Solution: Make it work (glepnir)

closes: vim/vim#15193

8159fb18a9

Cherry-pick insexpand.c changes from patch 9.1.0608.

N/A patch:
vim-patch:9.1.0632: MS-Windows: Compiler Warnings

Co-authored-by: glepnir <glephunter@gmail.com>
2024-12-18 10:22:42 +08:00
10 changed files with 513 additions and 81 deletions

View File

@ -1566,10 +1566,7 @@ A jump table for the options with a short description can be found at |Q_op|.
fuzzy Enable |fuzzy-matching| for completion candidates. This
allows for more flexible and intuitive matching, where
characters can be skipped and matches can be found even
if the exact sequence is not typed. Only makes a
difference how completion candidates are reduced from the
list of alternatives, but not how the candidates are
collected (using different completion types).
if the exact sequence is not typed.
*'completeslash'* *'csl'*
'completeslash' 'csl' string (default "")

View File

@ -1093,10 +1093,7 @@ vim.go.cia = vim.go.completeitemalign
--- fuzzy Enable `fuzzy-matching` for completion candidates. This
--- allows for more flexible and intuitive matching, where
--- characters can be skipped and matches can be found even
--- if the exact sequence is not typed. Only makes a
--- difference how completion candidates are reduced from the
--- list of alternatives, but not how the candidates are
--- collected (using different completion types).
--- if the exact sequence is not typed.
---
--- @type string
vim.o.completeopt = "menu,preview"

View File

@ -283,6 +283,8 @@ static size_t spell_bad_len = 0; // length of located bad word
static int compl_selected_item = -1;
static int *compl_fuzzy_scores;
// "compl_match_array" points the currently displayed list of entries in the
// popup menu. It is NULL when there is no popup menu.
static pumitem_T *compl_match_array = NULL;
@ -3008,7 +3010,7 @@ enum {
/// the "st->e_cpt" option value and process the next matching source.
/// INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_arg,
pos_T *start_match_pos)
pos_T *start_match_pos, bool in_fuzzy)
{
int compl_type = -1;
int status = INS_COMPL_CPT_OK;
@ -3024,7 +3026,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
st->first_match_pos = *start_match_pos;
// Move the cursor back one character so that ^N can match the
// word immediately after the cursor.
if (ctrl_x_mode_normal() && dec(&st->first_match_pos) < 0) {
if (ctrl_x_mode_normal() && (!in_fuzzy && dec(&st->first_match_pos) < 0)) {
// Move the cursor to after the last character in the
// buffer, so that word at start of buffer is found
// correctly.
@ -3162,11 +3164,75 @@ static void get_next_tag_completion(void)
p_ic = save_p_ic;
}
/// Compare function for qsort
static int compare_scores(const void *a, const void *b)
{
int idx_a = *(const int *)a;
int idx_b = *(const int *)b;
int score_a = compl_fuzzy_scores[idx_a];
int score_b = compl_fuzzy_scores[idx_b];
return score_a == score_b ? (idx_a == idx_b ? 0 : (idx_a < idx_b ? -1 : 1))
: (score_a > score_b ? -1 : 1);
}
/// Get the next set of filename matching "compl_pattern".
static void get_next_filename_completion(void)
{
char **matches;
int num_matches;
char *leader = ins_compl_leader();
size_t leader_len = strlen(leader);
bool in_fuzzy = ((get_cot_flags() & kOptCotFlagFuzzy) != 0 && leader_len > 0);
#ifdef BACKSLASH_IN_FILENAME
char pathsep = (curbuf->b_p_csl[0] == 's')
? '/' : (curbuf->b_p_csl[0] == 'b') ? '\\' : PATHSEP;
#else
char pathsep = PATHSEP;
#endif
if (in_fuzzy) {
#ifdef BACKSLASH_IN_FILENAME
if (curbuf->b_p_csl[0] == 's') {
for (size_t i = 0; i < leader_len; i++) {
if (leader[i] == '\\') {
leader[i] = '/';
}
}
} else if (curbuf->b_p_csl[0] == 'b') {
for (size_t i = 0; i < leader_len; i++) {
if (leader[i] == '/') {
leader[i] = '\\';
}
}
}
#endif
char *last_sep = strrchr(leader, pathsep);
if (last_sep == NULL) {
// No path separator or separator is the last character,
// fuzzy match the whole leader
xfree(compl_pattern);
compl_pattern = xstrdup("*");
compl_patternlen = strlen(compl_pattern);
} else if (*(last_sep + 1) == NUL) {
in_fuzzy = false;
} else {
// Split leader into path and file parts
size_t path_len = (size_t)(last_sep - leader) + 1;
size_t path_with_wildcard_len = path_len + 2;
char *path_with_wildcard = xmalloc(path_with_wildcard_len);
xmemcpyz(path_with_wildcard, leader, path_len);
xstrlcat(path_with_wildcard, "*", path_with_wildcard_len);
xfree(compl_pattern);
compl_pattern = path_with_wildcard;
compl_patternlen = strlen(compl_pattern);
// Move leader to the file part
leader = last_sep + 1;
leader_len = strlen(leader);
}
}
if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
return;
@ -3189,7 +3255,46 @@ static void get_next_filename_completion(void)
}
}
#endif
if (in_fuzzy) {
garray_T fuzzy_indices;
ga_init(&fuzzy_indices, sizeof(int), 10);
compl_fuzzy_scores = (int *)xmalloc(sizeof(int) * (size_t)num_matches);
for (int i = 0; i < num_matches; i++) {
char *ptr = matches[i];
int score = fuzzy_match_str(ptr, leader);
if (score > 0) {
GA_APPEND(int, &fuzzy_indices, i);
compl_fuzzy_scores[i] = score;
}
}
// prevent qsort from deref NULL pointer
if (fuzzy_indices.ga_len > 0) {
int *fuzzy_indices_data = (int *)fuzzy_indices.ga_data;
qsort(fuzzy_indices_data, (size_t)fuzzy_indices.ga_len, sizeof(int), compare_scores);
char **sorted_matches = (char **)xmalloc(sizeof(char *) * (size_t)fuzzy_indices.ga_len);
for (int i = 0; i < fuzzy_indices.ga_len; i++) {
sorted_matches[i] = xstrdup(matches[fuzzy_indices_data[i]]);
}
FreeWild(num_matches, matches);
matches = sorted_matches;
num_matches = fuzzy_indices.ga_len;
} else if (leader_len > 0) {
FreeWild(num_matches, matches);
num_matches = 0;
}
xfree(compl_fuzzy_scores);
ga_clear(&fuzzy_indices);
}
if (num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
}
}
/// Get the next set of command-line completions matching "compl_pattern".
@ -3310,6 +3415,11 @@ static char *ins_compl_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_po
/// @return OK if a new next match is found, otherwise FAIL.
static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
{
char *ptr = NULL;
int len = 0;
bool in_fuzzy = (get_cot_flags() & kOptCotFlagFuzzy) != 0 && compl_length > 0;
char *leader = ins_compl_leader();
// If 'infercase' is set, don't use 'smartcase' here
const int save_p_scs = p_scs;
assert(st->ins_buf);
@ -3324,7 +3434,7 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
const int save_p_ws = p_ws;
if (st->ins_buf != curbuf) {
p_ws = false;
} else if (*st->e_cpt == '.') {
} else if (*st->e_cpt == '.' && !in_fuzzy) {
p_ws = true;
}
bool looped_around = false;
@ -3335,9 +3445,14 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
msg_silent++; // Don't want messages for wrapscan.
// ctrl_x_mode_line_or_eval() || word-wise search that
// has added a word that was at the beginning of the line.
if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL)) {
if ((ctrl_x_mode_whole_line() && !in_fuzzy) || ctrl_x_mode_eval()
|| (compl_cont_status & CONT_SOL)) {
found_new_match = search_for_exact_line(st->ins_buf, st->cur_match_pos,
compl_direction, compl_pattern);
} else if (in_fuzzy) {
found_new_match = search_for_fuzzy_match(st->ins_buf,
st->cur_match_pos, leader, compl_direction,
start_pos, &len, &ptr, ctrl_x_mode_whole_line());
} else {
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
NULL, compl_direction, compl_pattern, compl_patternlen,
@ -3383,12 +3498,15 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
&& start_pos->col == st->cur_match_pos->col) {
continue;
}
int len;
char *ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
if (!in_fuzzy) {
ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
&len, &cont_s_ipos);
}
if (ptr == NULL) {
continue;
}
if (ins_compl_add_infercase(ptr, len, p_ic,
st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname,
0, cont_s_ipos) != NOTDONE) {
@ -3489,6 +3607,7 @@ static int ins_compl_get_exp(pos_T *ini)
static bool st_cleared = false;
int found_new_match;
int type = ctrl_x_mode;
bool in_fuzzy = (get_cot_flags() & kOptCotFlagFuzzy) != 0;
assert(curbuf != NULL);
@ -3525,7 +3644,7 @@ static int ins_compl_get_exp(pos_T *ini)
// entries from 'complete' that look in loaded buffers.
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
&& (!compl_started || st.found_all)) {
int status = process_next_cpt_value(&st, &type, ini);
int status = process_next_cpt_value(&st, &type, ini, in_fuzzy);
if (status == INS_COMPL_CPT_END) {
break;
}
@ -4483,6 +4602,7 @@ static int ins_compl_start(void)
line = ml_get(curwin->w_cursor.lnum);
}
bool in_fuzzy = get_cot_flags() & kOptCotFlagFuzzy;
if (compl_status_adding()) {
edit_submode_pre = _(" Adding");
if (ctrl_x_mode_line_or_eval()) {
@ -4496,6 +4616,9 @@ static int ins_compl_start(void)
curbuf->b_p_com = old;
compl_length = 0;
compl_col = curwin->w_cursor.col;
} else if (ctrl_x_mode_normal() && in_fuzzy) {
compl_startpos = curwin->w_cursor;
compl_cont_status &= CONT_S_IPOS;
}
} else {
edit_submode_pre = NULL;

View File

@ -1537,10 +1537,7 @@ return {
fuzzy Enable |fuzzy-matching| for completion candidates. This
allows for more flexible and intuitive matching, where
characters can be skipped and matches can be found even
if the exact sequence is not typed. Only makes a
difference how completion candidates are reduced from the
list of alternatives, but not how the candidates are
collected (using different completion types).
if the exact sequence is not typed.
]=],
expand_cb = 'expand_set_completeopt',
full_name = 'completeopt',

View File

@ -3564,6 +3564,166 @@ garray_T *fuzzy_match_str_with_pos(char *const str, const char *const pat)
return match_positions;
}
/// This function searches for a fuzzy match of the pattern `pat` within the
/// line pointed to by `*ptr`. It splits the line into words, performs fuzzy
/// matching on each word, and returns the length and position of the first
/// matched word.
static bool fuzzy_match_str_in_line(char **ptr, char *pat, int *len, pos_T *current_pos)
{
char *str = *ptr;
char *strBegin = str;
char *end = NULL;
char *start = NULL;
bool found = false;
if (str == NULL || pat == NULL) {
return found;
}
while (*str != NUL) {
// Skip non-word characters
start = find_word_start(str);
if (*start == NUL) {
break;
}
end = find_word_end(start);
// Extract the word from start to end
char save_end = *end;
*end = NUL;
// Perform fuzzy match
int result = fuzzy_match_str(start, pat);
*end = save_end;
if (result > 0) {
*len = (int)(end - start);
current_pos->col += (int)(end - strBegin);
found = true;
*ptr = start;
break;
}
// Move to the end of the current word for the next iteration
str = end;
// Ensure we continue searching after the current word
while (*str != NUL && !vim_iswordp(str)) {
MB_PTR_ADV(str);
}
}
return found;
}
/// Search for the next fuzzy match in the specified buffer.
/// This function attempts to find the next occurrence of the given pattern
/// in the buffer, starting from the current position. It handles line wrapping
/// and direction of search.
///
/// Return true if a match is found, otherwise false.
bool search_for_fuzzy_match(buf_T *buf, pos_T *pos, char *pattern, int dir, pos_T *start_pos,
int *len, char **ptr, bool whole_line)
{
pos_T current_pos = *pos;
pos_T circly_end;
bool found_new_match = false;
bool looped_around = false;
char *next_word_end = NULL;
char *match_word = NULL;
if (whole_line) {
current_pos.lnum += dir;
}
if (buf == curbuf) {
circly_end = *start_pos;
} else {
circly_end.lnum = buf->b_ml.ml_line_count;
circly_end.col = 0;
circly_end.coladd = 0;
}
while (true) {
// Check if looped around and back to start position
if (looped_around && equalpos(current_pos, circly_end)) {
break;
}
// Ensure current_pos is valid
if (current_pos.lnum >= 1 && current_pos.lnum <= buf->b_ml.ml_line_count) {
// Get the current line buffer
*ptr = ml_get_buf(buf, current_pos.lnum);
// If ptr is end of line is reached, move to next line
// or previous line based on direction
if (**ptr != NUL) {
if (!whole_line) {
*ptr += current_pos.col;
// Try to find a fuzzy match in the current line starting from current position
found_new_match = fuzzy_match_str_in_line(ptr, pattern, len, &current_pos);
if (found_new_match) {
if (ctrl_x_mode_normal()) {
match_word = xstrnsave(*ptr, (size_t)(*len));
if (strcmp(match_word, pattern) == 0) {
next_word_end = find_word_start(*ptr + *len);
if (*next_word_end != NUL && *next_word_end != NL) {
// Find end of the word.
while (*next_word_end != NUL) {
int l = utfc_ptr2len(next_word_end);
if (l < 2 && !vim_iswordc(*next_word_end)) {
break;
}
next_word_end += l;
}
} else if (looped_around) {
found_new_match = false;
}
*len = (int)(next_word_end - *ptr);
current_pos.col = *len;
}
xfree(match_word);
}
*pos = current_pos;
break;
} else if (looped_around && current_pos.lnum == circly_end.lnum) {
break;
}
} else {
if (fuzzy_match_str(*ptr, pattern) > 0) {
found_new_match = true;
*pos = current_pos;
*len = (int)strlen(*ptr);
break;
}
}
}
}
// Move to the next line or previous line based on direction
if (dir == FORWARD) {
if (++current_pos.lnum > buf->b_ml.ml_line_count) {
if (p_ws) {
current_pos.lnum = 1;
looped_around = true;
} else {
break;
}
}
} else {
if (--current_pos.lnum < 1) {
if (p_ws) {
current_pos.lnum = buf->b_ml.ml_line_count;
looped_around = true;
} else {
break;
}
}
}
current_pos.col = 0;
}
return found_new_match;
}
/// Copy a list of fuzzy matches into a string list after sorting the matches by
/// the fuzzy score. Frees the memory allocated for "fuzmatch".
void fuzzymatches_to_strmatches(fuzmatch_str_T *const fuzmatch, char ***const matches,

View File

@ -641,9 +641,6 @@ bool terminal_enter(void)
curwin->w_p_so = 0;
curwin->w_p_siso = 0;
// Save the existing cursor entry since it may be modified by the application
cursorentry_T save_cursorentry = shape_table[SHAPE_IDX_TERM];
// Update the cursor shape table and flush changes to the UI
s->term->pending.cursor = true;
refresh_cursor(s->term);
@ -674,8 +671,8 @@ bool terminal_enter(void)
RedrawingDisabled = s->save_rd;
apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf);
shape_table[SHAPE_IDX_TERM] = save_cursorentry;
ui_mode_info_set();
// Restore the terminal cursor to what is set in 'guicursor'
(void)parse_shape_opt(SHAPE_CURSOR);
if (save_curwin == curwin->handle) { // Else: window was closed.
curwin->w_p_cul = save_w_p_cul;

View File

@ -15,9 +15,20 @@ local skip = t.skip
describe(':terminal cursor', function()
local screen
local terminal_mode_idx ---@type number
before_each(function()
clear()
screen = tt.setup_screen()
if terminal_mode_idx == nil then
for i, v in ipairs(screen._mode_info) do
if v.name == 'terminal' then
terminal_mode_idx = i
end
end
assert(terminal_mode_idx)
end
end)
it('moves the screen cursor when focused', function()
@ -143,13 +154,6 @@ describe(':terminal cursor', function()
it('can be modified by application #3681', function()
skip(is_os('win'), '#31587')
local idx ---@type number
for i, v in ipairs(screen._mode_info) do
if v.name == 'terminal' then
idx = i
end
end
assert(idx)
local states = {
[1] = { blink = true, shape = 'block' },
@ -171,13 +175,13 @@ describe(':terminal cursor', function()
]],
condition = function()
if v.blink then
eq(500, screen._mode_info[idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff)
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
else
eq(0, screen._mode_info[idx].blinkon)
eq(0, screen._mode_info[idx].blinkoff)
eq(0, screen._mode_info[terminal_mode_idx].blinkon)
eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
end
eq(v.shape, screen._mode_info[idx].cursor_shape)
eq(v.shape, screen._mode_info[terminal_mode_idx].cursor_shape)
end,
})
end
@ -191,20 +195,13 @@ describe(':terminal cursor', function()
]])
-- Cursor returns to default on TermLeave
eq(500, screen._mode_info[idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff)
eq('block', screen._mode_info[idx].cursor_shape)
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
end)
it('can be modified per terminal', function()
skip(is_os('win'), '#31587')
local idx ---@type number
for i, v in ipairs(screen._mode_info) do
if v.name == 'terminal' then
idx = i
end
end
assert(idx)
-- Set cursor to vertical bar with blink
tt.feed_csi('5 q')
@ -216,9 +213,9 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} |
]],
condition = function()
eq(500, screen._mode_info[idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff)
eq('vertical', screen._mode_info[idx].cursor_shape)
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
end,
})
@ -231,9 +228,9 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} |
]],
condition = function()
eq(500, screen._mode_info[idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff)
eq('vertical', screen._mode_info[idx].cursor_shape)
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
end,
})
@ -256,9 +253,9 @@ describe(':terminal cursor', function()
]],
condition = function()
-- New terminal, cursor resets to defaults
eq(500, screen._mode_info[idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff)
eq('block', screen._mode_info[idx].cursor_shape)
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
end,
})
@ -275,9 +272,9 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} |
]],
condition = function()
eq(0, screen._mode_info[idx].blinkon)
eq(0, screen._mode_info[idx].blinkoff)
eq('horizontal', screen._mode_info[idx].cursor_shape)
eq(0, screen._mode_info[terminal_mode_idx].blinkon)
eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
eq('horizontal', screen._mode_info[terminal_mode_idx].cursor_shape)
end,
})
@ -294,9 +291,9 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} |
]],
condition = function()
eq(500, screen._mode_info[idx].blinkon)
eq(500, screen._mode_info[idx].blinkoff)
eq('vertical', screen._mode_info[idx].cursor_shape)
eq(500, screen._mode_info[terminal_mode_idx].blinkon)
eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
end,
})
end)
@ -326,6 +323,32 @@ describe(':terminal cursor', function()
{3:-- TERMINAL --} |
]])
end)
it('preserves guicursor value on TermLeave #31612', function()
eq(3, screen._mode_info[terminal_mode_idx].hl_id)
-- Change 'guicursor' while terminal mode is active
command('set guicursor+=t:Error')
local error_hl_id = call('hlID', 'Error')
screen:expect({
condition = function()
eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
end,
})
-- Exit terminal mode
feed([[<C-\><C-N>]])
screen:expect([[
tty ready |
^ |
|*5
]])
eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
end)
end)
describe('buffer cursor position is correct in terminal without number column', function()
@ -350,12 +373,6 @@ describe('buffer cursor position is correct in terminal without number column',
}, {
cols = 70,
})
screen:set_default_attr_ids({
[1] = { foreground = 253, background = 11 },
[2] = { reverse = true },
[3] = { bold = true },
[4] = { background = 11 },
})
-- Also check for real cursor position, as it is used for stuff like input methods
screen._handle_busy_start = function() end
screen._handle_busy_stop = function() end
@ -667,13 +684,6 @@ describe('buffer cursor position is correct in terminal with number column', fun
}, {
cols = 70,
})
screen:set_default_attr_ids({
[1] = { foreground = 253, background = 11 },
[2] = { reverse = true },
[3] = { bold = true },
[4] = { background = 11 },
[7] = { foreground = 130 },
})
-- Also check for real cursor position, as it is used for stuff like input methods
screen._handle_busy_start = function() end
screen._handle_busy_stop = function() end

View File

@ -5294,9 +5294,9 @@ describe('builtin popupmenu', function()
feed('S hello helio hero h<C-X><C-P>')
screen:expect([[
hello helio hero h^ |
{1:~ }{n: }{mn:h}{n:ello }{1: }|
{1:~ }{n: }{mn:h}{n:ero }{1: }|
{1:~ }{n: }{mn:h}{n:elio }{1: }|
{1:~ }{s: }{ms:h}{s:ero }{1: }|
{1:~ }{s: }{ms:h}{s:ello }{1: }|
{1:~ }|*15
{2:-- }{5:match 1 of 3} |
]])
@ -5304,13 +5304,20 @@ describe('builtin popupmenu', function()
feed('<Esc>S hello helio hero h<C-X><C-P><C-P>')
screen:expect([[
hello helio hero h^ |
{1:~ }{n: }{mn:h}{n:ello }{1: }|
{1:~ }{s: }{ms:h}{s:elio }{1: }|
{1:~ }{n: }{mn:h}{n:ero }{1: }|
{1:~ }{s: }{ms:h}{s:elio }{1: }|
{1:~ }{n: }{mn:h}{n:ello }{1: }|
{1:~ }|*15
{2:-- }{5:match 2 of 3} |
]])
feed('<Esc>S/non_existing_folder<C-X><C-F>')
screen:expect([[
/non_existing_folder^ |
{1:~ }|*18
{2:-- }{6:Pattern not found} |
]])
feed('<C-E><Esc>')
end)

View File

@ -2830,16 +2830,113 @@ func Test_complete_fuzzy_match()
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('hello help hero h', getline('.'))
set completeopt-=noinsert
call setline(1, ['xyz yxz x'])
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('xyz yxz xyz', getline('.'))
" can fuzzy get yxz when use Ctrl-N twice
call setline(1, ['xyz yxz x'])
call feedkeys("A\<C-X>\<C-N>\<C-N>\<Esc>0", 'tx!')
call assert_equal('xyz yxz yxz', getline('.'))
call setline(1, ['你好 你'])
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('你好 你好', getline('.'))
call setline(1, ['你的 我的 的'])
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('你的 我的 你的', getline('.'))
" can fuzzy get multiple-byte word when use Ctrl-N twice
call setline(1, ['你的 我的 的'])
call feedkeys("A\<C-X>\<C-N>\<C-N>\<Esc>0", 'tx!')
call assert_equal('你的 我的 我的', getline('.'))
" respect wrapscan
set nowrapscan
call setline(1, ["xyz", "yxz", ""])
call cursor(3, 1)
call feedkeys("Sy\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('y', getline('.'))
set wrapscan
call feedkeys("Sy\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('xyz', getline('.'))
" fuzzy on file
call writefile([''], 'fobar', 'D')
call writefile([''], 'foobar', 'D')
call setline(1, ['fob'])
call cursor(1, 1)
call feedkeys("A\<C-X>\<C-f>\<Esc>0", 'tx!')
call assert_equal('fobar', getline('.'))
call feedkeys("Sfob\<C-X>\<C-f>\<C-N>\<Esc>0", 'tx!')
call assert_equal('foobar', getline('.'))
call feedkeys("S../\<C-X>\<C-f>\<Esc>0", 'tx!')
call assert_match('../*', getline('.'))
call feedkeys("S../td\<C-X>\<C-f>\<Esc>0", 'tx!')
call assert_match('../testdir', getline('.'))
" can get completion from other buffer
set completeopt=fuzzy,menu,menuone
vnew
call setline(1, ["completeness,", "compatibility", "Composite", "Omnipotent"])
wincmd p
call feedkeys("Somp\<C-N>\<Esc>0", 'tx!')
call assert_equal('completeness', getline('.'))
call feedkeys("Somp\<C-N>\<C-N>\<Esc>0", 'tx!')
call assert_equal('compatibility', getline('.'))
call feedkeys("Somp\<C-P>\<Esc>0", 'tx!')
call assert_equal('Omnipotent', getline('.'))
call feedkeys("Somp\<C-P>\<C-P>\<Esc>0", 'tx!')
call assert_equal('Composite', getline('.'))
call feedkeys("S omp\<C-N>\<Esc>0", 'tx!')
call assert_equal(' completeness', getline('.'))
" fuzzy on whole line completion
call setline(1, ["world is on fire", "no one can save me but you", 'user can execute', ''])
call cursor(4, 1)
call feedkeys("Swio\<C-X>\<C-L>\<Esc>0", 'tx!')
call assert_equal('world is on fire', getline('.'))
call feedkeys("Su\<C-X>\<C-L>\<C-P>\<Esc>0", 'tx!')
call assert_equal('no one can save me but you', getline('.'))
" issue #15412
call setline(1, ['alpha bravio charlie'])
call feedkeys("Salpha\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('alpha bravio', getline('.'))
call feedkeys("Salp\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('alpha', getline('.'))
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('alpha bravio', getline('.'))
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('alpha bravio charlie', getline('.'))
set complete-=i
call feedkeys("Salp\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('alpha', getline('.'))
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('alpha bravio', getline('.'))
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('alpha bravio charlie', getline('.'))
call setline(1, ['alpha bravio charlie', 'alpha another'])
call feedkeys("Salpha\<C-X>\<C-N>\<C-N>\<Esc>0", 'tx!')
call assert_equal('alpha another', getline('.'))
call setline(1, ['你好 我好', '你好 他好'])
call feedkeys("S你好\<C-X>\<C-N>\<Esc>0", 'tx!')
call assert_equal('你好 我好', getline('.'))
call feedkeys("S你好\<C-X>\<C-N>\<C-N>\<Esc>0", 'tx!')
call assert_equal('你好 他好', getline('.'))
" issue #15526
set completeopt=fuzzy,menuone,menu,noselect
call setline(1, ['Text', 'ToText', ''])
call cursor(2, 1)
call cursor(3, 1)
call feedkeys("STe\<C-X>\<C-N>x\<CR>\<Esc>0", 'tx!')
call assert_equal('Tex', getline('.'))
" clean up
set omnifunc=
bw!
bw!
set complete& completeopt&
autocmd! AAAAA_Group
augroup! AAAAA_Group
@ -2850,6 +2947,38 @@ func Test_complete_fuzzy_match()
unlet g:word
endfunc
func Test_complete_fuzzy_with_completeslash()
CheckMSWindows
call writefile([''], 'fobar', 'D')
let orig_shellslash = &shellslash
set cpt&
new
set completeopt+=fuzzy
set noshellslash
" Test with completeslash unset
set completeslash=
call setline(1, ['.\fob'])
call feedkeys("A\<C-X>\<C-F>\<Esc>0", 'tx!')
call assert_equal('.\fobar', getline('.'))
" Test with completeslash=backslash
set completeslash=backslash
call feedkeys("S.\\fob\<C-X>\<C-F>\<Esc>0", 'tx!')
call assert_equal('.\fobar', getline('.'))
" Test with completeslash=slash
set completeslash=slash
call feedkeys("S.\\fob\<C-X>\<C-F>\<Esc>0", 'tx!')
call assert_equal('./fobar', getline('.'))
" Reset and clean up
let &shellslash = orig_shellslash
set completeslash=
%bw!
endfunc
" Check that tie breaking is stable for completeopt+=fuzzy (which should
" behave the same on different platforms).
func Test_complete_fuzzy_match_tie()
@ -2870,4 +2999,14 @@ func Test_complete_fuzzy_match_tie()
set completeopt&
endfunc
func Test_complete_backwards_default()
new
call append(1, ['foobar', 'foobaz'])
new
call feedkeys("i\<c-p>", 'tx')
call assert_equal('foobaz', getline('.'))
bw!
bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab nofoldenable

View File

@ -1513,6 +1513,11 @@ func Test_pum_highlights_match()
call TermWait(buf, 50)
call VerifyScreenDump(buf, 'Test_pum_highlights_11', {})
" issue #15357
call term_sendkeys(buf, "\<ESC>S/non_existing_folder\<C-X>\<C-F>")
call TermWait(buf, 50)
call VerifyScreenDump(buf, 'Test_pum_highlights_15', {})
call term_sendkeys(buf, "\<C-E>\<Esc>")
call TermWait(buf)