patch 9.1.1374: completion: 'smartcase' not respected when filtering matches
Problem: Currently, 'smartcase' is respected when completing keywords
using <C-N>, <C-P>, <C-X><C-N>, and <C-X><C-P>. However, when
a user continues typing and the completion menu is filtered
using cached matches, 'smartcase' is not applied. This leads
to poor-quality or irrelevant completion suggestions, as shown
in the example below.
Solution: When filtering cached completion items after typing additional
characters, apply case-sensitive comparison if 'smartcase' is
enabled and the typed pattern includes uppercase characters.
This ensures consistent and expected completion behavior.
(Girish Palya)
closes: #17271
Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
c3fbaa086e
commit
dc314053e1
@ -1,4 +1,4 @@
|
|||||||
*insert.txt* For Vim version 9.1. Last change: 2025 Apr 14
|
*insert.txt* For Vim version 9.1. Last change: 2025 May 08
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -1347,6 +1347,7 @@ use all space available.
|
|||||||
The 'pumwidth' option can be used to set a minimum width. The default is 15
|
The 'pumwidth' option can be used to set a minimum width. The default is 15
|
||||||
characters.
|
characters.
|
||||||
|
|
||||||
|
*compl-states*
|
||||||
There are three states:
|
There are three states:
|
||||||
1. A complete match has been inserted, e.g., after using CTRL-N or CTRL-P.
|
1. A complete match has been inserted, e.g., after using CTRL-N or CTRL-P.
|
||||||
2. A cursor key has been used to select another match. The match was not
|
2. A cursor key has been used to select another match. The match was not
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
*options.txt* For Vim version 9.1. Last change: 2025 May 07
|
*options.txt* For Vim version 9.1. Last change: 2025 May 08
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -7742,9 +7742,11 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
Override the 'ignorecase' option if the search pattern contains upper
|
Override the 'ignorecase' option if the search pattern contains upper
|
||||||
case characters. Only used when the search pattern is typed and
|
case characters. Only used when the search pattern is typed and
|
||||||
'ignorecase' option is on. Used for the commands "/", "?", "n", "N",
|
'ignorecase' option is on. Used for the commands "/", "?", "n", "N",
|
||||||
":g" and ":s". Not used for "*", "#", "gd", tag search, etc. After
|
":g" and ":s" and when filtering matches for the completion menu
|
||||||
"*" and "#" you can make 'smartcase' used by doing a "/" command,
|
|compl-states|.
|
||||||
recalling the search pattern from history and hitting <Enter>.
|
Not used for "*", "#", "gd", tag search, etc. After "*" and "#" you
|
||||||
|
can make 'smartcase' used by doing a "/" command, recalling the search
|
||||||
|
pattern from history and hitting <Enter>.
|
||||||
NOTE: This option is reset when 'compatible' is set.
|
NOTE: This option is reset when 'compatible' is set.
|
||||||
|
|
||||||
*'smartindent'* *'si'* *'nosmartindent'* *'nosi'*
|
*'smartindent'* *'si'* *'nosmartindent'* *'nosi'*
|
||||||
|
|||||||
@ -6645,6 +6645,7 @@ compl-keyword insert.txt /*compl-keyword*
|
|||||||
compl-omni insert.txt /*compl-omni*
|
compl-omni insert.txt /*compl-omni*
|
||||||
compl-omni-filetypes insert.txt /*compl-omni-filetypes*
|
compl-omni-filetypes insert.txt /*compl-omni-filetypes*
|
||||||
compl-spelling insert.txt /*compl-spelling*
|
compl-spelling insert.txt /*compl-spelling*
|
||||||
|
compl-states insert.txt /*compl-states*
|
||||||
compl-stop insert.txt /*compl-stop*
|
compl-stop insert.txt /*compl-stop*
|
||||||
compl-tag insert.txt /*compl-tag*
|
compl-tag insert.txt /*compl-tag*
|
||||||
compl-thesaurus insert.txt /*compl-thesaurus*
|
compl-thesaurus insert.txt /*compl-thesaurus*
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
*version9.txt* For Vim version 9.1. Last change: 2025 May 07
|
*version9.txt* For Vim version 9.1. Last change: 2025 May 08
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -41626,6 +41626,7 @@ Completion: ~
|
|||||||
"{flag}^<limit>" notation
|
"{flag}^<limit>" notation
|
||||||
- add ":filetype" command completion
|
- add ":filetype" command completion
|
||||||
- add "filetypecmd" completion type for |getcompletion()|
|
- add "filetypecmd" completion type for |getcompletion()|
|
||||||
|
- 'smartcase' applies to completion filtering
|
||||||
|
|
||||||
Options: ~
|
Options: ~
|
||||||
- the default for 'commentstring' contains whitespace padding to have
|
- the default for 'commentstring' contains whitespace padding to have
|
||||||
|
|||||||
@ -1507,7 +1507,7 @@ ins_compl_build_pum(void)
|
|||||||
match_count = 1;
|
match_count = 1;
|
||||||
max_matches_found = FALSE;
|
max_matches_found = FALSE;
|
||||||
}
|
}
|
||||||
else if (cpt_sources_array && !max_matches_found)
|
else if (cpt_sources_array != NULL && !max_matches_found)
|
||||||
{
|
{
|
||||||
int max_matches = cpt_sources_array[cur_source].max_matches;
|
int max_matches = cpt_sources_array[cur_source].max_matches;
|
||||||
if (max_matches > 0 && match_count > max_matches)
|
if (max_matches > 0 && match_count > max_matches)
|
||||||
@ -1515,10 +1515,16 @@ ins_compl_build_pum(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply 'smartcase' behavior during normal mode
|
||||||
|
if (ctrl_x_mode_normal() && !p_inf && compl_leader.string
|
||||||
|
&& !ignorecase(compl_leader.string) && !fuzzy_filter)
|
||||||
|
compl->cp_flags &= ~CP_ICASE;
|
||||||
|
|
||||||
if (!match_at_original_text(compl)
|
if (!match_at_original_text(compl)
|
||||||
&& !max_matches_found
|
&& !max_matches_found
|
||||||
&& (compl_leader.string == NULL
|
&& (compl_leader.string == NULL
|
||||||
|| ins_compl_equal(compl, compl_leader.string, (int)compl_leader.length)
|
|| ins_compl_equal(compl, compl_leader.string,
|
||||||
|
(int)compl_leader.length)
|
||||||
|| (fuzzy_filter && compl->cp_score > 0)))
|
|| (fuzzy_filter && compl->cp_score > 0)))
|
||||||
{
|
{
|
||||||
++compl_match_arraysize;
|
++compl_match_arraysize;
|
||||||
|
|||||||
@ -439,7 +439,7 @@ ignorecase(char_u *pat)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As ignorecase() put pass the "ic" and "scs" flags.
|
* As ignorecase() but pass the "ic" and "scs" flags.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
ignorecase_opt(char_u *pat, int ic_in, int scs)
|
ignorecase_opt(char_u *pat, int ic_in, int scs)
|
||||||
|
|||||||
@ -4213,6 +4213,93 @@ func Test_complete_append_selected_match_default()
|
|||||||
delfunc PrintMenuWords
|
delfunc PrintMenuWords
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test normal mode (^N/^P/^X^N/^X^P) with smartcase when 1) matches are first
|
||||||
|
" found and 2) matches are filtered (when a character is typed).
|
||||||
|
func Test_smartcase_normal_mode()
|
||||||
|
|
||||||
|
func! PrintMenu()
|
||||||
|
let info = complete_info(["matches"])
|
||||||
|
call map(info.matches, {_, v -> v.word})
|
||||||
|
return info
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! TestInner(key)
|
||||||
|
let pr = "\<c-r>=PrintMenu()\<cr>"
|
||||||
|
|
||||||
|
new
|
||||||
|
set completeopt=menuone,noselect ignorecase smartcase
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}{pr}"
|
||||||
|
call assert_equal('F{''matches'': [''Fast'', ''FAST'', ''False'',
|
||||||
|
\ ''FALSE'']}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}a{pr}"
|
||||||
|
call assert_equal('Fa{''matches'': [''Fast'', ''False'']}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}a\<bs>{pr}"
|
||||||
|
call assert_equal('F{''matches'': [''Fast'', ''FAST'', ''False'',
|
||||||
|
\ ''FALSE'']}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}ax{pr}"
|
||||||
|
call assert_equal('Fax{''matches'': []}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}ax\<bs>{pr}"
|
||||||
|
call assert_equal('Fa{''matches'': [''Fast'', ''False'']}', getline(1))
|
||||||
|
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}A{pr}"
|
||||||
|
call assert_equal('FA{''matches'': [''FAST'', ''FALSE'']}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}A\<bs>{pr}"
|
||||||
|
call assert_equal('F{''matches'': [''Fast'', ''FAST'', ''False'',
|
||||||
|
\ ''FALSE'']}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}AL{pr}"
|
||||||
|
call assert_equal('FAL{''matches'': [''FALSE'']}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}ALx{pr}"
|
||||||
|
call assert_equal('FALx{''matches'': []}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOF{a:key}ALx\<bs>{pr}"
|
||||||
|
call assert_equal('FAL{''matches'': [''FALSE'']}', getline(1))
|
||||||
|
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOf{a:key}{pr}"
|
||||||
|
call assert_equal('f{''matches'': [''Fast'', ''FAST'', ''False'', ''FALSE'',
|
||||||
|
\ ''fast'', ''false'']}', getline(1))
|
||||||
|
%d
|
||||||
|
call setline(1, ["Fast", "FAST", "False", "FALSE", "fast", "false"])
|
||||||
|
exe $"normal! ggOf{a:key}a{pr}"
|
||||||
|
call assert_equal('fa{''matches'': [''Fast'', ''FAST'', ''False'', ''FALSE'',
|
||||||
|
\ ''fast'', ''false'']}', getline(1))
|
||||||
|
|
||||||
|
%d
|
||||||
|
exe $"normal! ggOf{a:key}{pr}"
|
||||||
|
call assert_equal('f{''matches'': []}', getline(1))
|
||||||
|
exe $"normal! ggOf{a:key}a\<bs>{pr}"
|
||||||
|
call assert_equal('f{''matches'': []}', getline(1))
|
||||||
|
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 PrintMenu
|
||||||
|
delfunc TestInner
|
||||||
|
endfunc
|
||||||
|
|
||||||
" Test 'nearest' flag of 'completeopt'
|
" Test 'nearest' flag of 'completeopt'
|
||||||
func Test_nearest_cpt_option()
|
func Test_nearest_cpt_option()
|
||||||
|
|
||||||
|
|||||||
@ -704,6 +704,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1374,
|
||||||
/**/
|
/**/
|
||||||
1373,
|
1373,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
Reference in New Issue
Block a user