patch 9.1.1576: cannot easily trigger wildcard expansion
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>
This commit is contained in:
committed by
Christian Brabandt
parent
689f3bf313
commit
b486ed8266
@ -238,6 +238,7 @@ nextwild(
|
||||
cmdline_info_T *ccline = get_cmdline_info();
|
||||
int i;
|
||||
char_u *p;
|
||||
int from_wildtrigger_func = options & WILD_FUNC_TRIGGER;
|
||||
|
||||
if (xp->xp_numfiles == -1)
|
||||
{
|
||||
@ -269,17 +270,22 @@ nextwild(
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
i = (int)(xp->xp_pattern - ccline->cmdbuff);
|
||||
xp->xp_pattern_len = ccline->cmdpos - i;
|
||||
|
||||
// Skip showing matches if prefix is invalid during wildtrigger()
|
||||
if (from_wildtrigger_func && xp->xp_context == EXPAND_COMMANDS
|
||||
&& xp->xp_pattern_len == 0)
|
||||
return FAIL;
|
||||
|
||||
// If cmd_silent is set then don't show the dots, because redrawcmd() below
|
||||
// won't remove them.
|
||||
if (!cmd_silent)
|
||||
if (!cmd_silent && !from_wildtrigger_func)
|
||||
{
|
||||
msg_puts("..."); // show that we are busy
|
||||
out_flush();
|
||||
}
|
||||
|
||||
i = (int)(xp->xp_pattern - ccline->cmdbuff);
|
||||
xp->xp_pattern_len = ccline->cmdpos - i;
|
||||
|
||||
if (type == WILD_NEXT || type == WILD_PREV
|
||||
|| type == WILD_PAGEUP || type == WILD_PAGEDOWN)
|
||||
{
|
||||
|
||||
@ -3126,6 +3126,8 @@ static funcentry_T global_functions[] =
|
||||
ret_string, f_visualmode},
|
||||
{"wildmenumode", 0, 0, 0, NULL,
|
||||
ret_number, f_wildmenumode},
|
||||
{"wildtrigger", 0, 0, 0, NULL,
|
||||
ret_void, f_wildtrigger},
|
||||
{"win_execute", 2, 3, FEARG_2, arg23_win_execute,
|
||||
ret_string, f_win_execute},
|
||||
{"win_findbuf", 1, 1, FEARG_1, arg1_number,
|
||||
|
||||
@ -957,9 +957,11 @@ cmdline_wildchar_complete(
|
||||
}
|
||||
else // typed p_wc first time
|
||||
{
|
||||
if (c == p_wc || c == p_wcm)
|
||||
if (c == p_wc || c == p_wcm || c == K_WILD)
|
||||
{
|
||||
options |= WILD_MAY_EXPAND_PATTERN;
|
||||
if (c == K_WILD)
|
||||
options |= WILD_FUNC_TRIGGER;
|
||||
if (pre_incsearch_pos)
|
||||
xp->xp_pre_incsearch_pos = *pre_incsearch_pos;
|
||||
else
|
||||
@ -1395,7 +1397,7 @@ cmdline_browse_history(
|
||||
for (;;)
|
||||
{
|
||||
// one step backwards
|
||||
if (c == K_UP|| c == K_S_UP || c == Ctrl_P
|
||||
if (c == K_UP || c == K_S_UP || c == Ctrl_P
|
||||
|| c == K_PAGEUP || c == K_KPAGEUP)
|
||||
{
|
||||
if (hiscnt == get_hislen()) // first time
|
||||
@ -1818,9 +1820,9 @@ getcmdline_int(
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
int trigger_cmdlinechanged = TRUE;
|
||||
int end_wildmenu;
|
||||
int prev_cmdpos = ccline.cmdpos;
|
||||
int trigger_cmdlinechanged = TRUE;
|
||||
int end_wildmenu;
|
||||
int prev_cmdpos = ccline.cmdpos;
|
||||
|
||||
VIM_CLEAR(prev_cmdbuff);
|
||||
|
||||
@ -2058,9 +2060,11 @@ getcmdline_int(
|
||||
}
|
||||
}
|
||||
|
||||
// Completion for 'wildchar' or 'wildcharm' key.
|
||||
if ((c == p_wc && !gotesc && KeyTyped) || c == p_wcm)
|
||||
// Completion for 'wildchar', 'wildcharm', and wildtrigger()
|
||||
if ((c == p_wc && !gotesc && KeyTyped) || c == p_wcm || c == K_WILD)
|
||||
{
|
||||
if (c == K_WILD)
|
||||
++emsg_silent; // Silence the bell
|
||||
res = cmdline_wildchar_complete(c, firstc != '@', &did_wild_list,
|
||||
&wim_index, &xpc, &gotesc,
|
||||
#ifdef FEAT_SEARCH_EXTRA
|
||||
@ -2069,8 +2073,12 @@ getcmdline_int(
|
||||
NULL
|
||||
#endif
|
||||
);
|
||||
if (c == K_WILD)
|
||||
--emsg_silent;
|
||||
if (res == CMDLINE_CHANGED)
|
||||
goto cmdline_changed;
|
||||
if (c == K_WILD)
|
||||
goto cmdline_not_changed;
|
||||
}
|
||||
|
||||
gotesc = FALSE;
|
||||
@ -5109,3 +5117,30 @@ get_user_input(
|
||||
cmd_silent = cmd_silent_save;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "wildtrigger()" function
|
||||
*/
|
||||
void
|
||||
f_wildtrigger(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
{
|
||||
if (!(State & MODE_CMDLINE) || char_avail() || wild_menu_showing
|
||||
|| cmdline_pum_active())
|
||||
return;
|
||||
|
||||
int cmd_type = get_cmdline_type();
|
||||
|
||||
if (cmd_type == ':' || cmd_type == '/' || cmd_type == '?')
|
||||
{
|
||||
// Add K_WILD as a single special key
|
||||
char_u key_string[4];
|
||||
|
||||
key_string[0] = K_SPECIAL;
|
||||
key_string[1] = KS_EXTRA;
|
||||
key_string[2] = KE_WILD;
|
||||
key_string[3] = NUL;
|
||||
|
||||
// Insert it into the typeahead buffer
|
||||
ins_typebuf(key_string, REMAP_NONE, 0, TRUE, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,6 +279,7 @@ enum key_extra
|
||||
, KE_S_BS = 105 // shift + <BS>
|
||||
, KE_SID = 106 // <SID> special key, followed by {nr};
|
||||
, KE_ESC = 107 // used for K_ESC
|
||||
, KE_WILD = 108 // triggers wildmode completion
|
||||
};
|
||||
|
||||
/*
|
||||
@ -491,6 +492,8 @@ enum key_extra
|
||||
#define K_SCRIPT_COMMAND TERMCAP2KEY(KS_EXTRA, KE_SCRIPT_COMMAND)
|
||||
#define K_SID TERMCAP2KEY(KS_EXTRA, KE_SID)
|
||||
|
||||
#define K_WILD TERMCAP2KEY(KS_EXTRA, KE_WILD)
|
||||
|
||||
// Bits for modifier mask
|
||||
// 0x01 cannot be used, because the modifier must be 0x02 or higher
|
||||
#define MOD_MASK_SHIFT 0x02
|
||||
|
||||
@ -39,6 +39,7 @@ void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv);
|
||||
void f_getcmdtype(typval_T *argvars, typval_T *rettv);
|
||||
void f_setcmdline(typval_T *argvars, typval_T *rettv);
|
||||
void f_setcmdpos(typval_T *argvars, typval_T *rettv);
|
||||
void f_wildtrigger(typval_T *argvars, typval_T *rettv);
|
||||
int get_cmdline_firstc(void);
|
||||
int get_list_range(char_u **str, int *num1, int *num2);
|
||||
char *did_set_cedit(optset_T *args);
|
||||
|
||||
@ -4329,42 +4329,63 @@ func Test_cmdcomplete_info()
|
||||
autocmd CmdlineLeavePre * call expand('test_cmdline.*')
|
||||
autocmd CmdlineLeavePre * let g:cmdcomplete_info = string(cmdcomplete_info())
|
||||
augroup END
|
||||
new
|
||||
call assert_equal({}, cmdcomplete_info())
|
||||
call feedkeys(":h echom\<cr>", "tx") " No expansion
|
||||
call assert_equal('{}', g:cmdcomplete_info)
|
||||
call feedkeys(":h echoms\<tab>\<cr>", "tx")
|
||||
call assert_equal('{''cmdline_orig'': '''', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
|
||||
call feedkeys(":h echom\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': 0}',
|
||||
\ g:cmdcomplete_info)
|
||||
call feedkeys(":h echom\<tab>\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': 1}',
|
||||
\ g:cmdcomplete_info)
|
||||
call feedkeys(":h echom\<tab>\<tab>\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': -1}',
|
||||
\ g:cmdcomplete_info)
|
||||
|
||||
set wildoptions=pum
|
||||
call feedkeys(":h echoms\<tab>\<cr>", "tx")
|
||||
call assert_equal('{''cmdline_orig'': '''', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
|
||||
call feedkeys(":h echom\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': 0}',
|
||||
\ g:cmdcomplete_info)
|
||||
call feedkeys(":h echom\<tab>\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': 1}',
|
||||
\ g:cmdcomplete_info)
|
||||
call feedkeys(":h echom\<tab>\<tab>\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': -1}',
|
||||
\ g:cmdcomplete_info)
|
||||
bw!
|
||||
set wildoptions&
|
||||
" Disable char_avail so that wildtrigger() does not bail out
|
||||
call test_override("char_avail", 1)
|
||||
|
||||
cnoremap <F8> <C-R>=wildtrigger()[-1]<CR>
|
||||
|
||||
call assert_equal({}, cmdcomplete_info())
|
||||
|
||||
for trig in ["\<Tab>", "\<F8>"]
|
||||
new
|
||||
call assert_equal({}, cmdcomplete_info())
|
||||
call feedkeys(":h echom\<cr>", "tx") " No expansion
|
||||
call assert_equal('{}', g:cmdcomplete_info)
|
||||
call feedkeys($":h echoms{trig}\<cr>", "tx")
|
||||
call assert_equal('{''cmdline_orig'': '''', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
|
||||
call feedkeys($":h echom{trig}\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': 0}',
|
||||
\ g:cmdcomplete_info)
|
||||
call feedkeys($":h echom{trig}\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': 1}',
|
||||
\ g:cmdcomplete_info)
|
||||
call feedkeys($":h echom{trig}\<tab>\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': -1}',
|
||||
\ g:cmdcomplete_info)
|
||||
|
||||
set wildoptions=pum
|
||||
call feedkeys($":h echoms{trig}\<cr>", "tx")
|
||||
call assert_equal('{''cmdline_orig'': '''', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
|
||||
call feedkeys($":h echom{trig}\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': 0}',
|
||||
\ g:cmdcomplete_info)
|
||||
call feedkeys($":h echom{trig}\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': 1}',
|
||||
\ g:cmdcomplete_info)
|
||||
call feedkeys($":h echom{trig}\<tab>\<tab>\<cr>", "tx")
|
||||
call assert_equal(
|
||||
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': -1}',
|
||||
\ g:cmdcomplete_info)
|
||||
bw!
|
||||
set wildoptions&
|
||||
endfor
|
||||
|
||||
" wildtrigger() should not show matches when prefix is invalid
|
||||
for pat in ["", " ", "22"]
|
||||
call feedkeys($":{pat}\<F8>\<cr>", "tx") " No expansion
|
||||
call assert_equal('{}', g:cmdcomplete_info)
|
||||
endfor
|
||||
|
||||
augroup test_CmdlineLeavePre | autocmd! | augroup END
|
||||
call test_override("char_avail", 0)
|
||||
unlet g:cmdcomplete_info
|
||||
cunmap <F8>
|
||||
endfunc
|
||||
|
||||
func Test_redrawtabpanel_error()
|
||||
@ -4387,6 +4408,7 @@ func Test_search_complete()
|
||||
|
||||
new
|
||||
cnoremap <buffer><expr> <F9> GetComplInfo()
|
||||
cnoremap <buffer> <F8> <C-R>=wildtrigger()[-1]<CR>
|
||||
|
||||
" Pressing <Tab> inserts tab character
|
||||
set wildchar=0
|
||||
@ -4397,7 +4419,7 @@ func Test_search_complete()
|
||||
|
||||
call setline(1, ['the', 'these', 'thethe', 'thethere', 'foobar'])
|
||||
|
||||
for trig in ["\<tab>", "\<c-z>"]
|
||||
for trig in ["\<tab>", "\<c-z>", "\<F8>"]
|
||||
" Test menu first item and order
|
||||
call feedkeys($"gg2j/t{trig}\<f9>", 'tx')
|
||||
call assert_equal(['the', 'thethere', 'there', 'these', 'thethe'], g:compl_info.matches)
|
||||
@ -4610,10 +4632,11 @@ func Test_range_complete()
|
||||
endfunc
|
||||
new
|
||||
cnoremap <buffer><expr> <F9> GetComplInfo()
|
||||
cnoremap <buffer> <F8> <C-R>=wildtrigger()[-1]<CR>
|
||||
|
||||
call setline(1, ['ab', 'ba', 'ca', 'af'])
|
||||
|
||||
for trig in ["\<tab>", "\<c-z>"]
|
||||
for trig in ["\<tab>", "\<c-z>", "\<F8>"]
|
||||
call feedkeys($":%s/a{trig}\<f9>", 'xt')
|
||||
call assert_equal(['ab', 'a', 'af'], g:compl_info.matches)
|
||||
call feedkeys($":vim9cmd :%s/a{trig}\<f9>", 'xt')
|
||||
@ -4699,25 +4722,35 @@ func Test_cmdline_changed()
|
||||
autocmd CmdlineChanged * if getcmdline() =~ g:cmdprefix | let g:cmdchg_count += 1 | endif
|
||||
augroup END
|
||||
|
||||
" Disable char_avail so that wildtrigger() does not bail out
|
||||
call test_override("char_avail", 1)
|
||||
|
||||
new
|
||||
cnoremap <buffer> <F8> <C-R>=wildtrigger()[-1]<CR>
|
||||
set wildmenu
|
||||
set wildmode=full
|
||||
|
||||
let g:cmdprefix = 'echomsg'
|
||||
let g:cmdchg_count = 0
|
||||
call feedkeys(":echomsg\<Tab>", "tx")
|
||||
call assert_equal(1, g:cmdchg_count) " once only for 'g', not again for <Tab>
|
||||
for trig in ["\<Tab>", "\<F8>"]
|
||||
let g:cmdchg_count = 0
|
||||
call feedkeys($":echomsg{trig}", "tx")
|
||||
call assert_equal(1, g:cmdchg_count) " once only for 'g', not again for <Tab>
|
||||
endfor
|
||||
|
||||
let g:cmdchg_count = 0
|
||||
let g:cmdprefix = 'echo'
|
||||
call feedkeys(":ech\<Tab>", "tx")
|
||||
call assert_equal(1, g:cmdchg_count) " (once for 'h' and) once for 'o'
|
||||
for trig in ["\<Tab>", "\<F8>"]
|
||||
let g:cmdchg_count = 0
|
||||
call feedkeys($":ech{trig}", "tx")
|
||||
call assert_equal(1, g:cmdchg_count) " (once for 'h' and) once for 'o'
|
||||
endfor
|
||||
|
||||
set wildmode=noselect,full
|
||||
let g:cmdchg_count = 0
|
||||
let g:cmdprefix = 'ech'
|
||||
call feedkeys(":ech\<Tab>", "tx")
|
||||
call assert_equal(1, g:cmdchg_count) " once for 'h', not again for <tab>
|
||||
for trig in ["\<Tab>", "\<F8>"]
|
||||
let g:cmdchg_count = 0
|
||||
call feedkeys($":ech{trig}", "tx")
|
||||
call assert_equal(1, g:cmdchg_count) " once for 'h', not again for <tab>
|
||||
endfor
|
||||
|
||||
command! -nargs=+ -complete=custom,TestComplete Test echo
|
||||
|
||||
@ -4726,10 +4759,12 @@ func Test_cmdline_changed()
|
||||
endfunc
|
||||
|
||||
set wildoptions=fuzzy wildmode=full
|
||||
let g:cmdchg_count = 0
|
||||
let g:cmdprefix = 'Test \(AbC\|abc\)'
|
||||
call feedkeys(":Test abc\<Tab>", "tx")
|
||||
call assert_equal(2, g:cmdchg_count) " once for 'c', again for 'AbC'
|
||||
for trig in ["\<Tab>", "\<F8>"]
|
||||
let g:cmdchg_count = 0
|
||||
call feedkeys($":Test abc{trig}", "tx")
|
||||
call assert_equal(2, g:cmdchg_count) " once for 'c', again for 'AbC'
|
||||
endfor
|
||||
|
||||
bw!
|
||||
set wildmode& wildmenu& wildoptions&
|
||||
@ -4738,6 +4773,7 @@ func Test_cmdline_changed()
|
||||
unlet g:cmdprefix
|
||||
delfunc TestComplete
|
||||
delcommand Test
|
||||
call test_override("char_avail", 0)
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@ -719,6 +719,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1576,
|
||||
/**/
|
||||
1575,
|
||||
/**/
|
||||
|
||||
@ -897,6 +897,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
|
||||
#define BUF_DIFF_FILTER 0x2000
|
||||
#define WILD_KEEP_SOLE_ITEM 0x4000
|
||||
#define WILD_MAY_EXPAND_PATTERN 0x8000
|
||||
#define WILD_FUNC_TRIGGER 0x10000 // called from wildtrigger()
|
||||
|
||||
// Flags for expand_wildcards()
|
||||
#define EW_DIR 0x01 // include directory names
|
||||
|
||||
Reference in New Issue
Block a user