Merge pull request #21859 from zeertzjq/vim-8.2.4617

vim-patch:8.2.{4617,4618,4620,5126}
This commit is contained in:
zeertzjq 2023-01-17 20:49:08 +08:00 committed by GitHub
commit 99186508d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 142 additions and 45 deletions

View File

@ -2983,6 +2983,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
messages |:messages| suboptions
option options
packadd optional package |pack-add| names
scriptnames sourced script names |:scriptnames|
shellcmd Shell command
sign |:sign| suboptions
syntax syntax file names |'syntax'|
@ -3002,7 +3003,10 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
If the 'wildoptions' option contains "fuzzy", then fuzzy
matching is used to get the completion matches. Otherwise
regular expression matching is used.
regular expression matching is used. Thus this function
follows the user preference, what happens on the command line.
If you do not want this you can make 'wildoptions' empty
before calling getcompletion() and restore it afterwards.
If {type} is "cmdline", then the |cmdline-completion| result is
returned. For example, to complete the possible values after

View File

@ -1760,6 +1760,22 @@ static const char *set_context_in_breakadd_cmd(expand_T *xp, const char *arg, cm
return NULL;
}
static const char *set_context_in_scriptnames_cmd(expand_T *xp, const char *arg)
{
xp->xp_context = EXPAND_NOTHING;
xp->xp_pattern = NULL;
char *p = skipwhite(arg);
if (ascii_isdigit(*p)) {
return NULL;
}
xp->xp_context = EXPAND_SCRIPTNAMES;
xp->xp_pattern = p;
return NULL;
}
/// Set the completion context in "xp" for command "cmd" with index "cmdidx".
/// The argument to the command is "arg" and the argument flags is "argt".
/// For user-defined commands and for environment variables, "context" has the
@ -2119,6 +2135,9 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
case CMD_breakdel:
return set_context_in_breakadd_cmd(xp, arg, cmdidx);
case CMD_scriptnames:
return set_context_in_scriptnames_cmd(xp, arg);
case CMD_lua:
xp->xp_context = EXPAND_LUA;
break;
@ -2496,6 +2515,19 @@ static char *get_breakadd_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
return NULL;
}
/// Function given to ExpandGeneric() to obtain the possible arguments for the
/// ":scriptnames" command.
static char *get_scriptnames_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (!SCRIPT_ID_VALID(idx + 1)) {
return NULL;
}
scriptitem_T *si = &SCRIPT_ITEM(idx + 1);
home_replace(NULL, si->sn_name, NameBuff, MAXPATHL, true);
return NameBuff;
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":messages {clear}" command.
static char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
@ -2581,6 +2613,7 @@ static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches
{ EXPAND_USER, get_users, true, false },
{ EXPAND_ARGLIST, get_arglist_name, true, false },
{ EXPAND_BREAKPOINT, get_breakadd_arg, true, true },
{ EXPAND_SCRIPTNAMES, get_scriptnames_arg, true, false },
{ EXPAND_CHECKHEALTH, get_healthcheck_names, true, false },
};
int ret = FAIL;
@ -2823,11 +2856,22 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma
return;
}
// Sort the results. Keep menu's in the specified order.
if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) {
if (xp->xp_context == EXPAND_EXPRESSION
|| xp->xp_context == EXPAND_FUNCTIONS
|| xp->xp_context == EXPAND_USER_FUNC) {
// Sort the matches when using regular expression matching and sorting
// applies to the completion context. Menus and scriptnames should be kept
// in the specified order.
const bool sort_matches = !fuzzy
&& xp->xp_context != EXPAND_MENUNAMES
&& xp->xp_context != EXPAND_MENUS
&& xp->xp_context != EXPAND_SCRIPTNAMES;
// <SNR> functions should be sorted to the end.
const bool funcsort = xp->xp_context == EXPAND_EXPRESSION
|| xp->xp_context == EXPAND_FUNCTIONS
|| xp->xp_context == EXPAND_USER_FUNC;
// Sort the matches.
if (sort_matches) {
if (funcsort) {
// <SNR> functions should be sorted to the end.
qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(char *), sort_func_compare);
} else {
@ -2839,15 +2883,6 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma
*matches = ga.ga_data;
*numMatches = ga.ga_len;
} else {
bool funcsort = false;
if (xp->xp_context == EXPAND_EXPRESSION
|| xp->xp_context == EXPAND_FUNCTIONS
|| xp->xp_context == EXPAND_USER_FUNC) {
// <SNR> functions should be sorted to the end.
funcsort = true;
}
fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, funcsort);
*numMatches = ga.ga_len;
}

View File

@ -2387,7 +2387,7 @@ module.cmds = {
},
{
command='scriptnames',
flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
flags=bit.bor(BANG, FILES, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_scriptnames',
},

View File

@ -2890,6 +2890,33 @@ static void append_command(char *cmd)
*d = NUL;
}
/// Return true and set "*idx" if "p" points to a one letter command.
/// - The 'k' command can directly be followed by any character.
/// - The 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
/// but :sre[wind] is another command, as are :scr[iptnames],
/// :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
static int one_letter_cmd(const char *p, cmdidx_T *idx)
{
if (*p == 'k') {
*idx = CMD_k;
return true;
}
if (p[0] == 's'
&& ((p[1] == 'c'
&& (p[2] == NUL
|| (p[2] != 's' && p[2] != 'r'
&& (p[3] == NUL
|| (p[3] != 'i' && p[4] != 'p')))))
|| p[1] == 'g'
|| (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g')
|| p[1] == 'I'
|| (p[1] == 'r' && p[2] != 'e'))) {
*idx = CMD_substitute;
return true;
}
return false;
}
/// Find an Ex command by its name, either built-in or user.
/// Start of the name can be found at eap->cmd.
/// Sets eap->cmdidx and returns a pointer to char after the command name.
@ -2900,27 +2927,8 @@ char *find_ex_command(exarg_T *eap, int *full)
FUNC_ATTR_NONNULL_ARG(1)
{
// Isolate the command and search for it in the command table.
// Exceptions:
// - the 'k' command can directly be followed by any character.
// - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
// but :sre[wind] is another command, as are :scr[iptnames],
// :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
// - the "d" command can directly be followed by 'l' or 'p' flag.
char *p = eap->cmd;
if (*p == 'k') {
eap->cmdidx = CMD_k;
p++;
} else if (p[0] == 's'
&& ((p[1] == 'c'
&& (p[2] == NUL
|| (p[2] != 's' && p[2] != 'r'
&& (p[3] == NUL
|| (p[3] != 'i' && p[4] != 'p')))))
|| p[1] == 'g'
|| (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g')
|| p[1] == 'I'
|| (p[1] == 'r' && p[2] != 'e'))) {
eap->cmdidx = CMD_substitute;
if (one_letter_cmd(p, &eap->cmdidx)) {
p++;
} else {
while (ASCII_ISALPHA(*p)) {
@ -2938,6 +2946,7 @@ char *find_ex_command(exarg_T *eap, int *full)
p++;
}
int len = (int)(p - eap->cmd);
// The "d" command can directly be followed by 'l' or 'p' flag.
if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) {
// Check for ":dl", ":dell", etc. to ":deletel": that's
// :delete with the 'l' flag. Same for 'p'.
@ -3135,9 +3144,11 @@ cmdidx_T excmd_get_cmdidx(const char *cmd, size_t len)
{
cmdidx_T idx;
for (idx = (cmdidx_T)0; (int)idx < CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) {
if (strncmp(cmdnames[(int)idx].cmd_name, cmd, len) == 0) {
break;
if (!one_letter_cmd(cmd, &idx)) {
for (idx = (cmdidx_T)0; (int)idx < CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) {
if (strncmp(cmdnames[(int)idx].cmd_name, cmd, len) == 0) {
break;
}
}
}
@ -5238,9 +5249,9 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
old_curwin == NULL ? curwin : NULL);
} else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit)
|| *eap->arg != NUL) {
// Can't edit another file when "curbuf->b_ro_locked" is set. Only ":edit"
// can bring us here, others are stopped earlier.
if (*eap->arg != NUL && curbuf_locked()) {
// Can't edit another file when "textlock" or "curbuf->b_ro_locked" is set.
// Only ":edit" or ":script" can bring us here, others are stopped earlier.
if (*eap->arg != NUL && text_or_buf_locked()) {
return;
}
n = readonlymode;

View File

@ -2160,12 +2160,17 @@ scriptitem_T *get_current_script_id(char **fnamep, sctx_T *ret_sctx)
/// ":scriptnames"
void ex_scriptnames(exarg_T *eap)
{
if (eap->addr_count > 0) {
if (eap->addr_count > 0 || *eap->arg != NUL) {
// :script {scriptId}: edit the script
if (eap->line2 < 1 || eap->line2 > script_items.ga_len) {
if (eap->addr_count > 0 && !SCRIPT_ID_VALID(eap->line2)) {
emsg(_(e_invarg));
} else {
eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
if (eap->addr_count > 0) {
eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
} else {
expand_env(eap->arg, NameBuff, MAXPATHL);
eap->arg = NameBuff;
}
do_exedit(eap, NULL);
}
return;

View File

@ -3378,6 +3378,33 @@ func Test_cmdline_complete_breakdel()
call assert_equal("\"breakdel here ", @:)
endfunc
" Test for :scriptnames argument completion
func Test_cmdline_complete_scriptnames()
set wildmenu
call writefile(['let a = 1'], 'Xa1b2c3.vim')
source Xa1b2c3.vim
call feedkeys(":script \<Tab>\<Left>\<Left>\<C-B>\"\<CR>", 'tx')
call assert_match("\"script .*Xa1b2c3.vim$", @:)
call feedkeys(":script \<Tab>\<Left>\<Left>\<C-B>\"\<CR>", 'tx')
call assert_match("\"script .*Xa1b2c3.vim$", @:)
call feedkeys(":script b2c3\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"script b2c3", @:)
call feedkeys(":script 2\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_match("\"script 2\<Tab>$", @:)
call feedkeys(":script \<Tab>\<Left>\<Left> \<Tab>\<C-B>\"\<CR>", 'tx')
call assert_match("\"script .*Xa1b2c3.vim $", @:)
call feedkeys(":script \<Tab>\<Left>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"script ", @:)
call assert_match('Xa1b2c3.vim$', getcompletion('.*Xa1b2.*', 'scriptnames')[0])
call assert_equal([], getcompletion('Xa1b2', 'scriptnames'))
new
call feedkeys(":script \<Tab>\<Left>\<Left>\<CR>", 'tx')
call assert_equal('Xa1b2c3.vim', fnamemodify(@%, ':t'))
bw!
call delete('Xa1b2c3.vim')
set wildmenu&
endfunc
" this was going over the end of IObuff
func Test_report_error_with_composing()
let caught = 'no'

View File

@ -1076,6 +1076,19 @@ func Test_sub_open_cmdline_win()
call delete('Xresult')
endfunc
" This was editing a script file from the expression
func Test_sub_edit_scriptfile()
new
norm o0000000000000000000000000000000000000000000000000000
func EditScript()
silent! scr! Xfile
endfunc
s/\%')/\=EditScript()
delfunc EditScript
bwipe!
endfunc
" Test for the 2-letter and 3-letter :substitute commands
func Test_substitute_short_cmd()
new

View File

@ -96,6 +96,7 @@ static const char *command_complete[] = {
[EXPAND_USER] = "user",
[EXPAND_USER_VARS] = "var",
[EXPAND_BREAKPOINT] = "breakpoint",
[EXPAND_SCRIPTNAMES] = "scriptnames",
};
/// List of names of address types. Must be alphabetical for completion.

View File

@ -155,6 +155,7 @@ enum {
EXPAND_ARGLIST,
EXPAND_DIFF_BUFFERS,
EXPAND_BREAKPOINT,
EXPAND_SCRIPTNAMES,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};