-
-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Feat: Extend 'wildmode' Completion to Search Pattern Contexts
#17570
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
|
Oh very nice.
However that is a bit inconsistent. Would it work to use |
It would work and is relatively straightforward to implement. However, let's wait a few days to see if there are any objections since it does break backward compatibility. |
Now 'wildchar' and 'wildcharm' have compatible behaviour. Thanks for feedback. |
|
Here are a few things I just remembered:
|
|
One more thing: If your pattern includes a beginning-of-line or beginning-of-word anchor (like |
|
Any other comments? |
|
Thanks, this looks handy. Is there a reason not to include other commands accepting pattern args like |
|
|
||
| #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) | ||
| // Clear highlighting applied during wildmenu activity | ||
| set_no_hlsearch(TRUE); |
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.
should this be done only conditionally if (wild_menu_showing) ?
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.
It is already checked though:
Line 4298 in a3f6487
| if (!p_wmnu || wild_menu_showing == 0) |
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 wasn’t able to write a test for this, as even RunVimInTerminal() doesn't retain hlsearch highlights when the completion menu is visible during a screen dump. A potential test would involve triggering the completion menu with hlsearch active, then sending <C-C> or <Esc> and check that highlighting has disappeared.
src/cmdexpand.c
Outdated
| magic = (c == 'v') ? 1 : (c == 'V' ? 2 : magic); | ||
| if (c == 'v' || c == 'V' || c == 'm' || c == 'M' || c == 'c' | ||
| || c == 'C') | ||
| i += 2; |
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 seems a bit brittle, as the magicness could also be later in the pattern (e.g. the engine always has to come first). Should you use skip_regexp_ex() instead?
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.
skip_regexp_ex() doesn't quite work for this use case. The core requirement is to preserve the < or ^ tags when the user types them at the beginning of a pattern—otherwise, the completed matches may not be accurate during the actual search.
The interest in the 'magic' tag is primarily to determine whether a backslash should be included before < or ^. Ideally, these tags should be preserved anywhere in the pattern, but handling that comprehensively would add significant complexity.
The problem with skip_regexp_ex() is that it simply identifies the magic type used in the pattern, which isn't sufficient. It also skips over character classes like [], which is undesirable in this context.
One alternative I tried was retaining the full regex pattern as typed (including wildcards) and only completing the last word. However, this approach introduces ambiguity when ignorecase and smartcase are enabled, making it harder to determine correct matches.
Strictly speaking, the code that preserves < and ^ at the beginning of the pattern is a bit of a hack. But it's practical, effective, and in my opinion, necessary—removing it would break a key part of the user experience. The current solution is simple, handles 99% of use cases, and as far as I can tell, is not brittle.
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.
Changed approach:
I've made significant updates. Now, the matches retain the exact regex pattern typed by the user, and the completed word is appended directly to that pattern.
This preserves all regex elements — such as <, ^, grouping brackets, wildcards, and other special characters — in their original positions. Matching also correctly respects 'ignorecase' and 'smartcase'.
I've removed the previous code you commented on. Please take another look and review the new implementation.
src/cmdexpand.c
Outdated
| for (lnum = start->lnum + 1; lnum < end->lnum; lnum++) | ||
| { | ||
| line = ml_get(lnum); | ||
| int line_len = (int)STRLEN(line); |
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.
| int line_len = (int)STRLEN(line); | |
| int line_len = ml_get_len(line); |
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 seem correct. Did you mean ml_get_len(lnum)? In any case, we need to retrieve the entire line as a string through (ml_get()), so STRLEN() seems appropriate.
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.
yes, sorry, ml_get_len(lnum). This saves us a call to STRLEN().
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 understand now. buf->b_ml.ml_line_textlen already has length calculated. Good call.
|
BTW: how does it handle empty patterns, like |
|
Patterns that match everything are already skipped: Line 313 in a3f6487
|
chrisbra
left a comment
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.
Thanks, I am fine except with the single comment. Can you please squash into a single commit?
src/cmdexpand.c
Outdated
| for (lnum = start->lnum + 1; lnum < end->lnum; lnum++) | ||
| { | ||
| line = ml_get(lnum); | ||
| int line_len = (int)STRLEN(line); |
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.
yes, sorry, ml_get_len(lnum). This saves us a call to STRLEN().
This change enhances Vim's command-line completion by extending 'wildmode' behavior to search pattern contexts, including: * '/' and '?' search commands * ':s', ':g', ':v', and ':vim' commands Completions preserve the exact regex pattern typed by the user, appending the completed word directly to the original input. This ensures that all regex elements — such as '<', '^', grouping brackets '()', wildcards '\*', '.', and other special characters — remain intact and in their original positions.
Squashed, thanks. |
|
Sorry, you seem to have reverted v9.1.1475 commit accidentally |
M src/insexpand.c
Fixed. Sorry about that. |
|
thanks for this. Very nice! |
Problem: missing out-of-memory checks in cmdexpand.c
Solution: add out-of-memory checks for expand_files_and_dirs(),
ExpandUserDefined() and ExpandUserList()
(John Marriott)
closes: #17570
Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: 'wildchar' does not work in search contexts
Solution: implement search completion when 'wildchar' is typed
(Girish Palya).
This change enhances Vim's command-line completion by extending
'wildmode' behavior to search pattern contexts, including:
- '/' and '?' search commands
- ':s', ':g', ':v', and ':vim' commands
Completions preserve the exact regex pattern typed by the user,
appending the completed word directly to the original input. This
ensures that all regex elements — such as '<', '^', grouping brackets
'()', wildcards '\*', '.', and other special characters — remain intact
and in their original positions.
---
**Use Case**
While searching (using `/` or `?`) for lines containing a pattern like
`"foobar"`, you can now type a partial pattern (e.g., `/f`) followed by
a trigger key (`wildchar`) to open a **popup completion menu** showing
all matching words.
This offers two key benefits:
1. **Precision**: Select the exact word you're looking for without
typing it fully.
2. **Memory aid**: When you can’t recall a full function or variable
name, typing a few letters helps you visually identify and complete the
correct symbol.
---
**What’s New**
Completion is now supported in the following contexts:
- `/` and `?` search commands
- `:s`, `:g`, `:v`, and `:vimgrep` ex-commands
---
**Design Notes**
- While `'wildchar'` (usually `<Tab>`) triggers completion, you'll have
to use `<CTRL-V><Tab>` or "\t" to search for a literal tab.
- **Responsiveness**: Search remains responsive because it checks for
user input frequently.
---
**Try It Out**
Basic setup using the default `<Tab>` as the completion trigger:
```vim
set wim=noselect,full wop=pum wmnu
```
Now type:
```
/foo<Tab>
```
This opens a completion popup for matches containing "foo".
For matches beginning with "foo" type `/\<foo<Tab>`.
---
**Optional: Autocompletion**
For automatic popup menu completion as you type in search or `:`
commands, include this in your `.vimrc`:
```vim
vim9script
set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu
autocmd CmdlineChanged [:/?] CmdComplete()
def CmdComplete()
var [cmdline, curpos, cmdmode] = [getcmdline(), getcmdpos(),
expand('<afile>') == ':']
var trigger_char = '\%(\w\|[*/:.-]\)$'
var not_trigger_char = '^\%(\d\|,\|+\|-\)\+$' # Exclude numeric range
if getchar(1, {number: true}) == 0 # Typehead is empty, no more
pasted input
&& !wildmenumode() && curpos == cmdline->len() + 1
&& (!cmdmode || (cmdline =~ trigger_char && cmdline !~
not_trigger_char))
SkipCmdlineChanged()
feedkeys("\<C-@>", "t")
timer_start(0, (_) => getcmdline()->substitute('\%x00', '',
'ge')->setcmdline()) # Remove <C-@>
endif
enddef
def SkipCmdlineChanged(key = ''): string
set ei+=CmdlineChanged
timer_start(0, (_) => execute('set ei-=CmdlineChanged'))
return key == '' ? '' : ((wildmenumode() ? "\<C-E>" : '') .. key)
enddef
**Optional: Preserve history recall behavior**
cnoremap <expr> <Up> SkipCmdlineChanged("\<Up>")
cnoremap <expr> <Down> SkipCmdlineChanged("\<Down>")
**Optional: Customize popup height**
autocmd CmdlineEnter : set bo+=error | exec $'set ph={max([10,
winheight(0) - 4])}'
autocmd CmdlineEnter [/?] set bo+=error | set ph=8
autocmd CmdlineLeave [:/?] set bo-=error ph&
```
closes: vim#17570
Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
|
Thanks! |
Problem: 'wildchar' does not work in search contexts
Solution: implement search completion when 'wildchar' is typed
(Girish Palya).
This change enhances Vim's command-line completion by extending
'wildmode' behavior to search pattern contexts, including:
- '/' and '?' search commands
- ':s', ':g', ':v', and ':vim' commands
Completions preserve the exact regex pattern typed by the user,
appending the completed word directly to the original input. This
ensures that all regex elements — such as '<', '^', grouping brackets
'()', wildcards '\*', '.', and other special characters — remain intact
and in their original positions.
---
**Use Case**
While searching (using `/` or `?`) for lines containing a pattern like
`"foobar"`, you can now type a partial pattern (e.g., `/f`) followed by
a trigger key (`wildchar`) to open a **popup completion menu** showing
all matching words.
This offers two key benefits:
1. **Precision**: Select the exact word you're looking for without
typing it fully.
2. **Memory aid**: When you can’t recall a full function or variable
name, typing a few letters helps you visually identify and complete the
correct symbol.
---
**What’s New**
Completion is now supported in the following contexts:
- `/` and `?` search commands
- `:s`, `:g`, `:v`, and `:vimgrep` ex-commands
---
**Design Notes**
- While `'wildchar'` (usually `<Tab>`) triggers completion, you'll have
to use `<CTRL-V><Tab>` or "\t" to search for a literal tab.
- **Responsiveness**: Search remains responsive because it checks for
user input frequently.
---
**Try It Out**
Basic setup using the default `<Tab>` as the completion trigger:
```vim
set wim=noselect,full wop=pum wmnu
```
Now type:
```
/foo<Tab>
```
This opens a completion popup for matches containing "foo".
For matches beginning with "foo" type `/\<foo<Tab>`.
---
**Optional: Autocompletion**
For automatic popup menu completion as you type in search or `:`
commands, include this in your `.vimrc`:
```vim
vim9script
set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu
autocmd CmdlineChanged [:/?] CmdComplete()
def CmdComplete()
var [cmdline, curpos, cmdmode] = [getcmdline(), getcmdpos(),
expand('<afile>') == ':']
var trigger_char = '\%(\w\|[*/:.-]\)$'
var not_trigger_char = '^\%(\d\|,\|+\|-\)\+$' # Exclude numeric range
if getchar(1, {number: true}) == 0 # Typehead is empty, no more
pasted input
&& !wildmenumode() && curpos == cmdline->len() + 1
&& (!cmdmode || (cmdline =~ trigger_char && cmdline !~
not_trigger_char))
SkipCmdlineChanged()
feedkeys("\<C-@>", "t")
timer_start(0, (_) => getcmdline()->substitute('\%x00', '',
'ge')->setcmdline()) # Remove <C-@>
endif
enddef
def SkipCmdlineChanged(key = ''): string
set ei+=CmdlineChanged
timer_start(0, (_) => execute('set ei-=CmdlineChanged'))
return key == '' ? '' : ((wildmenumode() ? "\<C-E>" : '') .. key)
enddef
**Optional: Preserve history recall behavior**
cnoremap <expr> <Up> SkipCmdlineChanged("\<Up>")
cnoremap <expr> <Down> SkipCmdlineChanged("\<Down>")
**Optional: Customize popup height**
autocmd CmdlineEnter : set bo+=error | exec $'set ph={max([10,
winheight(0) - 4])}'
autocmd CmdlineEnter [/?] set bo+=error | set ph=8
autocmd CmdlineLeave [:/?] set bo-=error ph&
```
closes: vim/vim#17570
vim/vim@6b49fba
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: 'wildchar' does not work in search contexts
Solution: implement search completion when 'wildchar' is typed
(Girish Palya).
This change enhances Vim's command-line completion by extending
'wildmode' behavior to search pattern contexts, including:
- '/' and '?' search commands
- ':s', ':g', ':v', and ':vim' commands
Completions preserve the exact regex pattern typed by the user,
appending the completed word directly to the original input. This
ensures that all regex elements — such as '<', '^', grouping brackets
'()', wildcards '\*', '.', and other special characters — remain intact
and in their original positions.
---
**Use Case**
While searching (using `/` or `?`) for lines containing a pattern like
`"foobar"`, you can now type a partial pattern (e.g., `/f`) followed by
a trigger key (`wildchar`) to open a **popup completion menu** showing
all matching words.
This offers two key benefits:
1. **Precision**: Select the exact word you're looking for without
typing it fully.
2. **Memory aid**: When you can’t recall a full function or variable
name, typing a few letters helps you visually identify and complete the
correct symbol.
---
**What’s New**
Completion is now supported in the following contexts:
- `/` and `?` search commands
- `:s`, `:g`, `:v`, and `:vimgrep` ex-commands
---
**Design Notes**
- While `'wildchar'` (usually `<Tab>`) triggers completion, you'll have
to use `<CTRL-V><Tab>` or "\t" to search for a literal tab.
- **Responsiveness**: Search remains responsive because it checks for
user input frequently.
---
**Try It Out**
Basic setup using the default `<Tab>` as the completion trigger:
```vim
set wim=noselect,full wop=pum wmnu
```
Now type:
```
/foo<Tab>
```
This opens a completion popup for matches containing "foo".
For matches beginning with "foo" type `/\<foo<Tab>`.
---
**Optional: Autocompletion**
For automatic popup menu completion as you type in search or `:`
commands, include this in your `.vimrc`:
```vim
vim9script
set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu
autocmd CmdlineChanged [:/?] CmdComplete()
def CmdComplete()
var [cmdline, curpos, cmdmode] = [getcmdline(), getcmdpos(),
expand('<afile>') == ':']
var trigger_char = '\%(\w\|[*/:.-]\)$'
var not_trigger_char = '^\%(\d\|,\|+\|-\)\+$' # Exclude numeric range
if getchar(1, {number: true}) == 0 # Typehead is empty, no more
pasted input
&& !wildmenumode() && curpos == cmdline->len() + 1
&& (!cmdmode || (cmdline =~ trigger_char && cmdline !~
not_trigger_char))
SkipCmdlineChanged()
feedkeys("\<C-@>", "t")
timer_start(0, (_) => getcmdline()->substitute('\%x00', '',
'ge')->setcmdline()) # Remove <C-@>
endif
enddef
def SkipCmdlineChanged(key = ''): string
set ei+=CmdlineChanged
timer_start(0, (_) => execute('set ei-=CmdlineChanged'))
return key == '' ? '' : ((wildmenumode() ? "\<C-E>" : '') .. key)
enddef
**Optional: Preserve history recall behavior**
cnoremap <expr> <Up> SkipCmdlineChanged("\<Up>")
cnoremap <expr> <Down> SkipCmdlineChanged("\<Down>")
**Optional: Customize popup height**
autocmd CmdlineEnter : set bo+=error | exec $'set ph={max([10,
winheight(0) - 4])}'
autocmd CmdlineEnter [/?] set bo+=error | set ph=8
autocmd CmdlineLeave [:/?] set bo-=error ph&
```
closes: vim/vim#17570
vim/vim@6b49fba
Co-authored-by: Girish Palya <girishji@gmail.com>
This comment was marked as off-topic.
This comment was marked as off-topic.
Try |
This comment was marked as off-topic.
This comment was marked as off-topic.
|
Very cool PR, ty for working on this. I found an issue. If you load above snippet with
I assume this is some setting set by help filetype plugin that interferes. (it does not happen without |
You forgot to post what is in |
Meaning your autocomplete func |
Extend
'wildmode'Completion to Search Pattern ContextsThis change enables
'wildmode'popup completion in search pattern contexts, enhancing usability when searching for or substituting text in a large codebase.Use Case
While searching (using
/or?) for lines containing a pattern like"foobar", you can now type a partial pattern (e.g.,/f) followed by a trigger key (wildcharm) to open a popup completion menu showing all matching words.This offers two key benefits:
What’s New
Completion is now supported in the following contexts:
/and?search commands:s,:g,:v, and:vimcommandsDesign Notes
Only
'wildcharm'triggers completion in search contexts, not'wildchar'.'wildchar'(usually<Tab>) may be intended to insert a literal tab character in searches or substitution patterns.Responsiveness: Search remains responsive because it checks for user input frequently.
Try It Out
Basic setup using
<C-Z>as the completion trigger:Now type:
This opens a completion popup for matches containing "foo".
For matches beginning with "foo" type
/\<foo<C-Z>.⚡ Optional: Autocompletion
For automatic popup menu completion as you type in search or
:commands, include this in your.vimrc: