-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Add noselect
to wildmode
option
#16759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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 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 ``` M runtime/doc/options.txt M runtime/doc/tags M src/cmdexpand.c M src/ex_getln.c M src/option.h M src/testdir/gen_opt_test.vim M src/testdir/test_cmdline.vim M src/vim.h
M runtime/doc/tags
M src/testdir/test_cmdline.vim
src/testdir/test_cmdline.vim
Outdated
set wildmode=noselect:longest | ||
call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt') | ||
call assert_equal('"MyCmd one', @:) | ||
set wildmenu& |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tthe default value of 'wildmenu'
is on:
{"wildmenu", "wmnu", P_BOOL|P_VI_DEF,
(char_u *)&p_wmnu, PV_NONE, NULL, NULL,
{(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
And 'wildmenu'
is already on here, so this line doesn't do anything.
AD src/testdir/XtestDebug.vim M src/testdir/test_cmdline.vim A src/testdir/vimcmd
M src/testdir/test_cmdline.vim
Just playing around with it. I managed to get Vim to crash with this PR by doing the following:
|
Also, the examples listed were quite useful to demonstrate the use case! |
M src/optionstr.c
M src/testdir/test_cmdline.vim
Caused by segfault, when item gets deleted from completion list before being accessed. M src/ex_getln.c M src/testdir/test_cmdline.vim
Thanks for testing! Should be fixed now. |
thanks! |
@@ -974,7 +977,7 @@ cmdline_wildchar_complete( | |||
p_wmnu = 0; | |||
|
|||
// remove match | |||
nextwild(xp, WILD_PREV, 0, escape); | |||
nextwild(xp, WILD_PREV, 0 | (options & ~WIM_NOSELECT), escape); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look right. WIM_NOSELECT
is 0x10
, which is equal to WILD_ADD_SLASH
, which is never set in options
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And the 0 |
isn't needed here. But why do options
now need to be passed when they don't need to be passed before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering about that too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. It was supposed to be just nextwild(xp, WILD_PREV, options, escape);
(replace 0
with options
). Don't know how the meaningless thing got in there. options
is needed here to prevent xp_files from being emptied out if there is only 1 entry. For noselect
single element completion menu should be retained. options
contains WILD_KEEP_SOLE_ITEM
when noselect
is set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed #16778
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: vim#16551 closes: vim#16759 Signed-off-by: Girish Palya <girishji@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
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: vim/vim#16551 closes: vim/vim#16759 vim/vim@2bacc3e Co-authored-by: Girish Palya <girishji@gmail.com> Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
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: vim/vim#16551 closes: vim/vim#16759 vim/vim@2bacc3e Co-authored-by: Girish Palya <girishji@gmail.com> Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
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: vim/vim#16551 closes: vim/vim#16759 vim/vim@2bacc3e Co-authored-by: Girish Palya <girishji@gmail.com> Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
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: vim/vim#16551 closes: vim/vim#16759 vim/vim@2bacc3e Co-authored-by: Girish Palya <girishji@gmail.com> Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
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: vim/vim#16551 closes: vim/vim#16759 vim/vim@2bacc3e Cherry-pick Wildmode_Tests() change from patch 9.0.0418. Co-authored-by: Girish Palya <girishji@gmail.com> Co-authored-by: Tomas Slusny <slusnucky@gmail.com>
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: vim/vim#16551 closes: vim/vim#16759 vim/vim@2bacc3e Cherry-pick Wildmode_Tests() change from patch 9.0.0418. Co-authored-by: Girish Palya <girishji@gmail.com> Co-authored-by: Tomas Slusny <slusnucky@gmail.com>
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: vim/vim#16551 closes: vim/vim#16759 vim/vim@2bacc3e Co-authored-by: Girish Palya <girishji@gmail.com> Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
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: vim/vim#16551 closes: vim/vim#16759 vim/vim@2bacc3e Cherry-pick Wildmode_Tests() change from patch 9.0.0418. Co-authored-by: Girish Palya <girishji@gmail.com> Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
This update enables highlighting in the popup menu even when the matched fragment or pattern appears within an item (string) rather than only at the beginning. This is especially useful for custom completion, where menu items may not always start with the typed pattern. For specific use cases, refer to the two examples in vim#16759 A sliding window approach is used with direct string comparison. Performance is not a concern, as highlighting is applied only to displayed lines, even if the menu list is arbitrarily long. M src/popupmenu.c A src/testdir/dumps/Test_wildmenu_pum_hl_match_nonf.dump M src/testdir/test_cmdline.vim
Problem: wildmenu highlighting in popup can be improved Solution: Check if the completion items contain submatches of the entered text (Girish Palya). This update enables highlighting in the popup menu even when the matched fragment or pattern appears within an item (string) rather than only at the beginning. This is especially useful for custom completion, where menu items may not always start with the typed pattern. For specific use cases, refer to the two examples in #16759 A sliding window approach is used with direct string comparison. Performance is not a concern, as highlighting is applied only to displayed lines, even if the menu list is arbitrarily long. closes: #16785 Signed-off-by: Girish Palya <girishji@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: wildmenu highlighting in popup can be improved Solution: Check if the completion items contain submatches of the entered text (Girish Palya). This update enables highlighting in the popup menu even when the matched fragment or pattern appears within an item (string) rather than only at the beginning. This is especially useful for custom completion, where menu items may not always start with the typed pattern. For specific use cases, refer to the two examples in vim/vim#16759 A sliding window approach is used with direct string comparison. Performance is not a concern, as highlighting is applied only to displayed lines, even if the menu list is arbitrarily long. closes: vim/vim#16785 vim/vim@4ec46f3 Co-authored-by: Girish Palya <girishji@gmail.com>
…32728) Problem: wildmenu highlighting in popup can be improved Solution: Check if the completion items contain submatches of the entered text (Girish Palya). This update enables highlighting in the popup menu even when the matched fragment or pattern appears within an item (string) rather than only at the beginning. This is especially useful for custom completion, where menu items may not always start with the typed pattern. For specific use cases, refer to the two examples in vim/vim#16759 A sliding window approach is used with direct string comparison. Performance is not a concern, as highlighting is applied only to displayed lines, even if the menu list is arbitrarily long. closes: vim/vim#16785 vim/vim@4ec46f3 Co-authored-by: Girish Palya <girishji@gmail.com>
call assert_equal('"MyCmd o', @:) | ||
|
||
" When 'full' is present, complete after first <tab>. | ||
set wildmode=noselect,full |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to be order-sensitive. Does the test pass if this line is changed to:
set wildmode=full,noselect
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this just how 'wildmode'
works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That means set wildmode+=noselect
doesn't work with the default value. Seems strange, and probably should be documented if this is expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the docs say this?
It is a comma-separated list of up to four parts. Each part specifies what to do for each consecutive use of 'wildchar'. The first part specifies the behavior for the first use of 'wildchar', The second part for the second use, etc.
Each part consists of a colon separated list consisting of the following possible values:
…
It has always been order sensitive and the examples also don't use +=
. I do wonder if it noselect
ever makes sense to be not used as the first item in the element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not really. The docs you quoted are maybe barely understandable in these terms if you already know what you are looking for.
If set wildmode+=...
produces nonsense it should be an error, instead of pretending to support +=
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I'm just pointing out that it's not specific to this noselect
change, as the other values there are already order dependent for this option. That part of the docs has always been a bit hard to parse. I tried to give more examples in #8078 but maybe it's not clear enough.
I feel like clearing up the docs is better than just making +=
produce an error though, since it's not like it literally produces nonsense (unless you mean doing extra validation inside set wildmode+=
to check each value). It just adds a value to the end. The important part is understanding that wildmode
is order dependent (it's kind of impossible to properly configure this option without understanding that).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wildmode
option is inherently order-sensitive. Each comma-separated fragment determines the sequence in which wildchar
behaves. Within each fragment, the colon-separated parts define simultaneous effects for wildchar
. This is not clearly explained in the documentation. I considered rewriting the docs but chose not to include it in this PR.
Additionally, set wildmode=full,noselect
does not behave as expected. Ideally, the first wildchar
should open the menu, selecting and inserting the first item (full
), while the second wildchar
should unselect the item and revert the command-line change (noselect
). However, this use case is unlikely to be meaningful. In practice, the implementation simply ignores noselect
unless it appears before the first comma.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Problem: cannot get information about command line completion Solution: add CmdlineLeavePre autocommand and cmdcomplete_info() Vim script function (Girish Palya) This commit introduces two features to improve introspection and control over command-line completion in Vim: - Add CmdlineLeavePre autocmd event: A new event triggered just before leaving the command line and before CmdlineLeave. It allows capturing completion-related state that is otherwise cleared by the time CmdlineLeave fires. - Add cmdcomplete_info() Vim script function: Returns a Dictionary with details about the current command-line completion state. These are similar in spirit to InsertLeavePre and complete_info(), but focused on command-line mode. **Use case:** In [[PR #16759](#16759)], two examples demonstrate command-line completion: one for live grep, and another for fuzzy file finding. However, both examples share two key limitations: 1. **Broken history recall (`<Up>`)** When selecting a completion item via `<Tab>` or `<C-n>`, the original pattern used for searching (e.g., a regex or fuzzy string) is overwritten in the command-line history. This makes it impossible to recall the original query later. This is especially problematic for interactive grep workflows, where it’s useful to recall a previous search and simply select a different match from the menu. 2. **Lack of default selection on `<CR>`** Often, it’s helpful to allow `<CR>` (Enter) to accept the first match in the completion list, even when no item is explicitly selected. This behavior is particularly useful in fuzzy file finding. ---- Below are the updated examples incorporating these improvements: **Live grep, fuzzy find file, fuzzy find buffer:** ```vim command! -nargs=+ -complete=customlist,GrepComplete Grep VisitFile() def GrepComplete(arglead: string, cmdline: string, cursorpos: number): list<any> return arglead->len() > 1 ? systemlist($'grep -REIHns "{arglead}"' .. ' --exclude-dir=.git --exclude=".*" --exclude="tags" --exclude="*.swp"') : [] enddef def VisitFile() if (selected_match != null_string) var qfitem = getqflist({lines: [selected_match]}).items[0] if qfitem->has_key('bufnr') && qfitem.lnum > 0 var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos' exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}' setbufvar(qfitem.bufnr, '&buflisted', 1) endif endif enddef nnoremap <leader>g :Grep<space> nnoremap <leader>G :Grep <c-r>=expand("<cword>")<cr> command! -nargs=* -complete=customlist,FuzzyFind Find execute(selected_match != '' ? $'edit {selected_match}' : '') var allfiles: list<string> autocmd CmdlineEnter : allfiles = null_list def FuzzyFind(arglead: string, _: string, _: number): list<string> if allfiles == null_list allfiles = systemlist($'find {get(g:, "fzfind_root", ".")} \! \( -path "*/.git" -prune -o -name "*.swp" \) -type f -follow') endif return arglead == '' ? allfiles : allfiles->matchfuzzy(arglead) enddef nnoremap <leader><space> :<c-r>=execute('let fzfind_root="."')\|''<cr>Find<space><c-@> nnoremap <leader>fv :<c-r>=execute('let fzfind_root="$HOME/.vim"')\|''<cr>Find<space><c-@> nnoremap <leader>fV :<c-r>=execute('let fzfind_root="$VIMRUNTIME"')\|''<cr>Find<space><c-@> command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b ' .. selected_match->matchstr('\d\+')) def FuzzyBuffer(arglead: string, _: string, _: number): list<string> var bufs = execute('buffers', 'silent!')->split("\n") var altbuf = bufs->indexof((_, v) => v =~ '^\s*\d\+\s\+#') if altbuf != -1 [bufs[0], bufs[altbuf]] = [bufs[altbuf], bufs[0]] endif return arglead == '' ? bufs : bufs->matchfuzzy(arglead) enddef nnoremap <leader><bs> :Buffer <c-@> var selected_match = null_string autocmd CmdlineLeavePre : SelectItem() def SelectItem() selected_match = '' if getcmdline() =~ '^\s*\%(Grep\|Find\|Buffer\)\s' var info = cmdcomplete_info() if info != {} && info.pum_visible && !info.matches->empty() selected_match = info.selected != -1 ? info.matches[info.selected] : info.matches[0] setcmdline(info.cmdline_orig). # Preserve search pattern in history endif endif enddef ``` **Auto-completion snippet:** ```vim set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu autocmd CmdlineChanged : CmdComplete() def CmdComplete() var [cmdline, curpos] = [getcmdline(), getcmdpos()] if getchar(1, {number: true}) == 0 # Typehead is empty (no more pasted input) && !pumvisible() && curpos == cmdline->len() + 1 && cmdline =~ '\%(\w\|[*/:.-]\)$' && cmdline !~ '^\d\+$' # Reduce noise feedkeys("\<C-@>", "ti") SkipCmdlineChanged() # Suppress redundant completion attempts # Remove <C-@> that get inserted when no items are available timer_start(0, (_) => getcmdline()->substitute('\%x00', '', 'g')->setcmdline()) endif enddef cnoremap <expr> <up> SkipCmdlineChanged("\<up>") cnoremap <expr> <down> SkipCmdlineChanged("\<down>") autocmd CmdlineEnter : set bo+=error autocmd CmdlineLeave : set bo-=error def SkipCmdlineChanged(key = ''): string set ei+=CmdlineChanged timer_start(0, (_) => execute('set ei-=CmdlineChanged')) return key != '' ? ((pumvisible() ? "\<c-e>" : '') .. key) : '' enddef ``` These customizable snippets can serve as *lightweight* and *native* alternatives to picker plugins like **FZF** or **Telescope** for common, everyday workflows. Also, live grep snippet can replace **cscope** without the overhead of building its database. closes: #17115 Signed-off-by: Girish Palya <girishji@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: cannot get information about command line completion Solution: add CmdlineLeavePre autocommand and cmdcomplete_info() Vim script function (Girish Palya) This commit introduces two features to improve introspection and control over command-line completion in Vim: - Add CmdlineLeavePre autocmd event: A new event triggered just before leaving the command line and before CmdlineLeave. It allows capturing completion-related state that is otherwise cleared by the time CmdlineLeave fires. - Add cmdcomplete_info() Vim script function: Returns a Dictionary with details about the current command-line completion state. These are similar in spirit to InsertLeavePre and complete_info(), but focused on command-line mode. **Use case:** In [[PR vim/vim#16759](vim/vim#16759)], two examples demonstrate command-line completion: one for live grep, and another for fuzzy file finding. However, both examples share two key limitations: 1. **Broken history recall (`<Up>`)** When selecting a completion item via `<Tab>` or `<C-n>`, the original pattern used for searching (e.g., a regex or fuzzy string) is overwritten in the command-line history. This makes it impossible to recall the original query later. This is especially problematic for interactive grep workflows, where it’s useful to recall a previous search and simply select a different match from the menu. 2. **Lack of default selection on `<CR>`** Often, it’s helpful to allow `<CR>` (Enter) to accept the first match in the completion list, even when no item is explicitly selected. This behavior is particularly useful in fuzzy file finding. ---- Below are the updated examples incorporating these improvements: **Live grep, fuzzy find file, fuzzy find buffer:** ```vim command! -nargs=+ -complete=customlist,GrepComplete Grep VisitFile() def GrepComplete(arglead: string, cmdline: string, cursorpos: number): list<any> return arglead->len() > 1 ? systemlist($'grep -REIHns "{arglead}"' .. ' --exclude-dir=.git --exclude=".*" --exclude="tags" --exclude="*.swp"') : [] enddef def VisitFile() if (selected_match != null_string) var qfitem = getqflist({lines: [selected_match]}).items[0] if qfitem->has_key('bufnr') && qfitem.lnum > 0 var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos' exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}' setbufvar(qfitem.bufnr, '&buflisted', 1) endif endif enddef nnoremap <leader>g :Grep<space> nnoremap <leader>G :Grep <c-r>=expand("<cword>")<cr> command! -nargs=* -complete=customlist,FuzzyFind Find execute(selected_match != '' ? $'edit {selected_match}' : '') var allfiles: list<string> autocmd CmdlineEnter : allfiles = null_list def FuzzyFind(arglead: string, _: string, _: number): list<string> if allfiles == null_list allfiles = systemlist($'find {get(g:, "fzfind_root", ".")} \! \( -path "*/.git" -prune -o -name "*.swp" \) -type f -follow') endif return arglead == '' ? allfiles : allfiles->matchfuzzy(arglead) enddef nnoremap <leader><space> :<c-r>=execute('let fzfind_root="."')\|''<cr>Find<space><c-@> nnoremap <leader>fv :<c-r>=execute('let fzfind_root="$HOME/.vim"')\|''<cr>Find<space><c-@> nnoremap <leader>fV :<c-r>=execute('let fzfind_root="$VIMRUNTIME"')\|''<cr>Find<space><c-@> command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b ' .. selected_match->matchstr('\d\+')) def FuzzyBuffer(arglead: string, _: string, _: number): list<string> var bufs = execute('buffers', 'silent!')->split("\n") var altbuf = bufs->indexof((_, v) => v =~ '^\s*\d\+\s\+#') if altbuf != -1 [bufs[0], bufs[altbuf]] = [bufs[altbuf], bufs[0]] endif return arglead == '' ? bufs : bufs->matchfuzzy(arglead) enddef nnoremap <leader><bs> :Buffer <c-@> var selected_match = null_string autocmd CmdlineLeavePre : SelectItem() def SelectItem() selected_match = '' if getcmdline() =~ '^\s*\%(Grep\|Find\|Buffer\)\s' var info = cmdcomplete_info() if info != {} && info.pum_visible && !info.matches->empty() selected_match = info.selected != -1 ? info.matches[info.selected] : info.matches[0] setcmdline(info.cmdline_orig). # Preserve search pattern in history endif endif enddef ``` **Auto-completion snippet:** ```vim set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu autocmd CmdlineChanged : CmdComplete() def CmdComplete() var [cmdline, curpos] = [getcmdline(), getcmdpos()] if getchar(1, {number: true}) == 0 # Typehead is empty (no more pasted input) && !pumvisible() && curpos == cmdline->len() + 1 && cmdline =~ '\%(\w\|[*/:.-]\)$' && cmdline !~ '^\d\+$' # Reduce noise feedkeys("\<C-@>", "ti") SkipCmdlineChanged() # Suppress redundant completion attempts # Remove <C-@> that get inserted when no items are available timer_start(0, (_) => getcmdline()->substitute('\%x00', '', 'g')->setcmdline()) endif enddef cnoremap <expr> <up> SkipCmdlineChanged("\<up>") cnoremap <expr> <down> SkipCmdlineChanged("\<down>") autocmd CmdlineEnter : set bo+=error autocmd CmdlineLeave : set bo-=error def SkipCmdlineChanged(key = ''): string set ei+=CmdlineChanged timer_start(0, (_) => execute('set ei-=CmdlineChanged')) return key != '' ? ((pumvisible() ? "\<c-e>" : '') .. key) : '' enddef ``` These customizable snippets can serve as *lightweight* and *native* alternatives to picker plugins like **FZF** or **Telescope** for common, everyday workflows. Also, live grep snippet can replace **cscope** without the overhead of building its database. closes: vim/vim#17115 vim/vim@92f68e2 Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: cannot get information about command line completion Solution: add CmdlineLeavePre autocommand and cmdcomplete_info() Vim script function (Girish Palya) This commit introduces two features to improve introspection and control over command-line completion in Vim: - Add CmdlineLeavePre autocmd event: A new event triggered just before leaving the command line and before CmdlineLeave. It allows capturing completion-related state that is otherwise cleared by the time CmdlineLeave fires. - Add cmdcomplete_info() Vim script function: Returns a Dictionary with details about the current command-line completion state. These are similar in spirit to InsertLeavePre and complete_info(), but focused on command-line mode. **Use case:** In [[PR vim/vim#16759](vim/vim#16759)], two examples demonstrate command-line completion: one for live grep, and another for fuzzy file finding. However, both examples share two key limitations: 1. **Broken history recall (`<Up>`)** When selecting a completion item via `<Tab>` or `<C-n>`, the original pattern used for searching (e.g., a regex or fuzzy string) is overwritten in the command-line history. This makes it impossible to recall the original query later. This is especially problematic for interactive grep workflows, where it’s useful to recall a previous search and simply select a different match from the menu. 2. **Lack of default selection on `<CR>`** Often, it’s helpful to allow `<CR>` (Enter) to accept the first match in the completion list, even when no item is explicitly selected. This behavior is particularly useful in fuzzy file finding. ---- Below are the updated examples incorporating these improvements: **Live grep, fuzzy find file, fuzzy find buffer:** ```vim command! -nargs=+ -complete=customlist,GrepComplete Grep VisitFile() def GrepComplete(arglead: string, cmdline: string, cursorpos: number): list<any> return arglead->len() > 1 ? systemlist($'grep -REIHns "{arglead}"' .. ' --exclude-dir=.git --exclude=".*" --exclude="tags" --exclude="*.swp"') : [] enddef def VisitFile() if (selected_match != null_string) var qfitem = getqflist({lines: [selected_match]}).items[0] if qfitem->has_key('bufnr') && qfitem.lnum > 0 var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos' exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}' setbufvar(qfitem.bufnr, '&buflisted', 1) endif endif enddef nnoremap <leader>g :Grep<space> nnoremap <leader>G :Grep <c-r>=expand("<cword>")<cr> command! -nargs=* -complete=customlist,FuzzyFind Find execute(selected_match != '' ? $'edit {selected_match}' : '') var allfiles: list<string> autocmd CmdlineEnter : allfiles = null_list def FuzzyFind(arglead: string, _: string, _: number): list<string> if allfiles == null_list allfiles = systemlist($'find {get(g:, "fzfind_root", ".")} \! \( -path "*/.git" -prune -o -name "*.swp" \) -type f -follow') endif return arglead == '' ? allfiles : allfiles->matchfuzzy(arglead) enddef nnoremap <leader><space> :<c-r>=execute('let fzfind_root="."')\|''<cr>Find<space><c-@> nnoremap <leader>fv :<c-r>=execute('let fzfind_root="$HOME/.vim"')\|''<cr>Find<space><c-@> nnoremap <leader>fV :<c-r>=execute('let fzfind_root="$VIMRUNTIME"')\|''<cr>Find<space><c-@> command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b ' .. selected_match->matchstr('\d\+')) def FuzzyBuffer(arglead: string, _: string, _: number): list<string> var bufs = execute('buffers', 'silent!')->split("\n") var altbuf = bufs->indexof((_, v) => v =~ '^\s*\d\+\s\+#') if altbuf != -1 [bufs[0], bufs[altbuf]] = [bufs[altbuf], bufs[0]] endif return arglead == '' ? bufs : bufs->matchfuzzy(arglead) enddef nnoremap <leader><bs> :Buffer <c-@> var selected_match = null_string autocmd CmdlineLeavePre : SelectItem() def SelectItem() selected_match = '' if getcmdline() =~ '^\s*\%(Grep\|Find\|Buffer\)\s' var info = cmdcomplete_info() if info != {} && info.pum_visible && !info.matches->empty() selected_match = info.selected != -1 ? info.matches[info.selected] : info.matches[0] setcmdline(info.cmdline_orig). # Preserve search pattern in history endif endif enddef ``` **Auto-completion snippet:** ```vim set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu autocmd CmdlineChanged : CmdComplete() def CmdComplete() var [cmdline, curpos] = [getcmdline(), getcmdpos()] if getchar(1, {number: true}) == 0 # Typehead is empty (no more pasted input) && !pumvisible() && curpos == cmdline->len() + 1 && cmdline =~ '\%(\w\|[*/:.-]\)$' && cmdline !~ '^\d\+$' # Reduce noise feedkeys("\<C-@>", "ti") SkipCmdlineChanged() # Suppress redundant completion attempts # Remove <C-@> that get inserted when no items are available timer_start(0, (_) => getcmdline()->substitute('\%x00', '', 'g')->setcmdline()) endif enddef cnoremap <expr> <up> SkipCmdlineChanged("\<up>") cnoremap <expr> <down> SkipCmdlineChanged("\<down>") autocmd CmdlineEnter : set bo+=error autocmd CmdlineLeave : set bo-=error def SkipCmdlineChanged(key = ''): string set ei+=CmdlineChanged timer_start(0, (_) => execute('set ei-=CmdlineChanged')) return key != '' ? ((pumvisible() ? "\<c-e>" : '') .. key) : '' enddef ``` These customizable snippets can serve as *lightweight* and *native* alternatives to picker plugins like **FZF** or **Telescope** for common, everyday workflows. Also, live grep snippet can replace **cscope** without the overhead of building its database. closes: vim/vim#17115 vim/vim@92f68e2 Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: cannot get information about command line completion Solution: add CmdlineLeavePre autocommand and cmdcomplete_info() Vim script function (Girish Palya) This commit introduces two features to improve introspection and control over command-line completion in Vim: - Add CmdlineLeavePre autocmd event: A new event triggered just before leaving the command line and before CmdlineLeave. It allows capturing completion-related state that is otherwise cleared by the time CmdlineLeave fires. - Add cmdcomplete_info() Vim script function: Returns a Dictionary with details about the current command-line completion state. These are similar in spirit to InsertLeavePre and complete_info(), but focused on command-line mode. **Use case:** In [[PR vim/vim#16759](vim/vim#16759)], two examples demonstrate command-line completion: one for live grep, and another for fuzzy file finding. However, both examples share two key limitations: 1. **Broken history recall (`<Up>`)** When selecting a completion item via `<Tab>` or `<C-n>`, the original pattern used for searching (e.g., a regex or fuzzy string) is overwritten in the command-line history. This makes it impossible to recall the original query later. This is especially problematic for interactive grep workflows, where it’s useful to recall a previous search and simply select a different match from the menu. 2. **Lack of default selection on `<CR>`** Often, it’s helpful to allow `<CR>` (Enter) to accept the first match in the completion list, even when no item is explicitly selected. This behavior is particularly useful in fuzzy file finding. ---- Below are the updated examples incorporating these improvements: **Live grep, fuzzy find file, fuzzy find buffer:** ```vim command! -nargs=+ -complete=customlist,GrepComplete Grep VisitFile() def GrepComplete(arglead: string, cmdline: string, cursorpos: number): list<any> return arglead->len() > 1 ? systemlist($'grep -REIHns "{arglead}"' .. ' --exclude-dir=.git --exclude=".*" --exclude="tags" --exclude="*.swp"') : [] enddef def VisitFile() if (selected_match != null_string) var qfitem = getqflist({lines: [selected_match]}).items[0] if qfitem->has_key('bufnr') && qfitem.lnum > 0 var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos' exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}' setbufvar(qfitem.bufnr, '&buflisted', 1) endif endif enddef nnoremap <leader>g :Grep<space> nnoremap <leader>G :Grep <c-r>=expand("<cword>")<cr> command! -nargs=* -complete=customlist,FuzzyFind Find execute(selected_match != '' ? $'edit {selected_match}' : '') var allfiles: list<string> autocmd CmdlineEnter : allfiles = null_list def FuzzyFind(arglead: string, _: string, _: number): list<string> if allfiles == null_list allfiles = systemlist($'find {get(g:, "fzfind_root", ".")} \! \( -path "*/.git" -prune -o -name "*.swp" \) -type f -follow') endif return arglead == '' ? allfiles : allfiles->matchfuzzy(arglead) enddef nnoremap <leader><space> :<c-r>=execute('let fzfind_root="."')\|''<cr>Find<space><c-@> nnoremap <leader>fv :<c-r>=execute('let fzfind_root="$HOME/.vim"')\|''<cr>Find<space><c-@> nnoremap <leader>fV :<c-r>=execute('let fzfind_root="$VIMRUNTIME"')\|''<cr>Find<space><c-@> command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b ' .. selected_match->matchstr('\d\+')) def FuzzyBuffer(arglead: string, _: string, _: number): list<string> var bufs = execute('buffers', 'silent!')->split("\n") var altbuf = bufs->indexof((_, v) => v =~ '^\s*\d\+\s\+#') if altbuf != -1 [bufs[0], bufs[altbuf]] = [bufs[altbuf], bufs[0]] endif return arglead == '' ? bufs : bufs->matchfuzzy(arglead) enddef nnoremap <leader><bs> :Buffer <c-@> var selected_match = null_string autocmd CmdlineLeavePre : SelectItem() def SelectItem() selected_match = '' if getcmdline() =~ '^\s*\%(Grep\|Find\|Buffer\)\s' var info = cmdcomplete_info() if info != {} && info.pum_visible && !info.matches->empty() selected_match = info.selected != -1 ? info.matches[info.selected] : info.matches[0] setcmdline(info.cmdline_orig). # Preserve search pattern in history endif endif enddef ``` **Auto-completion snippet:** ```vim set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu autocmd CmdlineChanged : CmdComplete() def CmdComplete() var [cmdline, curpos] = [getcmdline(), getcmdpos()] if getchar(1, {number: true}) == 0 # Typehead is empty (no more pasted input) && !pumvisible() && curpos == cmdline->len() + 1 && cmdline =~ '\%(\w\|[*/:.-]\)$' && cmdline !~ '^\d\+$' # Reduce noise feedkeys("\<C-@>", "ti") SkipCmdlineChanged() # Suppress redundant completion attempts # Remove <C-@> that get inserted when no items are available timer_start(0, (_) => getcmdline()->substitute('\%x00', '', 'g')->setcmdline()) endif enddef cnoremap <expr> <up> SkipCmdlineChanged("\<up>") cnoremap <expr> <down> SkipCmdlineChanged("\<down>") autocmd CmdlineEnter : set bo+=error autocmd CmdlineLeave : set bo-=error def SkipCmdlineChanged(key = ''): string set ei+=CmdlineChanged timer_start(0, (_) => execute('set ei-=CmdlineChanged')) return key != '' ? ((pumvisible() ? "\<c-e>" : '') .. key) : '' enddef ``` These customizable snippets can serve as *lightweight* and *native* alternatives to picker plugins like **FZF** or **Telescope** for common, everyday workflows. Also, live grep snippet can replace **cscope** without the overhead of building its database. closes: vim/vim#17115 vim/vim@92f68e2 Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: cannot get information about command line completion Solution: add CmdlineLeavePre autocommand and cmdcomplete_info() Vim script function (Girish Palya) This commit introduces two features to improve introspection and control over command-line completion in Vim: - Add CmdlineLeavePre autocmd event: A new event triggered just before leaving the command line and before CmdlineLeave. It allows capturing completion-related state that is otherwise cleared by the time CmdlineLeave fires. - Add cmdcomplete_info() Vim script function: Returns a Dictionary with details about the current command-line completion state. These are similar in spirit to InsertLeavePre and complete_info(), but focused on command-line mode. **Use case:** In [[PR vim/vim#16759](vim/vim#16759)], two examples demonstrate command-line completion: one for live grep, and another for fuzzy file finding. However, both examples share two key limitations: 1. **Broken history recall (`<Up>`)** When selecting a completion item via `<Tab>` or `<C-n>`, the original pattern used for searching (e.g., a regex or fuzzy string) is overwritten in the command-line history. This makes it impossible to recall the original query later. This is especially problematic for interactive grep workflows, where it’s useful to recall a previous search and simply select a different match from the menu. 2. **Lack of default selection on `<CR>`** Often, it’s helpful to allow `<CR>` (Enter) to accept the first match in the completion list, even when no item is explicitly selected. This behavior is particularly useful in fuzzy file finding. ---- Below are the updated examples incorporating these improvements: **Live grep, fuzzy find file, fuzzy find buffer:** ```vim command! -nargs=+ -complete=customlist,GrepComplete Grep VisitFile() def GrepComplete(arglead: string, cmdline: string, cursorpos: number): list<any> return arglead->len() > 1 ? systemlist($'grep -REIHns "{arglead}"' .. ' --exclude-dir=.git --exclude=".*" --exclude="tags" --exclude="*.swp"') : [] enddef def VisitFile() if (selected_match != null_string) var qfitem = getqflist({lines: [selected_match]}).items[0] if qfitem->has_key('bufnr') && qfitem.lnum > 0 var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos' exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}' setbufvar(qfitem.bufnr, '&buflisted', 1) endif endif enddef nnoremap <leader>g :Grep<space> nnoremap <leader>G :Grep <c-r>=expand("<cword>")<cr> command! -nargs=* -complete=customlist,FuzzyFind Find execute(selected_match != '' ? $'edit {selected_match}' : '') var allfiles: list<string> autocmd CmdlineEnter : allfiles = null_list def FuzzyFind(arglead: string, _: string, _: number): list<string> if allfiles == null_list allfiles = systemlist($'find {get(g:, "fzfind_root", ".")} \! \( -path "*/.git" -prune -o -name "*.swp" \) -type f -follow') endif return arglead == '' ? allfiles : allfiles->matchfuzzy(arglead) enddef nnoremap <leader><space> :<c-r>=execute('let fzfind_root="."')\|''<cr>Find<space><c-@> nnoremap <leader>fv :<c-r>=execute('let fzfind_root="$HOME/.vim"')\|''<cr>Find<space><c-@> nnoremap <leader>fV :<c-r>=execute('let fzfind_root="$VIMRUNTIME"')\|''<cr>Find<space><c-@> command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b ' .. selected_match->matchstr('\d\+')) def FuzzyBuffer(arglead: string, _: string, _: number): list<string> var bufs = execute('buffers', 'silent!')->split("\n") var altbuf = bufs->indexof((_, v) => v =~ '^\s*\d\+\s\+#') if altbuf != -1 [bufs[0], bufs[altbuf]] = [bufs[altbuf], bufs[0]] endif return arglead == '' ? bufs : bufs->matchfuzzy(arglead) enddef nnoremap <leader><bs> :Buffer <c-@> var selected_match = null_string autocmd CmdlineLeavePre : SelectItem() def SelectItem() selected_match = '' if getcmdline() =~ '^\s*\%(Grep\|Find\|Buffer\)\s' var info = cmdcomplete_info() if info != {} && info.pum_visible && !info.matches->empty() selected_match = info.selected != -1 ? info.matches[info.selected] : info.matches[0] setcmdline(info.cmdline_orig). # Preserve search pattern in history endif endif enddef ``` **Auto-completion snippet:** ```vim set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu autocmd CmdlineChanged : CmdComplete() def CmdComplete() var [cmdline, curpos] = [getcmdline(), getcmdpos()] if getchar(1, {number: true}) == 0 # Typehead is empty (no more pasted input) && !pumvisible() && curpos == cmdline->len() + 1 && cmdline =~ '\%(\w\|[*/:.-]\)$' && cmdline !~ '^\d\+$' # Reduce noise feedkeys("\<C-@>", "ti") SkipCmdlineChanged() # Suppress redundant completion attempts # Remove <C-@> that get inserted when no items are available timer_start(0, (_) => getcmdline()->substitute('\%x00', '', 'g')->setcmdline()) endif enddef cnoremap <expr> <up> SkipCmdlineChanged("\<up>") cnoremap <expr> <down> SkipCmdlineChanged("\<down>") autocmd CmdlineEnter : set bo+=error autocmd CmdlineLeave : set bo-=error def SkipCmdlineChanged(key = ''): string set ei+=CmdlineChanged timer_start(0, (_) => execute('set ei-=CmdlineChanged')) return key != '' ? ((pumvisible() ? "\<c-e>" : '') .. key) : '' enddef ``` These customizable snippets can serve as *lightweight* and *native* alternatives to picker plugins like **FZF** or **Telescope** for common, everyday workflows. Also, live grep snippet can replace **cscope** without the overhead of building its database. closes: vim/vim#17115 vim/vim@92f68e2 Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: cannot get information about command line completion Solution: add CmdlineLeavePre autocommand and cmdcomplete_info() Vim script function (Girish Palya) This commit introduces two features to improve introspection and control over command-line completion in Vim: - Add CmdlineLeavePre autocmd event: A new event triggered just before leaving the command line and before CmdlineLeave. It allows capturing completion-related state that is otherwise cleared by the time CmdlineLeave fires. - Add cmdcomplete_info() Vim script function: Returns a Dictionary with details about the current command-line completion state. These are similar in spirit to InsertLeavePre and complete_info(), but focused on command-line mode. **Use case:** In [[PR vim/vim#16759](vim/vim#16759)], two examples demonstrate command-line completion: one for live grep, and another for fuzzy file finding. However, both examples share two key limitations: 1. **Broken history recall (`<Up>`)** When selecting a completion item via `<Tab>` or `<C-n>`, the original pattern used for searching (e.g., a regex or fuzzy string) is overwritten in the command-line history. This makes it impossible to recall the original query later. This is especially problematic for interactive grep workflows, where it’s useful to recall a previous search and simply select a different match from the menu. 2. **Lack of default selection on `<CR>`** Often, it’s helpful to allow `<CR>` (Enter) to accept the first match in the completion list, even when no item is explicitly selected. This behavior is particularly useful in fuzzy file finding. ---- Below are the updated examples incorporating these improvements: **Live grep, fuzzy find file, fuzzy find buffer:** ```vim command! -nargs=+ -complete=customlist,GrepComplete Grep VisitFile() def GrepComplete(arglead: string, cmdline: string, cursorpos: number): list<any> return arglead->len() > 1 ? systemlist($'grep -REIHns "{arglead}"' .. ' --exclude-dir=.git --exclude=".*" --exclude="tags" --exclude="*.swp"') : [] enddef def VisitFile() if (selected_match != null_string) var qfitem = getqflist({lines: [selected_match]}).items[0] if qfitem->has_key('bufnr') && qfitem.lnum > 0 var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos' exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}' setbufvar(qfitem.bufnr, '&buflisted', 1) endif endif enddef nnoremap <leader>g :Grep<space> nnoremap <leader>G :Grep <c-r>=expand("<cword>")<cr> command! -nargs=* -complete=customlist,FuzzyFind Find execute(selected_match != '' ? $'edit {selected_match}' : '') var allfiles: list<string> autocmd CmdlineEnter : allfiles = null_list def FuzzyFind(arglead: string, _: string, _: number): list<string> if allfiles == null_list allfiles = systemlist($'find {get(g:, "fzfind_root", ".")} \! \( -path "*/.git" -prune -o -name "*.swp" \) -type f -follow') endif return arglead == '' ? allfiles : allfiles->matchfuzzy(arglead) enddef nnoremap <leader><space> :<c-r>=execute('let fzfind_root="."')\|''<cr>Find<space><c-@> nnoremap <leader>fv :<c-r>=execute('let fzfind_root="$HOME/.vim"')\|''<cr>Find<space><c-@> nnoremap <leader>fV :<c-r>=execute('let fzfind_root="$VIMRUNTIME"')\|''<cr>Find<space><c-@> command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b ' .. selected_match->matchstr('\d\+')) def FuzzyBuffer(arglead: string, _: string, _: number): list<string> var bufs = execute('buffers', 'silent!')->split("\n") var altbuf = bufs->indexof((_, v) => v =~ '^\s*\d\+\s\+#') if altbuf != -1 [bufs[0], bufs[altbuf]] = [bufs[altbuf], bufs[0]] endif return arglead == '' ? bufs : bufs->matchfuzzy(arglead) enddef nnoremap <leader><bs> :Buffer <c-@> var selected_match = null_string autocmd CmdlineLeavePre : SelectItem() def SelectItem() selected_match = '' if getcmdline() =~ '^\s*\%(Grep\|Find\|Buffer\)\s' var info = cmdcomplete_info() if info != {} && info.pum_visible && !info.matches->empty() selected_match = info.selected != -1 ? info.matches[info.selected] : info.matches[0] setcmdline(info.cmdline_orig). # Preserve search pattern in history endif endif enddef ``` **Auto-completion snippet:** ```vim set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu autocmd CmdlineChanged : CmdComplete() def CmdComplete() var [cmdline, curpos] = [getcmdline(), getcmdpos()] if getchar(1, {number: true}) == 0 # Typehead is empty (no more pasted input) && !pumvisible() && curpos == cmdline->len() + 1 && cmdline =~ '\%(\w\|[*/:.-]\)$' && cmdline !~ '^\d\+$' # Reduce noise feedkeys("\<C-@>", "ti") SkipCmdlineChanged() # Suppress redundant completion attempts # Remove <C-@> that get inserted when no items are available timer_start(0, (_) => getcmdline()->substitute('\%x00', '', 'g')->setcmdline()) endif enddef cnoremap <expr> <up> SkipCmdlineChanged("\<up>") cnoremap <expr> <down> SkipCmdlineChanged("\<down>") autocmd CmdlineEnter : set bo+=error autocmd CmdlineLeave : set bo-=error def SkipCmdlineChanged(key = ''): string set ei+=CmdlineChanged timer_start(0, (_) => execute('set ei-=CmdlineChanged')) return key != '' ? ((pumvisible() ? "\<c-e>" : '') .. key) : '' enddef ``` These customizable snippets can serve as *lightweight* and *native* alternatives to picker plugins like **FZF** or **Telescope** for common, everyday workflows. Also, live grep snippet can replace **cscope** without the overhead of building its database. closes: vim/vim#17115 vim/vim@92f68e2 Co-authored-by: Girish Palya <girishji@gmail.com>
This PR introduces a new `wildtrigger()` function. See `:h wildtrigger()` `wildtrigger()` behaves like pressing the `wildchar,` but provides a more refined and controlled completion experience: - Suppresses beeps when no matches are found. - Avoids displaying irrelevant completions (like full command lists) when the prefix is insufficient or doesn't match. - Skips completion if the typeahead buffer has pending input or if a wildmenu is already active. - Does not print "..." before completion. This is an improvement on the `feedkeys()` based autocompletion script given in vim#16759. M runtime/doc/builtin.txt M runtime/doc/cmdline.txt M runtime/doc/options.txt M runtime/doc/tags M runtime/doc/usr_41.txt M src/cmdexpand.c M src/evalfunc.c M src/ex_getln.c M src/keymap.h M src/proto/ex_getln.pro M src/testdir/test_cmdline.vim M src/vim.h
Problem: cannot easily trigger wildcard expansion Solution: Introduce wildtrigger() function (Girish Palya) This PR introduces a new `wildtrigger()` function. See `:h wildtrigger()` `wildtrigger()` behaves like pressing the `wildchar,` but provides a more refined and controlled completion experience: - Suppresses beeps when no matches are found. - Avoids displaying irrelevant completions (like full command lists) when the prefix is insufficient or doesn't match. - Skips completion if the typeahead buffer has pending input or if a wildmenu is already active. - Does not print "..." before completion. This is an improvement on the `feedkeys()` based autocompletion script given in #16759. closes: #17806 Signed-off-by: Girish Palya <girishji@gmail.com> Co-authored-by: zeertzjq <zeertzjq@outlook.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: cannot easily trigger wildcard expansion Solution: Introduce wildtrigger() function (Girish Palya) This PR introduces a new `wildtrigger()` function. See `:h wildtrigger()` `wildtrigger()` behaves like pressing the `wildchar,` but provides a more refined and controlled completion experience: - Suppresses beeps when no matches are found. - Avoids displaying irrelevant completions (like full command lists) when the prefix is insufficient or doesn't match. - Skips completion if the typeahead buffer has pending input or if a wildmenu is already active. - Does not print "..." before completion. This is an improvement on the `feedkeys()` based autocompletion script given in vim/vim#16759. closes: vim/vim#17806 vim/vim@b486ed8 While at it, also make Ctrl-Z trigger search completion. Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: cannot easily trigger wildcard expansion Solution: Introduce wildtrigger() function (Girish Palya) This PR introduces a new `wildtrigger()` function. See `:h wildtrigger()` `wildtrigger()` behaves like pressing the `wildchar,` but provides a more refined and controlled completion experience: - Suppresses beeps when no matches are found. - Avoids displaying irrelevant completions (like full command lists) when the prefix is insufficient or doesn't match. - Skips completion if the typeahead buffer has pending input or if a wildmenu is already active. - Does not print "..." before completion. This is an improvement on the `feedkeys()` based autocompletion script given in vim/vim#16759. closes: vim/vim#17806 vim/vim@b486ed8 While at it, also make Ctrl-Z trigger search completion. Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: cannot easily trigger wildcard expansion Solution: Introduce wildtrigger() function (Girish Palya) This PR introduces a new `wildtrigger()` function. See `:h wildtrigger()` `wildtrigger()` behaves like pressing the `wildchar,` but provides a more refined and controlled completion experience: - Suppresses beeps when no matches are found. - Avoids displaying irrelevant completions (like full command lists) when the prefix is insufficient or doesn't match. - Skips completion if the typeahead buffer has pending input or if a wildmenu is already active. - Does not print "..." before completion. This is an improvement on the `feedkeys()` based autocompletion script given in vim/vim#16759. closes: vim/vim#17806 vim/vim@b486ed8 While at it, also make Ctrl-Z trigger search completion. Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: cannot easily trigger wildcard expansion Solution: Introduce wildtrigger() function (Girish Palya) This PR introduces a new `wildtrigger()` function. See `:h wildtrigger()` `wildtrigger()` behaves like pressing the `wildchar,` but provides a more refined and controlled completion experience: - Suppresses beeps when no matches are found. - Avoids displaying irrelevant completions (like full command lists) when the prefix is insufficient or doesn't match. - Skips completion if the typeahead buffer has pending input or if a wildmenu is already active. - Does not print "..." before completion. This is an improvement on the `feedkeys()` based autocompletion script given in vim/vim#16759. closes: vim/vim#17806 vim/vim@b486ed8 While at it, also make Ctrl-Z trigger search completion. Co-authored-by: Girish Palya <girishji@gmail.com>
When
noselect
is present inwildmode
andwildmenu
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 theCmdlineChanged
event to insertwildchar(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 snippet demonstrates how to configure auto-completion using
noselect
:Edit:
Here are a few examples that utilize the above auto-completion mechanism:
Fuzzy Find:
Grep (Interactive):