From 79571b92ced968ad27bee2a7515a4a04e84dbad2 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 27 Jan 2021 09:00:28 +0100 Subject: feat(lua): omnifunc for builting lua interpreter also make implicit submodules "uri" and "_inspector" work with completion this is needed for `:lua=vim.uri_` wildmenu completion to work even before uri or _inspector functions are used. --- runtime/doc/lua.txt | 6 ++++ runtime/doc/news.txt | 2 ++ runtime/lua/vim/_editor.lua | 41 +++++++++++++++++++--- src/nvim/lua/executor.c | 6 ++++ .../lua/command_line_completion_spec.lua | 7 ++-- test/functional/lua/vim_spec.lua | 23 ++++++++++++ 6 files changed, 79 insertions(+), 6 deletions(-) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 1eb5ab41e6..cb309eaf1a 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1404,6 +1404,12 @@ inspect({object}, {options}) *vim.inspect()* https://github.com/kikito/inspect.lua https://github.com/mpeterv/vinspect +lua_omnifunc({find_start}, {_}) *vim.lua_omnifunc()* + Omnifunc for completing lua values from from the runtime lua interpreter, + similar to the builtin completion for the `:lua` command. + + Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a lua buffer. + notify({msg}, {level}, {opts}) *vim.notify()* Display a notification to the user. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 415195e27e..41b59681ae 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -189,6 +189,8 @@ The following new APIs or features were added. disabled by default and can be enabled by setting the `workspace.didChangeWatchedFiles.dynamicRegistration=true` capability. +• Added an omnifunc implementation for lua, |vim.lua_omnifunc()| + ============================================================================== CHANGED FEATURES *news-changes* diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index c205451ff9..9516233b45 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -42,6 +42,18 @@ for k, v in pairs({ vim._submodules[k] = v end +-- There are things which have special rules in vim._init_packages +-- for legacy reasons (uri) or for performance (_inspector). +-- most new things should go into a submodule namespace ( vim.foobar.do_thing() ) +vim._extra = { + uri_from_fname = true, + uri_from_bufnr = true, + uri_to_fname = true, + uri_to_bufnr = true, + show_pos = true, + inspect_pos = true, +} + vim.log = { levels = { TRACE = 0, @@ -575,15 +587,13 @@ function vim._on_key(char) end --- Generate a list of possible completions for the string. ---- String starts with ^ and then has the pattern. +--- String has the pattern. --- --- 1. Can we get it to just return things in the global namespace with that name prefix --- 2. Can we get it to return things from global namespace even with `print(` in front. function vim._expand_pat(pat, env) env = env or _G - pat = string.sub(pat, 2, #pat) - if pat == '' then local result = vim.tbl_keys(env) table.sort(result) @@ -644,7 +654,7 @@ function vim._expand_pat(pat, env) local mt = getmetatable(final_env) if mt and type(mt.__index) == 'table' then field = rawget(mt.__index, key) - elseif final_env == vim and vim._submodules[key] then + elseif final_env == vim and (vim._submodules[key] or vim._extra[key]) then field = vim[key] end end @@ -674,6 +684,7 @@ function vim._expand_pat(pat, env) end if final_env == vim then insert_keys(vim._submodules) + insert_keys(vim._extra) end keys = vim.tbl_keys(keys) @@ -745,6 +756,28 @@ vim._expand_pat_get_parts = function(lua_string) return parts, search_index end +do + -- Ideally we should just call complete() inside omnifunc, though there are + -- some bugs, so fake the two-step dance for now. + local matches + + --- Omnifunc for completing lua values from from the runtime lua interpreter, + --- similar to the builtin completion for the `:lua` command. + --- + --- Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a lua buffer. + function vim.lua_omnifunc(find_start, _) + if find_start == 1 then + local line = vim.api.nvim_get_current_line() + local prefix = string.sub(line, 1, vim.api.nvim_win_get_cursor(0)[2]) + local pos + matches, pos = vim._expand_pat(prefix) + return (#matches > 0 and pos) or -1 + else + return matches + end + end +end + ---Prints given arguments in human-readable format. ---Example: ---
lua
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index bb461a7f13..8a50c8fe4f 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1872,6 +1872,12 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results)
   lua_getfield(lstate, -1, "_expand_pat");
   luaL_checktype(lstate, -1, LUA_TFUNCTION);
 
+  // ex expansion prepends a ^, but don't worry, it is not a regex
+  if (pat[0] != '^') {
+    return FAIL;
+  }
+  pat++;
+
   // [ vim, vim._expand_pat, buf ]
   lua_pushlstring(lstate, (const char *)pat, strlen(pat));
 
diff --git a/test/functional/lua/command_line_completion_spec.lua b/test/functional/lua/command_line_completion_spec.lua
index 3a5966755e..9a0d534358 100644
--- a/test/functional/lua/command_line_completion_spec.lua
+++ b/test/functional/lua/command_line_completion_spec.lua
@@ -5,7 +5,7 @@ local eq = helpers.eq
 local exec_lua = helpers.exec_lua
 
 local get_completions = function(input, env)
-  return exec_lua("return {vim._expand_pat(...)}", '^' .. input, env)
+  return exec_lua("return {vim._expand_pat(...)}", input, env)
 end
 
 local get_compl_parts = function(parts)
@@ -107,9 +107,12 @@ describe('nlua_expand_pat', function()
   end)
 
   it('should work with lazy submodules of "vim" global', function()
-    eq({{ 'inspect' }, 4 },
+    eq({{ 'inspect', 'inspect_pos' }, 4 },
        get_completions('vim.inspec'))
 
+    eq({{ 'treesitter' }, 4 },
+       get_completions('vim.treesi'))
+
     eq({{ 'set' }, 11 },
        get_completions('vim.keymap.se'))
   end)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index b43e5b28db..77628487ca 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -2900,6 +2900,29 @@ describe('lua stdlib', function()
     end)
   end)
 
+  it('vim.lua_omnifunc', function()
+    local screen = Screen.new(60,5)
+    screen:set_default_attr_ids {
+      [1] = {foreground = Screen.colors.Blue1, bold = true};
+      [2] = {background = Screen.colors.WebGray};
+      [3] = {background = Screen.colors.LightMagenta};
+      [4] = {bold = true};
+      [5] = {foreground = Screen.colors.SeaGreen, bold = true};
+    }
+    screen:attach()
+    command [[ set omnifunc=v:lua.vim.lua_omnifunc ]]
+
+    -- Note: the implementation is shared with lua command line completion.
+    -- More tests for completion in lua/command_line_completion_spec.lua
+    feed [[ivim.insp]]
+    screen:expect{grid=[[
+      vim.inspect^                                                 |
+      {1:~  }{2: inspect        }{1:                                         }|
+      {1:~  }{3: inspect_pos    }{1:                                         }|
+      {1:~                                                           }|
+      {4:-- Omni completion (^O^N^P) }{5:match 1 of 2}                    |
+    ]]}
+  end)
 end)
 
 describe('lua: builtin modules', function()
-- 
cgit