diff options
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r-- | runtime/lua/vim/lsp/codelens.lua | 16 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 70 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/rpc.lua | 39 | ||||
-rw-r--r-- | runtime/lua/vim/ui.lua | 36 |
4 files changed, 93 insertions, 68 deletions
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 9cedb2f1db..20b203fe99 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -31,10 +31,24 @@ local function execute_lens(lens, bufnr, client_id) local line = lens.range.start.line api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1) + local command = lens.command + local fn = vim.lsp.commands[command.command] + if fn then + fn(command, { bufnr = bufnr, client_id = client_id }) + return + end -- Need to use the client that returned the lens → must not use buf_request local client = vim.lsp.get_client_by_id(client_id) assert(client, 'Client is required to execute lens, client_id=' .. client_id) - client.request('workspace/executeCommand', lens.command, function(...) + local command_provider = client.server_capabilities.executeCommandProvider + local commands = type(command_provider) == 'table' and command_provider.commands or {} + if not vim.tbl_contains(commands, command.command) then + vim.notify(string.format( + "Language server does not support command `%s`. This command may require a client extension.", command.command), + vim.log.levels.WARN) + return + end + client.request('workspace/executeCommand', command, function(...) local result = vim.lsp.handlers['workspace/executeCommand'](...) M.refresh() return result diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 624f8b5462..def83a7320 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_item = 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/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 716f42faf9..255eb65dfe 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -4,34 +4,6 @@ local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap --- TODO replace with a better implementation. ----@private ---- Encodes to JSON. ---- ----@param data (table) Data to encode ----@returns (string) Encoded object -local function json_encode(data) - local status, result = pcall(vim.json.encode, data) - if status then - return result - else - return nil, result - end -end ----@private ---- Decodes from JSON. ---- ----@param data (string) Data to decode ----@returns (table) Decoded JSON object -local function json_decode(data) - local status, result = pcall(vim.json.decode, data) - if status then - return result - else - return nil, result - end -end - ---@private --- Checks whether a given path exists and is a directory. ---@param filename (string) path to check @@ -389,12 +361,12 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) --- Encodes {payload} into a JSON-RPC message and sends it to the remote --- process. --- - ---@param payload (table) Converted into a JSON string, see |json_encode()| + ---@param payload table ---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing. local function encode_and_send(payload) local _ = log.debug() and log.debug("rpc.send", payload) if handle == nil or handle:is_closing() then return false end - local encoded = assert(json_encode(payload)) + local encoded = vim.json.encode(payload) stdin:write(format_message_with_content_length(encoded)) return true end @@ -485,14 +457,15 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ---@private local function handle_body(body) - local decoded, err = json_decode(body) - if not decoded then - -- on_error(client_errors.INVALID_SERVER_JSON, err) + local ok, decoded = pcall(vim.json.decode, body) + if not ok then + on_error(client_errors.INVALID_SERVER_JSON, decoded) return end local _ = log.debug() and log.debug("rpc.receive", decoded) if type(decoded.method) == 'string' and decoded.id then + local err -- Server Request decoded.params = convert_NIL(decoded.params) -- Schedule here so that the users functions don't trigger an error and diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua new file mode 100644 index 0000000000..5eab20fc54 --- /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_item = opts.format_item or tostring + for i, item in pairs(items) do + table.insert(choices, string.format('%d: %s', i, format_item(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 |