diff options
author | Jongwook Choi <wookayin@gmail.com> | 2024-10-04 09:48:31 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-04 06:48:31 -0700 |
commit | d5ae5c84e94a2b15374ee0c7e2f4444c161a8a63 (patch) | |
tree | c37bd8000fe2e27199c2263fc9d9c0357d3f2067 /runtime/lua/vim/_editor.lua | |
parent | 86c5c8724bd85154c4e94474d1d9a0b01e296028 (diff) | |
download | rneovim-d5ae5c84e94a2b15374ee0c7e2f4444c161a8a63.tar.gz rneovim-d5ae5c84e94a2b15374ee0c7e2f4444c161a8a63.tar.bz2 rneovim-d5ae5c84e94a2b15374ee0c7e2f4444c161a8a63.zip |
feat(lua): completion for vim.fn, vim.v, vim.o #30472
Problem: Lua accessors for
- global, local, and special variables (`vim.{g,t,w,b,v}.*`), and
- options (`vim.{o,bo,wo,opt,opt_local,opt_global}.*`),
do not have command-line completion, unlike their vimscript counterparts
(e.g., `g:`, `b:`, `:set`, `:setlocal`, `:call <fn>`, etc.).
Completion for vimscript functions (`vim.fn.*`) is incomplete and does
not list all the available functions.
Solution: Implement completion for vimscript function, variable and
option accessors in `vim._expand_pat` through:
- `getcompletion()` for variable and vimscript function accessors, and
- `nvim_get_all_options_info()` for option accessors.
Note/Remark:
- Short names for options are yet to be implemented.
- Completions for accessors with handles (e.g. `vim.b[0]`, `vim.wo[0]`)
are also yet to be implemented, and are left as future work, which
involves some refactoring of options.
- For performance reasons, we may want to introduce caching for
completing options, but this is not considered at this time since the
number of the available options is not very big (only ~350) and Lua
completion for option accessors appears to be pretty fast.
- Can we have a more "general" framework for customizing completions?
In the future, we may want to improve the implementation by moving the
core logic for generating completion candidates to each accessor (or
its metatable) or through some central interface, rather than writing
all the accessor-specific completion implementations in a single
function: `vim._expand_pat`.
Diffstat (limited to 'runtime/lua/vim/_editor.lua')
-rw-r--r-- | runtime/lua/vim/_editor.lua | 64 |
1 files changed, 62 insertions, 2 deletions
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index bdca97cfb8..2e829578a7 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -787,7 +787,7 @@ function vim._expand_pat(pat, env) if mt and type(mt.__index) == 'table' then field = rawget(mt.__index, key) elseif final_env == vim and (vim._submodules[key] or vim._extra[key]) then - field = vim[key] + field = vim[key] --- @type any end end final_env = field @@ -798,14 +798,24 @@ function vim._expand_pat(pat, env) end local keys = {} --- @type table<string,true> + --- @param obj table<any,any> local function insert_keys(obj) for k, _ in pairs(obj) do - if type(k) == 'string' and string.sub(k, 1, string.len(match_part)) == match_part then + if + type(k) == 'string' + and string.sub(k, 1, string.len(match_part)) == match_part + and k:match('^[_%w]+$') ~= nil -- filter out invalid identifiers for field, e.g. 'foo#bar' + then keys[k] = true end end end + ---@param acc table<string,any> + local function _fold_to_map(acc, k, v) + acc[k] = (v or true) + return acc + end if type(final_env) == 'table' then insert_keys(final_env) @@ -814,11 +824,61 @@ function vim._expand_pat(pat, env) if mt and type(mt.__index) == 'table' then insert_keys(mt.__index) end + if final_env == vim then insert_keys(vim._submodules) insert_keys(vim._extra) end + -- Completion for dict accessors (special vim variables and vim.fn) + if mt and vim.tbl_contains({ vim.g, vim.t, vim.w, vim.b, vim.v, vim.fn }, final_env) then + local prefix, type = unpack( + vim.fn == final_env and { '', 'function' } + or vim.g == final_env and { 'g:', 'var' } + or vim.t == final_env and { 't:', 'var' } + or vim.w == final_env and { 'w:', 'var' } + or vim.b == final_env and { 'b:', 'var' } + or vim.v == final_env and { 'v:', 'var' } + or { nil, nil } + ) + assert(prefix, "Can't resolve final_env") + local vars = vim.fn.getcompletion(prefix .. match_part, type) --- @type string[] + insert_keys(vim + .iter(vars) + :map(function(s) ---@param s string + s = s:gsub('[()]+$', '') -- strip '(' and ')' for function completions + return s:sub(#prefix + 1) -- strip the prefix, e.g., 'g:foo' => 'foo' + end) + :fold({}, _fold_to_map)) + end + + -- Completion for option accessors (full names only) + if + mt + and vim.tbl_contains( + { vim.o, vim.go, vim.bo, vim.wo, vim.opt, vim.opt_local, vim.opt_global }, + final_env + ) + then + --- @type fun(option_name: string, option: vim.api.keyset.get_option_info): boolean + local filter = function(_, _) + return true + end + if vim.bo == final_env then + filter = function(_, option) + return option.scope == 'buf' + end + elseif vim.wo == final_env then + filter = function(_, option) + return option.scope == 'win' + end + end + + --- @type table<string, vim.api.keyset.get_option_info> + local options = vim.api.nvim_get_all_options_info() + insert_keys(vim.iter(options):filter(filter):fold({}, _fold_to_map)) + end + keys = vim.tbl_keys(keys) table.sort(keys) |