mirror of
https://github.com/neovim/neovim
synced 2025-07-16 01:01:49 +00:00
vim-patch:9.1.1518: getcompletiontype() may crash (#34819)
Problem: getcompletiontype() crashes when no completion is available
(after v9.1.1509).
Solution: Don't call set_expand_context() (zeertzjq)
fixes: vim/vim#17681
closes: vim/vim#17684
e2c0f81dd0
This commit is contained in:
@ -3351,8 +3351,8 @@ getcmdcompltype() *getcmdcompltype()*
|
|||||||
|getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
|
|getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
|
||||||
Returns an empty string when completion is not defined.
|
Returns an empty string when completion is not defined.
|
||||||
|
|
||||||
To get the type of the command-line completion for the
|
To get the type of the command-line completion for a specified
|
||||||
specified string, use |getcompletiontype()|.
|
string, use |getcompletiontype()|.
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
(`string`)
|
(`string`)
|
||||||
|
4
runtime/lua/vim/_meta/vimfn.lua
generated
4
runtime/lua/vim/_meta/vimfn.lua
generated
@ -3002,8 +3002,8 @@ function vim.fn.getcmdcomplpat() end
|
|||||||
--- |getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
|
--- |getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
|
||||||
--- Returns an empty string when completion is not defined.
|
--- Returns an empty string when completion is not defined.
|
||||||
---
|
---
|
||||||
--- To get the type of the command-line completion for the
|
--- To get the type of the command-line completion for a specified
|
||||||
--- specified string, use |getcompletiontype()|.
|
--- string, use |getcompletiontype()|.
|
||||||
---
|
---
|
||||||
--- @return string
|
--- @return string
|
||||||
function vim.fn.getcmdcompltype() end
|
function vim.fn.getcmdcompltype() end
|
||||||
|
@ -3854,10 +3854,7 @@ void f_getcompletiontype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
|
|
||||||
int cmdline_len = (int)strlen(pat);
|
int cmdline_len = (int)strlen(pat);
|
||||||
set_cmd_context(&xpc, (char *)pat, cmdline_len, cmdline_len, false);
|
set_cmd_context(&xpc, (char *)pat, cmdline_len, cmdline_len, false);
|
||||||
xpc.xp_pattern_len = strlen(xpc.xp_pattern);
|
rettv->vval.v_string = cmdcomplete_type_to_str(xpc.xp_context, xpc.xp_arg);
|
||||||
xpc.xp_col = cmdline_len;
|
|
||||||
|
|
||||||
rettv->vval.v_string = get_cmdline_completion(&xpc);
|
|
||||||
|
|
||||||
ExpandCleanup(&xpc);
|
ExpandCleanup(&xpc);
|
||||||
}
|
}
|
||||||
|
@ -3760,8 +3760,8 @@ M.funcs = {
|
|||||||
|getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
|
|getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
|
||||||
Returns an empty string when completion is not defined.
|
Returns an empty string when completion is not defined.
|
||||||
|
|
||||||
To get the type of the command-line completion for the
|
To get the type of the command-line completion for a specified
|
||||||
specified string, use |getcompletiontype()|.
|
string, use |getcompletiontype()|.
|
||||||
]=],
|
]=],
|
||||||
name = 'getcmdcompltype',
|
name = 'getcmdcompltype',
|
||||||
params = {},
|
params = {},
|
||||||
|
@ -4198,31 +4198,28 @@ static char *get_cmdline_completion_pattern(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the command-line completion type.
|
/// Get the command-line completion type.
|
||||||
char *get_cmdline_completion(expand_T *xpc)
|
static char *get_cmdline_completion(void)
|
||||||
{
|
{
|
||||||
int xp_context = xpc->xp_context;
|
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) {
|
if (xp_context == EXPAND_NOTHING) {
|
||||||
set_expand_context(xpc);
|
set_expand_context(p->xpc);
|
||||||
xp_context = xpc->xp_context;
|
xp_context = p->xpc->xp_context;
|
||||||
xpc->xp_context = EXPAND_NOTHING;
|
p->xpc->xp_context = EXPAND_NOTHING;
|
||||||
}
|
}
|
||||||
if (xp_context == EXPAND_UNSUCCESSFUL) {
|
if (xp_context == EXPAND_UNSUCCESSFUL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *cmd_compl = get_user_cmd_complete(NULL, xp_context);
|
return cmdcomplete_type_to_str(xp_context, p->xpc->xp_arg);
|
||||||
if (cmd_compl == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xp_context == EXPAND_USER_LIST || xp_context == EXPAND_USER_DEFINED) {
|
|
||||||
size_t buflen = strlen(cmd_compl) + strlen(xpc->xp_arg) + 2;
|
|
||||||
char *buffer = xmalloc(buflen);
|
|
||||||
snprintf(buffer, buflen, "%s,%s", cmd_compl, xpc->xp_arg);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return xstrdup(cmd_compl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "getcmdcomplpat()" function
|
/// "getcmdcomplpat()" function
|
||||||
@ -4236,14 +4233,7 @@ void f_getcmdcomplpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||||
{
|
{
|
||||||
rettv->v_type = VAR_STRING;
|
rettv->v_type = VAR_STRING;
|
||||||
rettv->vval.v_string = NULL;
|
rettv->vval.v_string = get_cmdline_completion();
|
||||||
|
|
||||||
CmdlineInfo *p = get_ccline_ptr();
|
|
||||||
if (cmdline_star > 0 || p == NULL || p->xpc == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rettv->vval.v_string = get_cmdline_completion(p->xpc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "getcmdline()" function
|
/// "getcmdline()" function
|
||||||
|
@ -403,7 +403,7 @@ char *get_user_cmd_nargs(expand_T *xp, int idx)
|
|||||||
|
|
||||||
static char *get_command_complete(int arg)
|
static char *get_command_complete(int arg)
|
||||||
{
|
{
|
||||||
if (arg >= (int)(ARRAY_SIZE(command_complete))) {
|
if (arg < 0 || arg >= (int)(ARRAY_SIZE(command_complete))) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return (char *)command_complete[arg];
|
return (char *)command_complete[arg];
|
||||||
@ -422,6 +422,26 @@ char *get_user_cmd_complete(expand_T *xp, int idx)
|
|||||||
return cmd_compl;
|
return cmd_compl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the name of completion type "expand" as an allocated string.
|
||||||
|
/// "compl_arg" is the function name for "custom" and "customlist" types.
|
||||||
|
/// Returns NULL if no completion is available.
|
||||||
|
char *cmdcomplete_type_to_str(int expand, const char *compl_arg)
|
||||||
|
{
|
||||||
|
char *cmd_compl = get_command_complete(expand);
|
||||||
|
if (cmd_compl == NULL || expand == EXPAND_USER_LUA) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expand == EXPAND_USER_LIST || expand == EXPAND_USER_DEFINED) {
|
||||||
|
size_t buflen = strlen(cmd_compl) + strlen(compl_arg) + 2;
|
||||||
|
char *buffer = xmalloc(buflen);
|
||||||
|
snprintf(buffer, buflen, "%s,%s", cmd_compl, compl_arg);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xstrdup(cmd_compl);
|
||||||
|
}
|
||||||
|
|
||||||
int cmdcomplete_str_to_type(const char *complete_str)
|
int cmdcomplete_str_to_type(const char *complete_str)
|
||||||
{
|
{
|
||||||
if (strncmp(complete_str, "custom,", 7) == 0) {
|
if (strncmp(complete_str, "custom,", 7) == 0) {
|
||||||
|
@ -740,12 +740,13 @@ endfunc
|
|||||||
func Test_getcompletiontype()
|
func Test_getcompletiontype()
|
||||||
call assert_fails('call getcompletiontype()', 'E119:')
|
call assert_fails('call getcompletiontype()', 'E119:')
|
||||||
call assert_fails('call getcompletiontype({})', 'E1174:')
|
call assert_fails('call getcompletiontype({})', 'E1174:')
|
||||||
call assert_equal(getcompletiontype(''), 'command')
|
call assert_equal('command', getcompletiontype(''))
|
||||||
call assert_equal(getcompletiontype('dummy '), '')
|
call assert_equal('', getcompletiontype('dummy '))
|
||||||
call assert_equal(getcompletiontype('cd '), 'dir_in_path')
|
call assert_equal('', getcompletiontype('ls '))
|
||||||
call assert_equal(getcompletiontype('let v:n'), 'var')
|
call assert_equal('dir_in_path', getcompletiontype('cd '))
|
||||||
call assert_equal(getcompletiontype('call tag'), 'function')
|
call assert_equal('var', getcompletiontype('let v:n'))
|
||||||
call assert_equal(getcompletiontype('help '), 'help')
|
call assert_equal('function', getcompletiontype('call tag'))
|
||||||
|
call assert_equal('help', getcompletiontype('help '))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_multibyte_expression()
|
func Test_multibyte_expression()
|
||||||
@ -4251,6 +4252,8 @@ func Test_custom_completion()
|
|||||||
|
|
||||||
call feedkeys(":Test1 \<C-R>=Check_custom_completion()\<CR>\<Esc>", "xt")
|
call feedkeys(":Test1 \<C-R>=Check_custom_completion()\<CR>\<Esc>", "xt")
|
||||||
call feedkeys(":Test2 \<C-R>=Check_customlist_completion()\<CR>\<Esc>", "xt")
|
call feedkeys(":Test2 \<C-R>=Check_customlist_completion()\<CR>\<Esc>", "xt")
|
||||||
|
call assert_equal('custom,CustomComplete1', getcompletiontype('Test1 '))
|
||||||
|
call assert_equal('customlist,CustomComplete2', getcompletiontype('Test2 '))
|
||||||
|
|
||||||
call assert_fails("call getcompletion('', 'custom')", 'E475:')
|
call assert_fails("call getcompletion('', 'custom')", 'E475:')
|
||||||
call assert_fails("call getcompletion('', 'customlist')", 'E475:')
|
call assert_fails("call getcompletion('', 'customlist')", 'E475:')
|
||||||
|
Reference in New Issue
Block a user