diff options
-rw-r--r-- | runtime/doc/lua.txt | 21 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 70 | ||||
-rw-r--r-- | runtime/lua/vim/ui.lua | 36 | ||||
-rwxr-xr-x | scripts/gen_vimdoc.py | 3 | ||||
-rw-r--r-- | src/nvim/lua/vim.lua | 3 | ||||
-rw-r--r-- | test/functional/lua/ui_spec.lua | 46 |
6 files changed, 145 insertions, 34 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 53d68fa5e6..22e323baa7 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1645,4 +1645,25 @@ uri_to_fname({uri}) *vim.uri_to_fname()* Return: ~ Filename + +============================================================================== +Lua module: ui *lua-ui* + +select({items}, {opts}, {on_choice}) *vim.ui.select()* + Prompts the user to pick a single item from a collection of + entries + + Parameters: ~ + {items} table Arbitrary items + {opts} table Additional options + • prompt (string|nil) Text of the prompt. + Defaults to `Select one of:` + • format_item (function item -> text) + Function to format an individual item from + `items` . Defaults to `tostring` . + {on_choice} function ((item|nil, idx|nil) -> ()) Called + once the user made a choice. `idx` is the + 1-based index of `item` within `item` . `nil` + if the user aborted the dialog. + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 624f8b5462..538bd2605b 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -116,42 +116,44 @@ M['textDocument/codeAction'] = function(_, result, ctx) return end - local option_strings = {"Code actions:"} - for i, action in ipairs(result) do - local title = action.title:gsub('\r\n', '\\r\\n') - title = title:gsub('\n', '\\n') - table.insert(option_strings, string.format("%d. %s", i, title)) - end - - local choice = vim.fn.inputlist(option_strings) - if choice < 1 or choice > #result then - return - end - local action = result[choice] - -- textDocument/codeAction can return either Command[] or CodeAction[] - -- - -- CodeAction - -- ... - -- edit?: WorkspaceEdit -- <- must be applied before command - -- command?: Command - -- - -- Command: - -- title: string - -- command: string - -- arguments?: any[] - -- - if action.edit then - util.apply_workspace_edit(action.edit) - end - if action.command then - local command = type(action.command) == 'table' and action.command or action - local fn = vim.lsp.commands[command.command] - if fn then - fn(command, ctx) - else - buf.execute_command(command) + ---@private + local function on_user_choice(action) + if not action then + return + end + -- textDocument/codeAction can return either Command[] or CodeAction[] + -- + -- CodeAction + -- ... + -- edit?: WorkspaceEdit -- <- must be applied before command + -- command?: Command + -- + -- Command: + -- title: string + -- command: string + -- arguments?: any[] + -- + if action.edit then + util.apply_workspace_edit(action.edit) + end + if action.command then + local command = type(action.command) == 'table' and action.command or action + local fn = vim.lsp.commands[command.command] + if fn then + fn(command, ctx) + else + buf.execute_command(command) + end end end + + vim.ui.select(result, { + prompt = 'Code actions:', + format_entry = function(action) + local title = action.title:gsub('\r\n', '\\r\\n') + return title:gsub('\n', '\\n') + end, + }, on_user_choice) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua new file mode 100644 index 0000000000..aebb63d56f --- /dev/null +++ b/runtime/lua/vim/ui.lua @@ -0,0 +1,36 @@ +local M = {} + +--- Prompts the user to pick a single item from a collection of entries +--- +---@param items table Arbitrary items +---@param opts table Additional options +--- - prompt (string|nil) +--- Text of the prompt. Defaults to `Select one of:` +--- - format_item (function item -> text) +--- Function to format an +--- individual item from `items`. Defaults to `tostring`. +---@param on_choice function ((item|nil, idx|nil) -> ()) +--- Called once the user made a choice. +--- `idx` is the 1-based index of `item` within `item`. +--- `nil` if the user aborted the dialog. +function M.select(items, opts, on_choice) + vim.validate { + items = { items, 'table', false }, + on_choice = { on_choice, 'function', false }, + } + opts = opts or {} + local choices = {opts.prompt or 'Select one of:'} + local format_entry = opts.format_entry or tostring + for i, item in pairs(items) do + table.insert(choices, string.format('%d: %s', i, format_entry(item))) + end + local choice = vim.fn.inputlist(choices) + if choice < 1 or choice > #items then + on_choice(nil, nil) + else + on_choice(items[choice], choice) + end +end + + +return M diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index 64ed8d61f6..36e01153f1 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -123,11 +123,13 @@ CONFIG = { 'vim.lua', 'shared.lua', 'uri.lua', + 'ui.lua', ], 'files': ' '.join([ os.path.join(base_dir, 'src/nvim/lua/vim.lua'), os.path.join(base_dir, 'runtime/lua/vim/shared.lua'), os.path.join(base_dir, 'runtime/lua/vim/uri.lua'), + os.path.join(base_dir, 'runtime/lua/vim/ui.lua'), ]), 'file_patterns': '*.lua', 'fn_name_prefix': '', @@ -141,6 +143,7 @@ CONFIG = { # `shared` functions are exposed on the `vim` module. 'shared': 'vim', 'uri': 'vim', + 'ui': 'vim.ui', }, 'append_only': [ 'shared.lua', diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index ba124c41ad..7a209f2d79 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -108,6 +108,9 @@ setmetatable(vim, { elseif key == 'diagnostic' then t.diagnostic = require('vim.diagnostic') return t.diagnostic + elseif key == 'ui' then + t.ui = require('vim.ui') + return t.ui end end }) diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua new file mode 100644 index 0000000000..25a208b70b --- /dev/null +++ b/test/functional/lua/ui_spec.lua @@ -0,0 +1,46 @@ +local helpers = require('test.functional.helpers')(after_each) +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local clear = helpers.clear + +describe('vim.ui', function() + before_each(function() + clear() + end) + + + describe('select', function() + it('can select an item', function() + local result = exec_lua[[ + local items = { + { name = 'Item 1' }, + { name = 'Item 2' }, + } + local opts = { + format_entry = function(entry) + return entry.name + end + } + local selected + local cb = function(item) + selected = item + end + -- inputlist would require input and block the test; + local choices + vim.fn.inputlist = function(x) + choices = x + return 1 + end + vim.ui.select(items, opts, cb) + vim.wait(100, function() return selected ~= nil end) + return {selected, choices} + ]] + eq({ name = 'Item 1' }, result[1]) + eq({ + 'Select one of:', + '1: Item 1', + '2: Item 2', + }, result[2]) + end) + end) +end) |