Merge pull request #40277 from zeertzjq/vim-9.2.0651

vim-patch:9.2.{0651,0656}
This commit is contained in:
zeertzjq
2026-06-17 09:16:30 +08:00
committed by GitHub
2 changed files with 82 additions and 6 deletions

View File

@@ -274,6 +274,7 @@ static linenr_T compl_lnum = 0; ///< lnum where the completion start
static colnr_T compl_col = 0; ///< column where the text starts
///< that is being completed
static colnr_T compl_ins_end_col = 0;
static colnr_T compl_longest_end_col = 0; ///< end of 'longest' inserted text
static String compl_orig_text = STRING_INIT; ///< text as it was before
///< completion started
/// Undo information to restore extmarks for original text.
@@ -1087,6 +1088,33 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len)
return strncmp(match->cp_str.data, str, len) == 0;
}
/// Like ins_compl_equal(), but ignore case in the 'longest'-inserted part of
/// the leader, so CTRL-N and CTRL-P filter the same way.
static bool ins_compl_equal_sc(compl_T *match, char *str, size_t len)
FUNC_ATTR_NONNULL_ALL
{
int typed = compl_length;
int longest_end = (compl_get_longest && compl_longest_end_col > compl_col)
? (int)(compl_longest_end_col - compl_col) : typed;
if ((match->cp_flags & (CP_EQUAL | CP_ICASE)) || longest_end <= typed) {
return ins_compl_equal(match, str, len);
}
if (match->cp_str.size < len) {
return false;
}
for (int i = 0; (size_t)i < len; i++) {
if (i >= typed && i < longest_end
? TOLOWER_LOC((uint8_t)match->cp_str.data[i]) != TOLOWER_LOC((uint8_t)str[i])
: match->cp_str.data[i] != str[i]) {
return false;
}
}
return true;
}
/// when len is -1 mean use whole length of p otherwise part of p
static void ins_compl_insert_bytes(char *p, int len)
FUNC_ATTR_NONNULL_ALL
@@ -1581,15 +1609,16 @@ static int ins_compl_build_pum(void)
String *leader = get_leader_for_startcol(comp, true);
// Apply 'smartcase' behavior during normal mode
if (ctrl_x_mode_normal() && !p_inf && leader->data
&& !ignorecase(leader->data) && !cot_fuzzy()) {
// Apply 'smartcase': judge case from compl_orig_text, not the leader
// which 'longest' may fill with uppercase the user never typed.
if (ctrl_x_mode_normal() && !p_inf && compl_orig_text.data
&& !ignorecase(compl_orig_text.data) && !cot_fuzzy()) {
comp->cp_flags &= ~CP_ICASE;
}
if (!match_at_original_text(comp)
&& (leader->data == NULL
|| ins_compl_equal(comp, leader->data, leader->size)
|| ins_compl_equal_sc(comp, leader->data, leader->size)
|| (cot_fuzzy() && comp->cp_score != FUZZY_SCORE_NONE))) {
// Limit number of items from each source if max_items is set.
bool match_limit_exceeded = false;
@@ -2096,6 +2125,7 @@ void ins_compl_clear(void)
compl_matches = 0;
compl_selected_item = -1;
compl_ins_end_col = 0;
compl_longest_end_col = 0;
compl_curr_win = NULL;
compl_curr_buf = NULL;
API_CLEAR_STRING(compl_pattern);
@@ -3956,6 +3986,7 @@ static void ins_compl_longest_insert(char *prefix)
{
ins_compl_delete(false);
ins_compl_insert_bytes(prefix + get_compl_len(), -1);
compl_longest_end_col = curwin->w_cursor.col;
ins_redraw(false);
}
@@ -5058,13 +5089,14 @@ static char *find_common_prefix(size_t *prefix_len, bool curbuf_only)
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)) {
if (ctrl_x_mode_normal() && !p_inf && compl_orig_text.data
&& !ignorecase(compl_orig_text.data)) {
compl->cp_flags &= ~CP_ICASE;
}
if (!match_at_original_text(compl)
&& (leader->data == NULL
|| ins_compl_equal(compl, leader->data, leader->size))) {
|| ins_compl_equal_sc(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;

View File

@@ -6468,4 +6468,48 @@ func Test_mapped_ctrl_n_during_complete_function()
bwipe!
endfunc
func Test_smartcase_longest()
func! GetMatches()
let info = complete_info(["matches"])
return map(copy(info.matches), {_, v -> v.word})
endfunc
func! TestInner(key)
let pr = "\<c-r>=string(GetMatches())\<cr>"
let words = ["InputEvent", "inputmap", "INPUT_MAP"]
new
set completeopt=menuone,noselect,longest ignorecase smartcase
" Lowercase 'inp' all three (case-insensitive).
call setline(1, words)
exe $"normal! ggOinp{a:key}{pr}"
let line = getline(1)
call assert_match('\c^input', line, 'inp prefix, key=' .. strtrans(a:key))
call assert_equal("['InputEvent', 'inputmap', 'INPUT_MAP']",
\ substitute(line, '\c^input', '', ''),
\ 'inp matches, key=' .. strtrans(a:key))
" Uppercase 'I' excludes lowercase 'inputmap'
%d
call setline(1, words)
exe $"normal! ggOI{a:key}{pr}"
let line = getline(1)
call assert_match('\c^input', line, 'I prefix, key=' .. strtrans(a:key))
call assert_equal("['InputEvent', 'INPUT_MAP']",
\ substitute(line, '\c^input', '', ''),
\ 'I matches, key=' .. strtrans(a:key))
set ignorecase& smartcase& completeopt&
bw!
endfunc
call TestInner("\<c-n>")
call TestInner("\<c-p>")
call TestInner("\<c-x>\<c-n>")
call TestInner("\<c-x>\<c-p>")
delfunc GetMatches
delfunc TestInner
endfunc
" vim: shiftwidth=2 sts=2 expandtab nofoldenable