neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit ac8ae1596cda8a96af0c26046463ba6327cfb0f8
parent 63689deb45737925cc0d5f9b09a6466b6e536bcc
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Thu, 24 Apr 2025 08:50:33 +0800

vim-patch:9.1.1340: cannot complete :filetype arguments (#33602)

Problem:  cannot complete :filetype arguments (Phạm Bình An)
Solution: add :filetype ex command completion, add "filetypecmd"
          completion type for getcompletion()

fixes: vim/vim#17165
closes: vim/vim#17167

https://github.com/vim/vim/commit/a3422aa3170d49a7c3d1e6cd4242b86e42ef3945

Co-authored-by: Christian Brabandt <cb@256bit.org>
Diffstat:
Mruntime/doc/builtin.txt | 1+
Mruntime/lua/vim/_meta/vimfn.lua | 1+
Msrc/nvim/cmdexpand.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/nvim/cmdexpand_defs.h | 1+
Msrc/nvim/eval.lua | 1+
Msrc/nvim/usercmd.c | 1+
Mtest/old/testdir/test_cmdline.vim | 27+++++++++++++++++++++++++++
7 files changed, 118 insertions(+), 0 deletions(-)

diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt @@ -3401,6 +3401,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* file file and directory names file_in_path file and directory names in |'path'| filetype filetype names |'filetype'| + filetypecmd |:filetype| suboptions function function name help help subjects highlight highlight groups diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua @@ -3048,6 +3048,7 @@ function vim.fn.getcmdwintype() end --- file file and directory names --- file_in_path file and directory names in |'path'| --- filetype filetype names |'filetype'| +--- filetypecmd |:filetype| suboptions --- function function name --- help help subjects --- highlight highlight groups diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c @@ -110,6 +110,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp) && xp->xp_context != EXPAND_FILES && xp->xp_context != EXPAND_FILES_IN_PATH && xp->xp_context != EXPAND_FILETYPE + && xp->xp_context != EXPAND_FILETYPECMD && xp->xp_context != EXPAND_FINDFUNC && xp->xp_context != EXPAND_HELP && xp->xp_context != EXPAND_KEYMAP @@ -1765,6 +1766,19 @@ static const char *set_context_in_lang_cmd(expand_T *xp, const char *arg) } static enum { + EXP_FILETYPECMD_ALL, ///< expand all :filetype values + EXP_FILETYPECMD_PLUGIN, ///< expand plugin on off + EXP_FILETYPECMD_INDENT, ///< expand indent on off + EXP_FILETYPECMD_ONOFF, ///< expand on off +} filetype_expand_what; + +enum { + EXPAND_FILETYPECMD_PLUGIN = 0x01, + EXPAND_FILETYPECMD_INDENT = 0x02, + EXPAND_FILETYPECMD_ONOFF = 0x04, +}; + +static enum { EXP_BREAKPT_ADD, ///< expand ":breakadd" sub-commands EXP_BREAKPT_DEL, ///< expand ":breakdel" sub-commands EXP_PROFDEL, ///< expand ":profdel" sub-commands @@ -1836,6 +1850,47 @@ static const char *set_context_in_scriptnames_cmd(expand_T *xp, const char *arg) return NULL; } +/// Set the completion context for the :filetype command. Always returns NULL. +static const char *set_context_in_filetype_cmd(expand_T *xp, const char *arg) +{ + xp->xp_context = EXPAND_FILETYPECMD; + xp->xp_pattern = (char *)arg; + filetype_expand_what = EXP_FILETYPECMD_ALL; + + char *p = skipwhite(arg); + if (*p == NUL) { + return NULL; + } + + int val = 0; + + while (true) { + if (strncmp(p, "plugin", 6) == 0) { + val |= EXPAND_FILETYPECMD_PLUGIN; + p = skipwhite(p + 6); + continue; + } + if (strncmp(p, "indent", 6) == 0) { + val |= EXPAND_FILETYPECMD_INDENT; + p = skipwhite(p + 6); + continue; + } + break; + } + + if ((val & EXPAND_FILETYPECMD_PLUGIN) && (val & EXPAND_FILETYPECMD_INDENT)) { + filetype_expand_what = EXP_FILETYPECMD_ONOFF; + } else if ((val & EXPAND_FILETYPECMD_PLUGIN)) { + filetype_expand_what = EXP_FILETYPECMD_INDENT; + } else if ((val & EXPAND_FILETYPECMD_INDENT)) { + filetype_expand_what = EXP_FILETYPECMD_PLUGIN; + } + + 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 @@ -2198,6 +2253,9 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa case CMD_scriptnames: return set_context_in_scriptnames_cmd(xp, arg); + case CMD_filetype: + return set_context_in_filetype_cmd(xp, arg); + case CMD_lua: case CMD_equal: xp->xp_context = EXPAND_LUA; @@ -2566,6 +2624,30 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int * } /// Function given to ExpandGeneric() to obtain the possible arguments of the +/// ":filetype {plugin,indent}" command. +static char *get_filetypecmd_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + char *opts_all[] = { "indent", "plugin", "on", "off" }; + char *opts_plugin[] = { "plugin", "on", "off" }; + char *opts_indent[] = { "indent", "on", "off" }; + char *opts_onoff[] = { "on", "off" }; + + if (filetype_expand_what == EXP_FILETYPECMD_ALL && idx < 4) { + return opts_all[idx]; + } + if (filetype_expand_what == EXP_FILETYPECMD_PLUGIN && idx < 3) { + return opts_plugin[idx]; + } + if (filetype_expand_what == EXP_FILETYPECMD_INDENT && idx < 3) { + return opts_indent[idx]; + } + if (filetype_expand_what == EXP_FILETYPECMD_ONOFF && idx < 2) { + return opts_onoff[idx]; + } + return NULL; +} + +/// Function given to ExpandGeneric() to obtain the possible arguments of the /// ":breakadd {expr, file, func, here}" command. /// ":breakdel {func, file, here}" command. static char *get_breakadd_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) @@ -2660,6 +2742,7 @@ static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches int escaped; } tab[] = { { EXPAND_COMMANDS, get_command_name, false, true }, + { EXPAND_FILETYPECMD, get_filetypecmd_arg, true, true }, { EXPAND_MAPCLEAR, get_mapclear_arg, true, true }, { EXPAND_MESSAGES, get_messages_arg, true, true }, { EXPAND_HISTORY, get_history_arg, true, true }, @@ -3643,6 +3726,9 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) set_context_for_wildcard_arg(NULL, xpc.xp_pattern, false, &xpc, &context); xpc.xp_pattern_len = strlen(xpc.xp_pattern); } + if (xpc.xp_context == EXPAND_FILETYPECMD) { + filetype_expand_what = EXP_FILETYPECMD_ALL; + } theend: if (xpc.xp_context == EXPAND_LUA) { diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h @@ -108,6 +108,7 @@ enum { EXPAND_DIRS_IN_CDPATH, EXPAND_SHELLCMDLINE, EXPAND_FINDFUNC, + EXPAND_FILETYPECMD, EXPAND_CHECKHEALTH, EXPAND_LUA, }; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua @@ -3831,6 +3831,7 @@ M.funcs = { file file and directory names file_in_path file and directory names in |'path'| filetype filetype names |'filetype'| + filetypecmd |:filetype| suboptions function function name help help subjects highlight highlight groups diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c @@ -72,6 +72,7 @@ static const char *command_complete[] = { [EXPAND_FILES] = "file", [EXPAND_FILES_IN_PATH] = "file_in_path", [EXPAND_FILETYPE] = "filetype", + [EXPAND_FILETYPECMD] = "filetypecmd", [EXPAND_FUNCTIONS] = "function", [EXPAND_HELP] = "help", [EXPAND_HIGHLIGHT] = "highlight", diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim @@ -548,6 +548,13 @@ func Test_getcompletion() let l = getcompletion('kill', 'expression') call assert_equal([], l) + let l = getcompletion('', 'filetypecmd') + call assert_equal(["indent", "off", "on", "plugin"], l) + let l = getcompletion('not', 'filetypecmd') + call assert_equal([], l) + let l = getcompletion('o', 'filetypecmd') + call assert_equal(['off', 'on'], l) + let l = getcompletion('tag', 'function') call assert_true(index(l, 'taglist(') >= 0) let l = getcompletion('paint', 'function') @@ -3240,6 +3247,26 @@ func Test_fuzzy_completion_behave() set wildoptions& endfunc +" :filetype suboptions completion +func Test_completion_filetypecmd() + set wildoptions& + call feedkeys(":filetype \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"filetype indent off on plugin', @:) + call feedkeys(":filetype plugin \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"filetype plugin indent off on', @:) + call feedkeys(":filetype indent \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"filetype indent off on plugin', @:) + call feedkeys(":filetype i\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"filetype indent', @:) + call feedkeys(":filetype p\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"filetype plugin', @:) + call feedkeys(":filetype o\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"filetype off on', @:) + call feedkeys(":filetype indent of\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"filetype indent off', @:) + set wildoptions& +endfunc + " " colorscheme name fuzzy completion - NOT supported " func Test_fuzzy_completion_colorscheme() " endfunc