aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2024-10-09 08:14:18 +0800
committerGitHub <noreply@github.com>2024-10-09 08:14:18 +0800
commitf449a38f6a47bee30f0d4e291d8234d1ac8288a7 (patch)
treee65db9b696571e64352b16f849b8cc7582183f8c
parente98b1b0235a5e817c00814549606631703ab2041 (diff)
downloadrneovim-f449a38f6a47bee30f0d4e291d8234d1ac8288a7.tar.gz
rneovim-f449a38f6a47bee30f0d4e291d8234d1ac8288a7.tar.bz2
rneovim-f449a38f6a47bee30f0d4e291d8234d1ac8288a7.zip
vim-patch:9.1.0770: current command line completion is a bit limited (#30728)
Problem: current command completion is a bit limited Solution: Add the shellcmdline completion type and getmdcomplpat() function (Ruslan Russkikh). closes: vim/vim#15823 https://github.com/vim/vim/commit/0407d621bbad020b840ffbbbd25ba023bbc05edd Co-authored-by: Ruslan Russkikh <dvrussk@yandex.ru>
-rw-r--r--runtime/doc/builtin.txt13
-rw-r--r--runtime/doc/map.txt2
-rw-r--r--runtime/doc/usr_41.txt2
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua12
-rw-r--r--runtime/syntax/vim.vim2
-rw-r--r--src/nvim/cmdexpand.c4
-rw-r--r--src/nvim/cmdexpand_defs.h1
-rw-r--r--src/nvim/eval.lua16
-rw-r--r--src/nvim/ex_getln.c39
-rw-r--r--src/nvim/usercmd.c7
-rw-r--r--test/old/testdir/test_cmdline.vim86
11 files changed, 161 insertions, 23 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 4c726f86d2..617fd87553 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -3224,13 +3224,24 @@ getcharstr([{expr}]) *getcharstr()*
Return: ~
(`string`)
+getcmdcomplpat() *getcmdcomplpat()*
+ Return completion pattern of the current command-line.
+ Only works when the command line is being edited, thus
+ requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
+ Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
+ |getcmdprompt()|, |getcmdcompltype()| and |setcmdline()|.
+ Returns an empty string when completion is not defined.
+
+ Return: ~
+ (`string`)
+
getcmdcompltype() *getcmdcompltype()*
Return the type of the current command-line completion.
Only works when the command line is being edited, thus
requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
See |:command-completion| for the return string.
Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
- |getcmdprompt()| and |setcmdline()|.
+ |getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
Returns an empty string when completion is not defined.
Return: ~
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index 4a4e34661f..11048aee30 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -1413,6 +1413,8 @@ completion can be enabled:
-complete=runtime file and directory names in |'runtimepath'|
-complete=scriptnames sourced script names
-complete=shellcmd Shell command
+ -complete=shellcmdline First is a shell command and subsequent ones
+ are filenames. The same behavior as |:!cmd|
-complete=sign |:sign| suboptions
-complete=syntax syntax file names |'syntax'|
-complete=syntime |:syntime| suboptions
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 8c7ed875cf..3202a70b76 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -906,6 +906,8 @@ Buffers, windows and the argument list:
swapname() get the swap file path of a buffer
Command line: *command-line-functions*
+ getcmdcomplpat() get completion pattern of the current command
+ line
getcmdcompltype() get the type of the current command line
completion
getcmdline() get the current command line input
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index 3f6deba092..1e1e87fca8 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -2879,12 +2879,22 @@ function vim.fn.getcharsearch() end
--- @return string
function vim.fn.getcharstr(expr) end
+--- Return completion pattern of the current command-line.
+--- Only works when the command line is being edited, thus
+--- requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
+--- Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
+--- |getcmdprompt()|, |getcmdcompltype()| and |setcmdline()|.
+--- Returns an empty string when completion is not defined.
+---
+--- @return string
+function vim.fn.getcmdcomplpat() end
+
--- Return the type of the current command-line completion.
--- Only works when the command line is being edited, thus
--- requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
--- See |:command-completion| for the return string.
--- Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
---- |getcmdprompt()| and |setcmdline()|.
+--- |getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
--- Returns an empty string when completion is not defined.
---
--- @return string
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 9073c6e7bf..6e38076d35 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -381,7 +381,7 @@ endif
syn case ignore
syn keyword vimUserCmdAttrKey contained a[ddr] ban[g] bar bu[ffer] com[plete] cou[nt] k[eepscript] n[args] ra[nge] re[gister]
" GEN_SYN_VIM: vimUserCmdAttrCmplt, START_STR='syn keyword vimUserCmdAttrCmplt contained', END_STR=''
-syn keyword vimUserCmdAttrCmplt contained arglist augroup behave breakpoint buffer color command compiler cscope diff_buffer dir dir_in_path environment event expression file file_in_path filetype function help highlight history keymap locale mapclear mapping menu messages option packadd runtime scriptnames shellcmd sign syntax syntime tag tag_listfiles user var
+syn keyword vimUserCmdAttrCmplt contained arglist augroup behave breakpoint buffer color command compiler cscope diff_buffer dir dir_in_path environment event expression file file_in_path filetype function help highlight history keymap locale mapclear mapping menu messages option packadd runtime scriptnames shellcmd shellcmdline sign syntax syntime tag tag_listfiles user var
syn keyword vimUserCmdAttrCmplt contained custom customlist nextgroup=vimUserCmdAttrCmpltFunc,vimUserCmdError
syn match vimUserCmdAttrCmpltFunc contained ",\%([sS]:\|<[sS][iI][dD]>\)\=\%(\h\w*\%([.#]\h\w*\)\+\|\h\w*\)"hs=s+1 nextgroup=vimUserCmdError
" GEN_SYN_VIM: vimUserCmdAttrAddr, START_STR='syn keyword vimUserCmdAttrAddr contained', END_STR=''
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 402a891099..b37a1d690f 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -119,6 +119,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
&& xp->xp_context != EXPAND_PACKADD
&& xp->xp_context != EXPAND_RUNTIME
&& xp->xp_context != EXPAND_SHELLCMD
+ && xp->xp_context != EXPAND_SHELLCMDLINE
&& xp->xp_context != EXPAND_TAGS
&& xp->xp_context != EXPAND_TAGS_LISTFILES
&& xp->xp_context != EXPAND_USER_LIST
@@ -1527,7 +1528,8 @@ static void set_context_for_wildcard_arg(exarg_T *eap, const char *arg, bool use
xp->xp_context = EXPAND_FILES;
// For a shell command more chars need to be escaped.
- if (usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal) {
+ if (usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal
+ || *complp == EXPAND_SHELLCMDLINE) {
#ifndef BACKSLASH_IN_FILENAME
xp->xp_shell = true;
#endif
diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h
index 3369790151..86725eafd6 100644
--- a/src/nvim/cmdexpand_defs.h
+++ b/src/nvim/cmdexpand_defs.h
@@ -106,6 +106,7 @@ enum {
EXPAND_ARGOPT,
EXPAND_KEYMAP,
EXPAND_DIRS_IN_CDPATH,
+ EXPAND_SHELLCMDLINE,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 50aaf9e03b..24f986ef4e 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -3611,6 +3611,20 @@ M.funcs = {
returns = 'string',
signature = 'getcharstr([{expr}])',
},
+ getcmdcomplpat = {
+ desc = [=[
+ Return completion pattern of the current command-line.
+ Only works when the command line is being edited, thus
+ requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
+ Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
+ |getcmdprompt()|, |getcmdcompltype()| and |setcmdline()|.
+ Returns an empty string when completion is not defined.
+ ]=],
+ name = 'getcmdcomplpat',
+ params = {},
+ returns = 'string',
+ signature = 'getcmdcomplpat()',
+ },
getcmdcompltype = {
desc = [=[
Return the type of the current command-line completion.
@@ -3618,7 +3632,7 @@ M.funcs = {
requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
See |:command-completion| for the return string.
Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
- |getcmdprompt()| and |setcmdline()|.
+ |getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
Returns an empty string when completion is not defined.
]=],
name = 'getcmdcompltype',
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index ca01a158a3..b58a6b16f1 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -4086,14 +4086,44 @@ static char *get_cmdline_str(void)
return xstrnsave(p->cmdbuff, (size_t)p->cmdlen);
}
+/// Get the current command-line completion pattern.
+static char *get_cmdline_completion_pattern(void)
+{
+ if (cmdline_star > 0) {
+ return NULL;
+ }
+
+ CmdlineInfo *p = get_ccline_ptr();
+ if (p == NULL || p->xpc == NULL) {
+ return NULL;
+ }
+
+ int xp_context = p->xpc->xp_context;
+ if (xp_context == EXPAND_NOTHING) {
+ set_expand_context(p->xpc);
+ xp_context = p->xpc->xp_context;
+ p->xpc->xp_context = EXPAND_NOTHING;
+ }
+ if (xp_context == EXPAND_UNSUCCESSFUL) {
+ return NULL;
+ }
+
+ char *compl_pat = p->xpc->xp_pattern;
+ if (compl_pat == NULL) {
+ return NULL;
+ }
+
+ return xstrdup(compl_pat);
+}
+
/// Get the current command-line completion type.
static char *get_cmdline_completion(void)
{
if (cmdline_star > 0) {
return NULL;
}
- CmdlineInfo *p = get_ccline_ptr();
+ CmdlineInfo *p = get_ccline_ptr();
if (p == NULL || p->xpc == NULL) {
return NULL;
}
@@ -4123,6 +4153,13 @@ static char *get_cmdline_completion(void)
return xstrdup(cmd_compl);
}
+/// "getcmdcomplpat()" function
+void f_getcmdcomplpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = get_cmdline_completion_pattern();
+}
+
/// "getcmdcompltype()" function
void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index d32e0ee319..8404b2bc14 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -91,6 +91,7 @@ static const char *command_complete[] = {
[EXPAND_PACKADD] = "packadd",
[EXPAND_RUNTIME] = "runtime",
[EXPAND_SHELLCMD] = "shellcmd",
+ [EXPAND_SHELLCMDLINE] = "shellcmdline",
[EXPAND_SIGN] = "sign",
[EXPAND_TAGS] = "tag",
[EXPAND_TAGS_LISTFILES] = "tag_listfiles",
@@ -285,8 +286,7 @@ const char *set_context_in_user_cmdarg(const char *cmd FUNC_ATTR_UNUSED, const c
}
if (argt & EX_XFILE) {
- // EX_XFILE: file names are handled above.
- xp->xp_context = context;
+ // EX_XFILE: file names are handled before this call.
return NULL;
}
@@ -675,7 +675,8 @@ int parse_compl_arg(const char *value, int vallen, int *complp, uint32_t *argt,
*complp = i;
if (i == EXPAND_BUFFERS) {
*argt |= EX_BUFNAME;
- } else if (i == EXPAND_DIRECTORIES || i == EXPAND_FILES) {
+ } else if (i == EXPAND_DIRECTORIES || i == EXPAND_FILES
+ || i == EXPAND_SHELLCMDLINE) {
*argt |= EX_XFILE;
}
break;
diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim
index b3179686b1..a5df637cc2 100644
--- a/test/old/testdir/test_cmdline.vim
+++ b/test/old/testdir/test_cmdline.vim
@@ -1012,8 +1012,7 @@ func Test_cmdline_complete_user_names()
call feedkeys(':e ~' . first_letter . "\<c-a>\<c-B>\"\<cr>", 'tx')
call assert_match('^"e \~.*\<' . whoami . '\>', @:)
endif
- endif
- if has('win32')
+ elseif has('win32')
" Just in case: check that the system has an Administrator account.
let names = system('net user')
if names =~ 'Administrator'
@@ -1022,14 +1021,25 @@ func Test_cmdline_complete_user_names()
call feedkeys(':e ~A' . "\<c-a>\<c-B>\"\<cr>", 'tx')
call assert_match('^"e \~.*Administrator', @:)
endif
+ else
+ throw 'Skipped: does not work on this platform'
endif
endfunc
+func Test_cmdline_complete_shellcmdline()
+ CheckExecutable whoami
+ command -nargs=1 -complete=shellcmdline MyCmd
+
+ call feedkeys(":MyCmd whoam\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^".*\<whoami\>', @:)
+
+ delcommand MyCmd
+endfunc
+
func Test_cmdline_complete_bang()
- if executable('whoami')
- call feedkeys(":!whoam\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('^".*\<whoami\>', @:)
- endif
+ CheckExecutable whoami
+ call feedkeys(":!whoam\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^".*\<whoami\>', @:)
endfunc
func Test_cmdline_complete_languages()
@@ -3800,6 +3810,52 @@ func Test_cmdline_complete_substitute_short()
endfor
endfunc
+" Test for shellcmdline command argument completion
+func Test_cmdline_complete_shellcmdline_argument()
+ command -nargs=+ -complete=shellcmdline MyCmd
+
+ set wildoptions=fuzzy
+
+ call feedkeys(":MyCmd vim test_cmdline.\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim test_cmdline.vim', @:)
+
+ call feedkeys(":MyCmd vim nonexistentfile\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim nonexistentfile', @:)
+
+ let compl1 = getcompletion('', 'file')[0]
+ let compl2 = getcompletion('', 'file')[1]
+ call feedkeys(":MyCmd vim \<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim ' .. compl1, @:)
+
+ call feedkeys(":MyCmd vim \<Tab> \<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim ' .. compl1 .. ' ' .. compl1, @:)
+
+ let compl = getcompletion('', 'file')[1]
+ call feedkeys(":MyCmd vim \<Tab> \<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim ' .. compl1 .. ' ' .. compl2, @:)
+
+ set wildoptions&
+ call feedkeys(":MyCmd vim test_cmdline.\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim test_cmdline.vim', @:)
+
+ call feedkeys(":MyCmd vim nonexistentfile\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim nonexistentfile', @:)
+
+ let compl1 = getcompletion('', 'file')[0]
+ let compl2 = getcompletion('', 'file')[1]
+ call feedkeys(":MyCmd vim \<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim ' .. compl1, @:)
+
+ call feedkeys(":MyCmd vim \<Tab> \<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim ' .. compl1 .. ' ' .. compl1, @:)
+
+ let compl = getcompletion('', 'file')[1]
+ call feedkeys(":MyCmd vim \<Tab> \<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd vim ' .. compl1 .. ' ' .. compl2, @:)
+
+ delcommand MyCmd
+endfunc
+
" Test for :! shell command argument completion
func Test_cmdline_complete_bang_cmd_argument()
set wildoptions=fuzzy
@@ -3811,30 +3867,32 @@ func Test_cmdline_complete_bang_cmd_argument()
endfunc
func Call_cmd_funcs()
- return [getcmdpos(), getcmdscreenpos(), getcmdcompltype()]
+ return [getcmdpos(), getcmdscreenpos(), getcmdcompltype(), getcmdcomplpat()]
endfunc
func Test_screenpos_and_completion()
call assert_equal(0, getcmdpos())
call assert_equal(0, getcmdscreenpos())
call assert_equal('', getcmdcompltype())
+ call assert_equal('', getcmdcomplpat())
cnoremap <expr> <F2> string(Call_cmd_funcs())
call feedkeys(":let a\<F2>\<C-B>\"\<CR>", "xt")
- call assert_equal("\"let a[6, 7, 'var']", @:)
+ call assert_equal("\"let a[6, 7, 'var', 'a']", @:)
call feedkeys(":quit \<F2>\<C-B>\"\<CR>", "xt")
- call assert_equal("\"quit [6, 7, '']", @:)
+ call assert_equal("\"quit [6, 7, '', '']", @:)
call feedkeys(":nosuchcommand \<F2>\<C-B>\"\<CR>", "xt")
- call assert_equal("\"nosuchcommand [15, 16, '']", @:)
+ call assert_equal("\"nosuchcommand [15, 16, '', '']", @:)
- " Check that getcmdcompltype() doesn't interfere with cmdline completion.
+ " Check that getcmdcompltype() and getcmdcomplpat() don't interfere with
+ " cmdline completion.
let g:results = []
cnoremap <F2> <Cmd>let g:results += [[getcmdline()] + Call_cmd_funcs()]<CR>
call feedkeys(":sign un\<Tab>\<F2>\<Tab>\<F2>\<Tab>\<F2>\<C-C>", "xt")
call assert_equal([
- \ ['sign undefine', 14, 15, 'sign'],
- \ ['sign unplace', 13, 14, 'sign'],
- \ ['sign un', 8, 9, 'sign']], g:results)
+ \ ['sign undefine', 14, 15, 'sign', 'undefine'],
+ \ ['sign unplace', 13, 14, 'sign', 'unplace'],
+ \ ['sign un', 8, 9, 'sign', 'un']], g:results)
unlet g:results
cunmap <F2>