diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-01-17 20:49:08 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-17 20:49:08 +0800 |
commit | 99186508d9a1b7536441112f9bbe48690592b5d7 (patch) | |
tree | 8d9c872732f56feb3175819b1f532250f7e0717c | |
parent | 2d71ed9929d6393ed46f9d0c218ed9ef37b67f1f (diff) | |
parent | c416da9d1a41b919412d88aecf827aebb8ea973b (diff) | |
download | rneovim-99186508d9a1b7536441112f9bbe48690592b5d7.tar.gz rneovim-99186508d9a1b7536441112f9bbe48690592b5d7.tar.bz2 rneovim-99186508d9a1b7536441112f9bbe48690592b5d7.zip |
Merge pull request #21859 from zeertzjq/vim-8.2.4617
vim-patch:8.2.{4617,4618,4620,5126}
-rw-r--r-- | runtime/doc/builtin.txt | 6 | ||||
-rw-r--r-- | src/nvim/cmdexpand.c | 63 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 2 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 63 | ||||
-rw-r--r-- | src/nvim/runtime.c | 11 | ||||
-rw-r--r-- | src/nvim/testdir/test_cmdline.vim | 27 | ||||
-rw-r--r-- | src/nvim/testdir/test_substitute.vim | 13 | ||||
-rw-r--r-- | src/nvim/usercmd.c | 1 | ||||
-rw-r--r-- | src/nvim/vim.h | 1 |
9 files changed, 142 insertions, 45 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index bdbd026a09..ca4518d1cc 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -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 diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 4f6936958f..b5956d75ab 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -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; } diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 68db03bd7d..c8b6ceab69 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -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', }, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index e11dd0a2f6..511251f0b5 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -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; diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index ea975042ed..321023bce2 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -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; diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index ea7bfae639..ebcbc8177e 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -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' diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index 5fd30c7da7..c99a0d456d 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -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 diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 22e092781c..9c4fdd4ead 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -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. diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 0ab9d96173..c4baa911f1 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -155,6 +155,7 @@ enum { EXPAND_ARGLIST, EXPAND_DIFF_BUFFERS, EXPAND_BREAKPOINT, + EXPAND_SCRIPTNAMES, EXPAND_CHECKHEALTH, EXPAND_LUA, }; |