-*options.txt* For Vim version 9.1. Last change: 2025 Jul 05
+*options.txt* For Vim version 9.1. Last change: 2025 Jul 08
VIM REFERENCE MANUAL by Bram Moolenaar
< 'wildchar' also enables completion in search pattern contexts such as
|/|, |?|, |:s|, |:g|, |:v|, and |:vim|. To insert a literal <Tab>
instead of triggering completion, type <C-V><Tab> or "\t".
+ See also |'wildoptions'|.
NOTE: This option is set to the Vi default value when 'compatible' is
set and to the Vim default value when 'compatible' is reset.
A list of words that change how |cmdline-completion| is done.
The following values are supported:
+ exacttext When this flag is present, search pattern completion
+ (e.g., in |/|, |?|, |:s|, |:g|, |:v|, and |:vim|)
+ shows exact buffer text as menu items, without
+ preserving regex artifacts like position
+ anchors (e.g., |/\<|). This provides more intuitive
+ menu items that match the actual buffer text.
+ However, searches may be less accurate since the
+ pattern is not preserved exactly.
+ By default, Vim preserves the typed pattern (with
+ anchors) and appends the matched word. This preserves
+ search correctness, especially when using regular
+ expressions or with 'smartcase' enabled. However, the
+ case of the appended matched word may not exactly
+ match the case of the word in the buffer.
fuzzy Use |fuzzy-matching| to find completion matches. When
this value is specified, wildcard expansion will not
be used for completion. The matches will be sorted by
-*version9.txt* For Vim version 9.1. Last change: 2025 Jul 05
+*version9.txt* For Vim version 9.1. Last change: 2025 Jul 08
VIM REFERENCE MANUAL by Bram Moolenaar
- improved commandline completion for the |:hi| command
- New option value for 'wildmode':
"noselect" - do not auto select an entry in the wildmenu
+ "exacttext" - show exact matches in wildmenu with search
+ completion
- New flags for 'complete':
"F{func}" - complete using given function
"F" - complete using 'completefunc'
int segment_len;
linenr_T lnum;
garray_T ga;
+ int exacttext = vim_strchr(p_wop, WOP_EXACTTEXT) != NULL;
if (start->lnum > end->lnum
|| (start->lnum == end->lnum && start->col >= end->col))
segment_len = is_single_line ? (end->col - start->col)
: (int)STRLEN(start_ptr);
- if (ga_grow(&ga, segment_len + 1) != OK)
+ if (ga_grow(&ga, segment_len + 2) != OK)
return FAIL;
ga_concat_len(&ga, start_ptr, segment_len);
if (!is_single_line)
- ga_append(&ga, '\n');
+ {
+ if (exacttext)
+ ga_concat_len(&ga, (char_u *)"\\n", 2);
+ else
+ ga_append(&ga, '\n');
+ }
// Append full lines between start and end
if (!is_single_line)
for (lnum = start->lnum + 1; lnum < end->lnum; lnum++)
{
line = ml_get(lnum);
- if (ga_grow(&ga, ml_get_len(lnum) + 1) != OK)
+ if (ga_grow(&ga, ml_get_len(lnum) + 2) != OK)
return FAIL;
ga_concat(&ga, line);
- ga_append(&ga, '\n');
+ if (exacttext)
+ ga_concat_len(&ga, (char_u *)"\\n", 2);
+ else
+ ga_append(&ga, '\n');
}
}
int compl_started = FALSE;
int search_flags;
char_u *match, *full_match;
+ int exacttext = vim_strchr(p_wop, WOP_EXACTTEXT) != NULL;
#ifdef FEAT_SEARCH_EXTRA
has_range = search_first_line != 0;
&word_end_pos))
break;
- // Construct a new match from completed word appended to pattern itself
- match = concat_pattern_with_buffer_match(pat, pat_len, &end_match_pos,
- FALSE);
-
- // The regex pattern may include '\C' or '\c'. First, try matching the
- // buffer word as-is. If it doesn't match, try again with the lowercase
- // version of the word to handle smartcase behavior.
- if (match == NULL || !is_regex_match(match, full_match))
+ if (exacttext)
+ match = full_match;
+ else
{
- vim_free(match);
- match = concat_pattern_with_buffer_match(pat, pat_len,
- &end_match_pos, TRUE);
+ // Construct a new match from completed word appended to pattern itself
+ match = concat_pattern_with_buffer_match(pat, pat_len, &end_match_pos,
+ FALSE);
+
+ // The regex pattern may include '\C' or '\c'. First, try matching the
+ // buffer word as-is. If it doesn't match, try again with the lowercase
+ // version of the word to handle smartcase behavior.
if (match == NULL || !is_regex_match(match, full_match))
{
vim_free(match);
- vim_free(full_match);
- continue;
+ match = concat_pattern_with_buffer_match(pat, pat_len,
+ &end_match_pos, TRUE);
+ if (match == NULL || !is_regex_match(match, full_match))
+ {
+ vim_free(match);
+ vim_free(full_match);
+ continue;
+ }
}
+ vim_free(full_match);
}
- vim_free(full_match);
// Include this match if it is not a duplicate
for (int i = 0; i < ga.ga_len; ++i)
// flags for the 'wildoptions' option
// each defined char should be unique over all values.
#define WOP_FUZZY 'z'
-#define WOP_TAGFILE 't'
+#define WOP_TAGFILE 'g'
#define WOP_PUM 'p'
+#define WOP_EXACTTEXT 'x'
// arguments for can_bs()
// each defined char should be unique over all values
static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", "NONE", NULL};
// Note: Keep this in sync with check_opt_wim()
static char *(p_wim_values[]) = {"full", "longest", "list", "lastused", "noselect", NULL};
-static char *(p_wop_values[]) = {"fuzzy", "tagfile", "pum", NULL};
+static char *(p_wop_values[]) = {"fuzzy", "tagfile", "pum", "exacttext", NULL};
#ifdef FEAT_WAK
static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
#endif
" Match case correctly
%d
call setline(1, ["foobar", "Foobar", "fooBAr", "FooBARR"])
+
call feedkeys("gg/f\<tab>\<f9>", 'tx')
call assert_equal(['fooBAr', 'foobar'], g:compl_info.matches)
call feedkeys("gg/Fo\<tab>\<f9>", 'tx')
call assert_equal({}, g:compl_info)
call feedkeys("gg/\\cFo\<tab>\<f9>", 'tx')
call assert_equal(['\cFoobar', '\cFooBAr', '\cFooBARR'], g:compl_info.matches)
+
set ignorecase
call feedkeys("gg/f\<tab>\<f9>", 'tx')
call assert_equal(['foobar', 'fooBAr', 'fooBARR'], g:compl_info.matches)
call assert_equal(['FOobar', 'FOoBAr', 'FOoBARR'], g:compl_info.matches)
call feedkeys("gg/\\Cfo\<tab>\<f9>", 'tx')
call assert_equal(['\CfooBAr', '\Cfoobar'], g:compl_info.matches)
+
set smartcase
call feedkeys("gg/f\<tab>\<f9>", 'tx')
call assert_equal(['foobar', 'fooBAr', 'foobarr'], g:compl_info.matches)
call assert_equal(['Foobar', 'FooBARR'], g:compl_info.matches)
call feedkeys("gg/FO\<tab>\<f9>", 'tx')
call assert_equal({}, g:compl_info)
+ call feedkeys("gg/foob\<tab>\<f9>", 'tx')
+ call assert_equal(['foobar', 'foobarr'], g:compl_info.matches)
call feedkeys("gg/\\Cfo\<tab>\<f9>", 'tx')
call assert_equal(['\CfooBAr', '\Cfoobar'], g:compl_info.matches)
call feedkeys("gg/\\cFo\<tab>\<f9>", 'tx')
call assert_equal(['\cFoobar', '\cFooBAr', '\cFooBARR'], g:compl_info.matches)
+ set wildoptions+=exacttext ignorecase& smartcase&
+ call feedkeys("gg/F\<tab>\<f9>", 'tx')
+ call assert_equal(['Foobar', 'FooBARR'], g:compl_info.matches)
+ call feedkeys("gg/foob\<tab>\<f9>", 'tx')
+ call assert_equal([], g:compl_info.matches)
+ call feedkeys("gg/r\\n.\<tab>\<f9>", 'tx')
+ call assert_equal(['r\nFoobar', 'r\nfooBAr', 'r\nFooBARR'], g:compl_info.matches)
+
+ set ignorecase
+ call feedkeys("gg/F\<tab>\<f9>", 'tx')
+ call assert_equal(['Foobar', 'fooBAr', 'FooBARR', 'foobar'], g:compl_info.matches)
+ call feedkeys("gg/R\\n.\<tab>\<f9>", 'tx')
+ call assert_equal(['r\nFoobar', 'r\nfooBAr', 'r\nFooBARR'], g:compl_info.matches)
+
+ set smartcase
+ call feedkeys("gg/f\<tab>\<f9>", 'tx')
+ call assert_equal(['Foobar', 'fooBAr', 'FooBARR', 'foobar'], g:compl_info.matches)
+ call feedkeys("gg/foob\<tab>\<f9>", 'tx')
+ call assert_equal(['Foobar', 'fooBAr', 'FooBARR', 'foobar'], g:compl_info.matches)
+ call feedkeys("gg/R\\n.\<tab>\<f9>", 'tx')
+ call assert_equal({}, g:compl_info)
+ call feedkeys("gg/r\\n.*\\n\<tab>\<f9>", 'tx')
+ call assert_equal(['r\nFoobar\nfooBAr', 'r\nfooBAr\nFooBARR'], g:compl_info.matches)
+
bw!
call test_override("char_avail", 0)
delfunc GetComplInfo
unlet! g:compl_info
- set wildcharm=0 incsearch& ignorecase& smartcase&
+ set wildcharm=0 incsearch& ignorecase& smartcase& wildoptions&
endfunc
func Test_search_wildmenu_screendump()
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1526,
/**/
1525,
/**/