patch 9.1.1166: command-line auto-completion hard with wildmenu

Problem:  command-line auto-completion hard with wildmenu
Solution: implement "noselect" wildoption value (Girish Palya)

When `noselect` is present in `wildmode` and 'wildmenu' is enabled, the
completion menu appears without pre-selecting the first item.

This change makes it easier to implement command-line auto-completion,
where the menu dynamically appears as characters are typed, and `<Tab>`
can be used to manually select an item. This can be achieved by
leveraging the `CmdlineChanged` event to insert `wildchar(m)`,
triggering completion menu.

Without this change, auto-completion using the 'wildmenu' mechanism is
not feasible, as it automatically inserts the first match, preventing
dynamic selection.

The following Vimscript snippet demonstrates how to configure
auto-completion using `noselect`:

```vim
vim9script
set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu
autocmd CmdlineChanged : timer_start(0, function(CmdComplete, [getcmdline()]))

def CmdComplete(cur_cmdline: string, timer: number)
  var [cmdline, curpos] = [getcmdline(), getcmdpos()]
  if cur_cmdline ==# cmdline  # Avoid completing each character in keymaps and pasted text
    && !pumvisible() && curpos == cmdline->len() + 1

    if cmdline[curpos - 2] =~ '[\w*/:]'  # Reduce noise by completing only selected characters
      feedkeys("\<C-@>", "ti")
      set eventignore+=CmdlineChanged  # Suppress redundant completion attempts
      timer_start(0, (_) => {
        getcmdline()->substitute('\%x00$', '', '')->setcmdline()  # Remove <C-@> if no completion items exist
        set eventignore-=CmdlineChanged
      })
    endif
  endif
enddef
```

fixes: #16551
closes: #16759

Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index be4ae4e..2e9b32b 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -2170,16 +2170,52 @@
   call assert_equal('AAA    AAAA   AAAAA', g:Sline)
   call assert_equal('"b A', @:)
 
+  " When 'wildmenu' is not set, 'noselect' completes first item
+  set wildmode=noselect
+  call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneA', @:)
+
+  " When 'noselect' is present, do not complete first <tab>.
+  set wildmenu
+  set wildmode=noselect
+  call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+  call feedkeys(":MyCmd o\t\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+  call feedkeys(":MyCmd o\t\t\<C-Y>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+
+  " When 'full' is present, complete after first <tab>.
+  set wildmode=noselect,full
+  call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+  call feedkeys(":MyCmd o\t\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneA', @:)
+  call feedkeys(":MyCmd o\t\t\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneB', @:)
+  call feedkeys(":MyCmd o\t\t\t\<C-Y>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneB', @:)
+
+  " 'noselect' has no effect when 'longest' is present.
+  set wildmode=noselect:longest
+  call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd one', @:)
+
+  " Complete 'noselect' value in 'wildmode' option
+  set wildmode&
+  call feedkeys(":set wildmode=n\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set wildmode=noselect', @:)
+  call feedkeys(":set wildmode=\t\t\t\t\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set wildmode=noselect', @:)
+
   " when using longest completion match, matches shorter than the argument
   " should be ignored (happens with :help)
   set wildmode=longest,full
-  set wildmenu
   call feedkeys(":help a*\t\<C-B>\"\<CR>", 'xt')
   call assert_equal('"help a', @:)
   " non existing file
   call feedkeys(":e a1b2y3z4\t\<C-B>\"\<CR>", 'xt')
   call assert_equal('"e a1b2y3z4', @:)
-  set wildmenu&
 
   " Test for longest file name completion with 'fileignorecase'
   " On MS-Windows, file names are case insensitive.
@@ -2199,6 +2235,21 @@
     set fileignorecase&
   endif
 
+  " If 'noselect' is present, single item menu should not insert item
+  func! T(a, c, p)
+    return "oneA"
+  endfunc
+  command! -nargs=1 -complete=custom,T MyCmd
+  set wildmode=noselect,full
+  call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+  call feedkeys(":MyCmd o\t\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneA', @:)
+  " 'nowildmenu' should make 'noselect' ineffective
+  set nowildmenu
+  call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneA', @:)
+
   %argdelete
   delcommand MyCmd
   delfunc T