patch 9.1.1374: completion: 'smartcase' not respected when filtering matches

Commit: 
https://github.com/vim/vim/commit/dc314053e121b0a995bdfbcdd2f03ce228e14eb3
Author: Girish Palya <giris...@gmail.com>
Date:   Thu May 8 23:28:52 2025 +0200

    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 <giris...@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index f52cf40ad..f3d92b290 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -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
@@ -1347,6 +1347,7 @@ use all space available.
 The 'pumwidth' option can be used to set a minimum width.  The default is 15
 characters.
 
+                                                       *compl-states*
 There are three states:
 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
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index bed14abb1..f3824cdbf 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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
@@ -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
        case characters.  Only used when the search pattern is typed and
        'ignorecase' option is on.  Used for the commands "/", "?", "n", "N",
-       ":g" and ":s".  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>.
+       ":g" and ":s" and when filtering matches for the completion menu
+       |compl-states|.
+       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.
 
                             *'smartindent'* *'si'* *'nosmartindent'* *'nosi'*
diff --git a/runtime/doc/tags b/runtime/doc/tags
index bb0558dab..ae80f0e6d 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -6645,6 +6645,7 @@ compl-keyword     insert.txt      /*compl-keyword*
 compl-omni     insert.txt      /*compl-omni*
 compl-omni-filetypes   insert.txt      /*compl-omni-filetypes*
 compl-spelling insert.txt      /*compl-spelling*
+compl-states   insert.txt      /*compl-states*
 compl-stop     insert.txt      /*compl-stop*
 compl-tag      insert.txt      /*compl-tag*
 compl-thesaurus        insert.txt      /*compl-thesaurus*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index afa800d86..371e4b314 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -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
@@ -41626,6 +41626,7 @@ Completion: ~
   "{flag}^<limit>" notation
 - add ":filetype" command completion
 - add "filetypecmd" completion type for |getcompletion()|
+- 'smartcase' applies to completion filtering
 
 Options: ~
 - the default for 'commentstring' contains whitespace padding to have
diff --git a/src/insexpand.c b/src/insexpand.c
index 3839586db..7bbff4ecb 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -1507,7 +1507,7 @@ ins_compl_build_pum(void)
                match_count = 1;
                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;
                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)
                && !max_matches_found
                && (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)))
        {
            ++compl_match_arraysize;
diff --git a/src/search.c b/src/search.c
index 5222a433d..ea7e65492 100644
--- a/src/search.c
+++ b/src/search.c
@@ -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
 ignorecase_opt(char_u *pat, int ic_in, int scs)
diff --git a/src/testdir/test_ins_complete.vim 
b/src/testdir/test_ins_complete.vim
index 73565a56e..6f342ae46 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -4213,6 +4213,93 @@ func Test_complete_append_selected_match_default()
   delfunc PrintMenuWords
 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'
 func Test_nearest_cpt_option()
 
diff --git a/src/version.c b/src/version.c
index 5b85a2bbd..236306eb2 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1374,
 /**/
     1373,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1uD93A-00GFwo-Is%40256bit.org.

Raspunde prin e-mail lui