aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
authorTJ DeVries <devries.timothyj@gmail.com>2020-11-12 22:21:34 -0500
committerGitHub <noreply@github.com>2020-11-12 22:21:34 -0500
commitf75be5e9d510d5369c572cf98e78d9480df3b0bb (patch)
treee25baab19bcb47ca0d2edcf0baa18b71cfd03f9e /runtime/lua/vim
parent4ae31c46f75aef7d7a80dd2a8d269c168806a1bd (diff)
downloadrneovim-f75be5e9d510d5369c572cf98e78d9480df3b0bb.tar.gz
rneovim-f75be5e9d510d5369c572cf98e78d9480df3b0bb.tar.bz2
rneovim-f75be5e9d510d5369c572cf98e78d9480df3b0bb.zip
lsp: vim.lsp.diagnostic (#12655)
Breaking Changes: - Deprecated all `vim.lsp.util.{*diagnostics*}()` functions. - Instead, all functions must be found in vim.lsp.diagnostic - For now, they issue a warning ONCE per neovim session. In a "little while" we will remove them completely. - `vim.lsp.callbacks` has moved to `vim.lsp.handlers`. - For a "little while" we will just redirect `vim.lsp.callbacks` to `vim.lsp.handlers`. However, we will remove this at some point, so it is recommended that you change all of your references to `callbacks` into `handlers`. - This also means that for functions like |vim.lsp.start_client()| and similar, keyword style arguments have moved from "callbacks" to "handlers". Once again, these are currently being forward, but will cease to be forwarded in a "little while". - Changed the highlight groups for LspDiagnostic highlight as they were inconsistently named. - For more information, see |lsp-highlight-diagnostics| - Changed the sign group names as well, to be consistent with |lsp-highlight-diagnostics| General Enhancements: - Rewrote much of the getting started help document for lsp. It also provides a much nicer configuration strategy, so as to not recommend globally overwriting builtin neovim mappings. LSP Enhancements: - Introduced the concept of |lsp-handlers| which will allow much better customization for users without having to copy & paste entire files / functions / etc. Diagnostic Enhancements: - "goto next diagnostic" |vim.lsp.diagnostic.goto_next()| - "goto prev diagnostic" |vim.lsp.diagnostic.goto_prev()| - For each of the gotos, auto open diagnostics is available as a configuration option - Configurable diagnostic handling: - See |vim.lsp.diagnostic.on_publish_diagnostics()| - Delay display until after insert mode - Configure signs - Configure virtual text - Configure underline - Set the location list with the buffers diagnostics. - See |vim.lsp.diagnostic.set_loclist()| - Better performance for getting counts and line diagnostics - They are now cached on save, to enhance lookups. - Particularly useful for checking in statusline, etc. - Actual testing :) - See ./test/functional/plugin/lsp/diagnostic_spec.lua - Added `guisp` for underline highlighting NOTE: "a little while" means enough time to feel like most plugins and plugin authors have had a chance to refactor their code to use the updated calls. Then we will remove them completely. There is no need to keep them, because we don't have any released version of neovim that exposes these APIs. I'm trying to be nice to people following HEAD :) Co-authored: [Twitch Chat 2020](https://twitch.tv/teej_dv)
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/F.lua24
-rw-r--r--runtime/lua/vim/highlight.lua16
-rw-r--r--runtime/lua/vim/lsp.lua216
-rw-r--r--runtime/lua/vim/lsp/buf.lua15
-rw-r--r--runtime/lua/vim/lsp/callbacks.lua345
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua1195
-rw-r--r--runtime/lua/vim/lsp/handlers.lua310
-rw-r--r--runtime/lua/vim/lsp/protocol.lua25
-rw-r--r--runtime/lua/vim/lsp/rpc.lua68
-rw-r--r--runtime/lua/vim/lsp/util.lua402
-rw-r--r--runtime/lua/vim/shared.lua4
11 files changed, 1824 insertions, 796 deletions
diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua
new file mode 100644
index 0000000000..5887e978b9
--- /dev/null
+++ b/runtime/lua/vim/F.lua
@@ -0,0 +1,24 @@
+local F = {}
+
+--- Returns {a} if it is not nil, otherwise returns {b}.
+---
+--@param a
+--@param b
+function F.if_nil(a, b)
+ if a == nil then return b end
+ return a
+end
+
+-- Use in combination with pcall
+function F.ok_or_nil(status, ...)
+ if not status then return end
+ return ...
+end
+
+-- Nil pcall.
+function F.npcall(fn, ...)
+ return F.ok_or_nil(pcall(fn, ...))
+end
+
+
+return F
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
index 705b34dc99..0012dce081 100644
--- a/runtime/lua/vim/highlight.lua
+++ b/runtime/lua/vim/highlight.lua
@@ -2,6 +2,22 @@ local api = vim.api
local highlight = {}
+--@private
+function highlight.create(higroup, hi_info, default)
+ local options = {}
+ -- TODO: Add validation
+ for k, v in pairs(hi_info) do
+ table.insert(options, string.format("%s=%s", k, v))
+ end
+ vim.cmd(string.format([[highlight %s %s %s]], default and "default" or "", higroup, table.concat(options, " ")))
+end
+
+--@private
+function highlight.link(higroup, link_to, force)
+ vim.cmd(string.format([[highlight%s link %s %s]], force and "!" or " default", higroup, link_to))
+end
+
+
--- Highlight range between two positions
---
--@param bufnr number of buffer to apply highlighting to
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 1a0015e2db..dacdbcfa17 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1,4 +1,4 @@
-local default_callbacks = require 'vim.lsp.callbacks'
+local default_handlers = require 'vim.lsp.handlers'
local log = require 'vim.lsp.log'
local lsp_rpc = require 'vim.lsp.rpc'
local protocol = require 'vim.lsp.protocol'
@@ -13,16 +13,21 @@ local validate = vim.validate
local lsp = {
protocol = protocol;
- callbacks = default_callbacks;
+
+ -- TODO(tjdevries): Add in the warning that `callbacks` is no longer supported.
+ -- util.warn_once("vim.lsp.callbacks is deprecated. Use vim.lsp.handlers instead.")
+ handlers = default_handlers;
+ callbacks = default_handlers;
+
buf = require'vim.lsp.buf';
+ diagnostic = require'vim.lsp.diagnostic';
util = util;
+
-- Allow raw RPC access.
rpc = lsp_rpc;
+
-- Export these directly from rpc.
rpc_response_error = lsp_rpc.rpc_response_error;
- -- You probably won't need this directly, since __tostring is set for errors
- -- by the RPC.
- -- format_rpc_error = lsp_rpc.format_rpc_error;
}
-- maps request name to the required resolved_capability in the client.
@@ -72,7 +77,7 @@ local function resolve_bufnr(bufnr)
end
--@private
---- callback called by the client when trying to call a method that's not
+--- Called by the client when trying to call a method that's not
--- supported in any of the servers registered for the current buffer.
--@param method (string) name of the method
function lsp._unsupported_method(method)
@@ -115,14 +120,14 @@ local all_buffer_active_clients = {}
local uninitialized_clients = {}
--@private
---- Invokes a callback for each LSP client attached to the buffer {bufnr}.
+--- Invokes a function for each LSP client attached to the buffer {bufnr}.
---
--@param bufnr (Number) of buffer
---@param callback (function({client}, {client_id}, {bufnr}) Function to run on
+--@param fn (function({client}, {client_id}, {bufnr}) Function to run on
---each client attached to that buffer.
-local function for_each_buffer_client(bufnr, callback)
+local function for_each_buffer_client(bufnr, fn)
validate {
- callback = { callback, 'f' };
+ fn = { fn, 'f' };
}
bufnr = resolve_bufnr(bufnr)
local client_ids = all_buffer_active_clients[bufnr]
@@ -132,7 +137,7 @@ local function for_each_buffer_client(bufnr, callback)
for client_id in pairs(client_ids) do
local client = active_clients[client_id]
if client then
- callback(client, client_id, bufnr)
+ fn(client, client_id, bufnr)
end
end
end
@@ -209,7 +214,9 @@ local function validate_client_config(config)
}
validate {
root_dir = { config.root_dir, is_dir, "directory" };
+ -- TODO(remove-callbacks)
callbacks = { config.callbacks, "t", true };
+ handlers = { config.handlers, "t", true };
capabilities = { config.capabilities, "t", true };
cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" };
cmd_env = { config.cmd_env, "t", true };
@@ -220,13 +227,23 @@ local function validate_client_config(config)
before_init = { config.before_init, "f", true };
offset_encoding = { config.offset_encoding, "s", true };
}
+
+ -- TODO(remove-callbacks)
+ if config.handlers and config.callbacks then
+ error(debug.traceback(
+ "Unable to configure LSP with both 'config.handlers' and 'config.callbacks'. Use 'config.handlers' exclusively."
+ ))
+ end
+
local cmd, cmd_args = lsp._cmd_parts(config.cmd)
local offset_encoding = valid_encodings.UTF16
if config.offset_encoding then
offset_encoding = validate_encoding(config.offset_encoding)
end
+
return {
- cmd = cmd; cmd_args = cmd_args;
+ cmd = cmd;
+ cmd_args = cmd_args;
offset_encoding = offset_encoding;
}
end
@@ -276,12 +293,11 @@ end
---
--- - Methods:
---
---- - request(method, params, [callback], bufnr)
+--- - request(method, params, [handler], bufnr)
--- Sends a request to the server.
--- This is a thin wrapper around {client.rpc.request} with some additional
--- checking.
---- If {callback} is not specified, it will use {client.callbacks} to try to
---- find a callback. If one is not found there, then an error will occur.
+--- If {handler} is not specified, If one is not found there, then an error will occur.
--- Returns: {status}, {[client_id]}. {status} is a boolean indicating if
--- the notification was successful. If it is `false`, then it will always
--- be `false` (the client has shutdown).
@@ -325,8 +341,7 @@ end
--- with the server. You can modify this in the `config`'s `on_init` method
--- before text is sent to the server.
---
---- - {callbacks} (table): The callbacks used by the client as
---- described in |lsp-callbacks|.
+--- - {handlers} (table): The handlers used by the client as described in |lsp-handler|.
---
--- - {config} (table): copy of the table that was passed by the user
--- to |vim.lsp.start_client()|.
@@ -378,15 +393,7 @@ end
--- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an
--- array.
---
---@param callbacks Map of language server method names to
---- `function(err, method, params, client_id)` handler. Invoked for:
---- - Notifications to the server, where `err` will always be `nil`.
---- - Requests by the server. For these you can respond by returning
---- two values: `result, err` where err must be shaped like a RPC error,
---- i.e. `{ code, message, data? }`. Use |vim.lsp.rpc_response_error()| to
---- help with this.
---- - Default callback for client requests not explicitly specifying
---- a callback.
+--@param handlers Map of language server method names to |lsp-handler|
---
--@param init_options Values to pass in the initialization request
--- as `initializationOptions`. See `initialize` in the LSP spec.
@@ -437,52 +444,51 @@ function lsp.start_client(config)
local client_id = next_client_id()
- local callbacks = config.callbacks or {}
+ -- TODO(remove-callbacks)
+ local handlers = config.handlers or config.callbacks or {}
local name = config.name or tostring(client_id)
local log_prefix = string.format("LSP[%s]", name)
- local handlers = {}
+ local dispatch = {}
--@private
- --- Returns the callback associated with an LSP method. Returns the default
- --- callback if the user hasn't set a custom one.
+ --- Returns the handler associated with an LSP method.
+ --- Returns the default handler if the user hasn't set a custom one.
---
--@param method (string) LSP method name
- --@returns (fn) The callback for the given method, if defined, or the default
- ---from |lsp-callbacks|
- local function resolve_callback(method)
- return callbacks[method] or default_callbacks[method]
+ --@returns (fn) The handler for the given method, if defined, or the default from |vim.lsp.handlers|
+ local function resolve_handler(method)
+ return handlers[method] or default_handlers[method]
end
--@private
--- Handles a notification sent by an LSP server by invoking the
- --- corresponding callback.
+ --- corresponding handler.
---
--@param method (string) LSP method name
--@param params (table) The parameters for that method.
- function handlers.notification(method, params)
+ function dispatch.notification(method, params)
local _ = log.debug() and log.debug('notification', method, params)
- local callback = resolve_callback(method)
- if callback then
+ local handler = resolve_handler(method)
+ if handler then
-- Method name is provided here for convenience.
- callback(nil, method, params, client_id)
+ handler(nil, method, params, client_id)
end
end
--@private
- --- Handles a request from an LSP server by invoking the corresponding
- --- callback.
+ --- Handles a request from an LSP server by invoking the corresponding handler.
---
--@param method (string) LSP method name
--@param params (table) The parameters for that method
- function handlers.server_request(method, params)
+ function dispatch.server_request(method, params)
local _ = log.debug() and log.debug('server_request', method, params)
- local callback = resolve_callback(method)
- if callback then
- local _ = log.debug() and log.debug("server_request: found callback for", method)
- return callback(nil, method, params, client_id)
+ local handler = resolve_handler(method)
+ if handler then
+ local _ = log.debug() and log.debug("server_request: found handler for", method)
+ return handler(nil, method, params, client_id)
end
- local _ = log.debug() and log.debug("server_request: no callback found for", method)
+ local _ = log.debug() and log.debug("server_request: no handler found for", method)
return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound)
end
@@ -493,7 +499,7 @@ function lsp.start_client(config)
--@param err (...) Other arguments may be passed depending on the error kind
--@see |vim.lsp.client_errors| for possible errors. Use
---`vim.lsp.client_errors[code]` to get a human-friendly name.
- function handlers.on_error(code, err)
+ function dispatch.on_error(code, err)
local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err })
err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err))
if config.on_error then
@@ -510,7 +516,7 @@ function lsp.start_client(config)
---
--@param code (number) exit code of the process
--@param signal (number) the signal used to terminate (if any)
- function handlers.on_exit(code, signal)
+ function dispatch.on_exit(code, signal)
active_clients[client_id] = nil
uninitialized_clients[client_id] = nil
local active_buffers = {}
@@ -523,7 +529,7 @@ function lsp.start_client(config)
-- Buffer level cleanup
vim.schedule(function()
for _, bufnr in ipairs(active_buffers) do
- util.buf_clear_diagnostics(bufnr)
+ lsp.diagnostic.clear(bufnr)
end
end)
if config.on_exit then
@@ -532,7 +538,7 @@ function lsp.start_client(config)
end
-- Start the RPC client.
- local rpc = lsp_rpc.start(cmd, cmd_args, handlers, {
+ local rpc = lsp_rpc.start(cmd, cmd_args, dispatch, {
cwd = config.cmd_cwd;
env = config.cmd_env;
})
@@ -542,12 +548,14 @@ function lsp.start_client(config)
name = name;
rpc = rpc;
offset_encoding = offset_encoding;
- callbacks = callbacks;
config = config;
+
+ -- TODO(remove-callbacks)
+ callbacks = handlers;
+ handlers = handlers;
}
- -- Store the uninitialized_clients for cleanup in case we exit before
- -- initialize finishes.
+ -- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
uninitialized_clients[client_id] = client;
--@private
@@ -641,13 +649,11 @@ function lsp.start_client(config)
--- Sends a request to the server.
---
--- This is a thin wrapper around {client.rpc.request} with some additional
- --- checks for capabilities and callback availability.
+ --- checks for capabilities and handler availability.
---
--@param method (string) LSP method name.
--@param params (table) LSP request params.
- --@param callback (function, optional) Response handler for this method.
- ---If {callback} is not specified, it will use {client.callbacks} to try to
- ---find a callback. If one is not found there, then an error will occur.
+ --@param handler (function, optional) Response |lsp-handler| for this method.
--@param bufnr (number) Buffer handle (0 for current).
--@returns ({status}, [request_id]): {status} is a bool indicating
---whether the request was successful. If it is `false`, then it will
@@ -656,16 +662,14 @@ function lsp.start_client(config)
---second result. You can use this with `client.cancel_request(request_id)`
---to cancel the-request.
--@see |vim.lsp.buf_request()|
- function client.request(method, params, callback, bufnr)
- -- FIXME: callback is optional, but bufnr is apparently not? Shouldn't that
- -- require a `select('#', ...)` call?
- if not callback then
- callback = resolve_callback(method)
- or error(string.format("not found: %q request callback for client %q.", method, client.name))
+ function client.request(method, params, handler, bufnr)
+ if not handler then
+ handler = resolve_handler(method)
+ or error(string.format("not found: %q request handler for client %q.", method, client.name))
end
- local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback, bufnr)
+ local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr)
return rpc.request(method, params, function(err, result)
- callback(err, method, result, client_id, bufnr)
+ handler(err, method, result, client_id, bufnr)
end)
end
@@ -995,19 +999,18 @@ nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()")
--@param bufnr (number) Buffer handle, or 0 for current.
--@param method (string) LSP method name
--@param params (optional, table) Parameters to send to the server
---@param callback (optional, functionnil) Handler
--- `function(err, method, params, client_id)` for this request. Defaults
--- to the client callback in `client.callbacks`. See |lsp-callbacks|.
+--@param handler (optional, function) See |lsp-handler|
+-- If nil, follows resolution strategy defined in |lsp-handler-configuration|
--
--@returns 2-tuple:
--- - Map of client-id:request-id pairs for all successful requests.
--- - Function which can be used to cancel all the requests. You could instead
--- iterate all clients and call their `cancel_request()` methods.
-function lsp.buf_request(bufnr, method, params, callback)
+function lsp.buf_request(bufnr, method, params, handler)
validate {
bufnr = { bufnr, 'n', true };
method = { method, 's' };
- callback = { callback, 'f', true };
+ handler = { handler, 'f', true };
}
local client_request_ids = {}
@@ -1015,7 +1018,7 @@ function lsp.buf_request(bufnr, method, params, callback)
for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr)
if client.supports_method(method) then
method_supported = true
- local request_success, request_id = client.request(method, params, callback, resolved_bufnr)
+ local request_success, request_id = client.request(method, params, handler, resolved_bufnr)
-- This could only fail if the client shut down in the time since we looked
-- it up and we did the request, which should be rare.
@@ -1025,13 +1028,13 @@ function lsp.buf_request(bufnr, method, params, callback)
end
end)
- -- if no clients support the given method, call the callback with the proper
+ -- if no clients support the given method, call the handler with the proper
-- error message.
if not method_supported then
local unsupported_err = lsp._unsupported_method(method)
- local cb = callback or lsp.callbacks[method]
- if cb then
- cb(unsupported_err, method, bufnr)
+ handler = handler or lsp.handlers[method]
+ if handler then
+ handler(unsupported_err, method, bufnr)
end
return
end
@@ -1064,11 +1067,11 @@ end
function lsp.buf_request_sync(bufnr, method, params, timeout_ms)
local request_results = {}
local result_count = 0
- local function _callback(err, _method, result, client_id)
+ local function _sync_handler(err, _, result, client_id)
request_results[client_id] = { error = err, result = result }
result_count = result_count + 1
end
- local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, _callback)
+ local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, _sync_handler)
local expected_result_count = 0
for _ in pairs(client_request_ids) do
expected_result_count = expected_result_count + 1
@@ -1209,22 +1212,53 @@ function lsp.get_log_path()
return log.get_filename()
end
--- Defines the LspDiagnostics signs if they're not defined already.
-do
- --@private
- --- Defines a sign if it isn't already defined.
- --@param name (String) Name of the sign
- --@param properties (table) Properties to attach to the sign
- local function define_default_sign(name, properties)
- if vim.tbl_isempty(vim.fn.sign_getdefined(name)) then
- vim.fn.sign_define(name, properties)
+--- Call {fn} for every client attached to {bufnr}
+function lsp.for_each_buffer_client(bufnr, fn)
+ return for_each_buffer_client(bufnr, fn)
+end
+
+--- Function to manage overriding defaults for LSP handlers.
+--@param handler (function) See |lsp-handler|
+--@param override_config (table) Table containing the keys to override behavior of the {handler}
+function lsp.with(handler, override_config)
+ return function(err, method, params, client_id, bufnr, config)
+ return handler(err, method, params, client_id, bufnr, vim.tbl_deep_extend("force", config or {}, override_config))
+ end
+end
+
+--- Helper function to use when implementing a handler.
+--- This will check that all of the keys in the user configuration
+--- are valid keys and make sense to include for this handler.
+---
+--- Will error on invalid keys (i.e. keys that do not exist in the options)
+function lsp._with_extend(name, options, user_config)
+ user_config = user_config or {}
+
+ local resulting_config = {}
+ for k, v in pairs(user_config) do
+ if options[k] == nil then
+ error(debug.traceback(string.format(
+ "Invalid option for `%s`: %s. Valid options are:\n%s",
+ name,
+ k,
+ vim.inspect(vim.tbl_keys(options))
+ )))
end
+
+ resulting_config[k] = v
end
- define_default_sign('LspDiagnosticsErrorSign', {text='E', texthl='LspDiagnosticsErrorSign', linehl='', numhl=''})
- define_default_sign('LspDiagnosticsWarningSign', {text='W', texthl='LspDiagnosticsWarningSign', linehl='', numhl=''})
- define_default_sign('LspDiagnosticsInformationSign', {text='I', texthl='LspDiagnosticsInformationSign', linehl='', numhl=''})
- define_default_sign('LspDiagnosticsHintSign', {text='H', texthl='LspDiagnosticsHintSign', linehl='', numhl=''})
+
+ for k, v in pairs(options) do
+ if resulting_config[k] == nil then
+ resulting_config[k] = v
+ end
+ end
+
+ return resulting_config
end
+-- Define the LspDiagnostics signs if they're not defined already.
+require('vim.lsp.diagnostic')._define_default_signs_and_highlights()
+
return lsp
-- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 0b8e08f36c..fa62905c0a 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -30,9 +30,7 @@ end
---
--@param method (string) LSP method name
--@param params (optional, table) Parameters to send to the server
---@param callback (optional, functionnil) Handler
--- `function(err, method, params, client_id)` for this request. Defaults
--- to the client callback in `client.callbacks`. See |lsp-callbacks|.
+--@param handler (optional, functionnil) See |lsp-handler|. Follows |lsp-handler-resolution|
--
--@returns 2-tuple:
--- - Map of client-id:request-id pairs for all successful requests.
@@ -40,12 +38,12 @@ end
--- iterate all clients and call their `cancel_request()` methods.
---
--@see |vim.lsp.buf_request()|
-local function request(method, params, callback)
+local function request(method, params, handler)
validate {
method = {method, 's'};
- callback = {callback, 'f', true};
+ handler = {handler, 'f', true};
}
- return vim.lsp.buf_request(0, method, params, callback)
+ return vim.lsp.buf_request(0, method, params, handler)
end
--- Checks whether the language servers attached to the current buffer are
@@ -64,6 +62,7 @@ function M.hover()
end
--- Jumps to the declaration of the symbol under the cursor.
+--@note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead.
---
function M.declaration()
local params = util.make_position_params()
@@ -279,7 +278,7 @@ end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
function M.code_action(context)
validate { context = { context, 't', true } }
- context = context or { diagnostics = util.get_line_diagnostics() }
+ context = context or { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() }
local params = util.make_range_params()
params.context = context
request('textDocument/codeAction', params)
@@ -294,7 +293,7 @@ end
---Defaults to the end of the last visual selection.
function M.range_code_action(context, start_pos, end_pos)
validate { context = { context, 't', true } }
- context = context or { diagnostics = util.get_line_diagnostics() }
+ context = context or { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() }
local params = util.make_given_range_params(start_pos, end_pos)
params.context = context
request('textDocument/codeAction', params)
diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua
index 3270d1d2a9..1da92b900d 100644
--- a/runtime/lua/vim/lsp/callbacks.lua
+++ b/runtime/lua/vim/lsp/callbacks.lua
@@ -1,345 +1,4 @@
-local log = require 'vim.lsp.log'
-local protocol = require 'vim.lsp.protocol'
local util = require 'vim.lsp.util'
-local vim = vim
-local api = vim.api
-local buf = require 'vim.lsp.buf'
-local M = {}
-
--- FIXME: DOC: Expose in vimdocs
-
---@private
---- Writes to error buffer.
---@param ... (table of strings) Will be concatenated before being written
-local function err_message(...)
- api.nvim_err_writeln(table.concat(vim.tbl_flatten{...}))
- api.nvim_command("redraw")
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
-M['workspace/executeCommand'] = function(err, _)
- if err then
- error("Could not execute code action: "..err.message)
- end
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
-M['textDocument/codeAction'] = function(_, _, actions)
- if actions == nil or vim.tbl_isempty(actions) then
- print("No code actions available")
- return
- end
-
- local option_strings = {"Code Actions:"}
- for i, action in ipairs(actions) 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 > #actions then
- return
- end
- local action_chosen = actions[choice]
- -- textDocument/codeAction can return either Command[] or CodeAction[].
- -- If it is a CodeAction, it can have either an edit, a command or both.
- -- Edits should be executed first
- if action_chosen.edit or type(action_chosen.command) == "table" then
- if action_chosen.edit then
- util.apply_workspace_edit(action_chosen.edit)
- end
- if type(action_chosen.command) == "table" then
- buf.execute_command(action_chosen.command)
- end
- else
- buf.execute_command(action_chosen)
- end
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
-M['workspace/applyEdit'] = function(_, _, workspace_edit)
- if not workspace_edit then return end
- -- TODO(ashkan) Do something more with label?
- if workspace_edit.label then
- print("Workspace edit", workspace_edit.label)
- end
- local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit)
- return {
- applied = status;
- failureReason = result;
- }
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_publishDiagnostics
-M['textDocument/publishDiagnostics'] = function(_, _, result)
- if not result then return end
- local uri = result.uri
- local bufnr = vim.uri_to_bufnr(uri)
- if not bufnr then
- err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri)
- return
- end
-
- -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic
- -- The diagnostic's severity. Can be omitted. If omitted it is up to the
- -- client to interpret diagnostics as error, warning, info or hint.
- -- TODO: Replace this with server-specific heuristics to infer severity.
- for _, diagnostic in ipairs(result.diagnostics) do
- if diagnostic.severity == nil then
- diagnostic.severity = protocol.DiagnosticSeverity.Error
- end
- end
-
- util.buf_clear_diagnostics(bufnr)
-
- -- Always save the diagnostics, even if the buf is not loaded.
- -- Language servers may report compile or build errors via diagnostics
- -- Users should be able to find these, even if they're in files which
- -- are not loaded.
- util.buf_diagnostics_save_positions(bufnr, result.diagnostics)
-
- -- Unloaded buffers should not handle diagnostics.
- -- When the buffer is loaded, we'll call on_attach, which sends textDocument/didOpen.
- -- This should trigger another publish of the diagnostics.
- --
- -- In particular, this stops a ton of spam when first starting a server for current
- -- unloaded buffers.
- if not api.nvim_buf_is_loaded(bufnr) then
- return
- end
- util.buf_diagnostics_underline(bufnr, result.diagnostics)
- util.buf_diagnostics_virtual_text(bufnr, result.diagnostics)
- util.buf_diagnostics_signs(bufnr, result.diagnostics)
- vim.api.nvim_command("doautocmd User LspDiagnosticsChanged")
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
-M['textDocument/references'] = function(_, _, result)
- if not result then return end
- util.set_qflist(util.locations_to_items(result))
- api.nvim_command("copen")
- api.nvim_command("wincmd p")
-end
-
---@private
---- Prints given list of symbols to the quickfix list.
---@param _ (not used)
---@param _ (not used)
---@param result (list of Symbols) LSP method name
---@param result (table) result of LSP method; a location or a list of locations.
----(`textDocument/definition` can return `Location` or `Location[]`
-local symbol_callback = function(_, _, result, _, bufnr)
- if not result or vim.tbl_isempty(result) then return end
-
- util.set_qflist(util.symbols_to_items(result, bufnr))
- api.nvim_command("copen")
- api.nvim_command("wincmd p")
-end
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
-M['textDocument/documentSymbol'] = symbol_callback
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol
-M['workspace/symbol'] = symbol_callback
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename
-M['textDocument/rename'] = function(_, _, result)
- if not result then return end
- util.apply_workspace_edit(result)
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting
-M['textDocument/rangeFormatting'] = function(_, _, result)
- if not result then return end
- util.apply_text_edits(result)
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
-M['textDocument/formatting'] = function(_, _, result)
- if not result then return end
- util.apply_text_edits(result)
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
-M['textDocument/completion'] = function(_, _, result)
- if vim.tbl_isempty(result or {}) then return end
- local row, col = unpack(api.nvim_win_get_cursor(0))
- local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1])
- local line_to_cursor = line:sub(col+1)
- local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
- local prefix = line_to_cursor:sub(textMatch+1)
-
- local matches = util.text_document_completion_list_to_complete_items(result, prefix)
- vim.fn.complete(textMatch+1, matches)
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
-M['textDocument/hover'] = function(_, method, result)
- util.focusable_float(method, function()
- if not (result and result.contents) then
- -- return { 'No information available' }
- return
- end
- local markdown_lines = util.convert_input_to_markdown_lines(result.contents)
- markdown_lines = util.trim_empty_lines(markdown_lines)
- if vim.tbl_isempty(markdown_lines) then
- -- return { 'No information available' }
- return
- end
- local bufnr, winnr = util.fancy_floating_markdown(markdown_lines, {
- pad_left = 1; pad_right = 1;
- })
- util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, winnr)
- return bufnr, winnr
- end)
-end
-
---@private
---- Jumps to a location. Used as a callback for multiple LSP methods.
---@param _ (not used)
---@param method (string) LSP method name
---@param result (table) result of LSP method; a location or a list of locations.
----(`textDocument/definition` can return `Location` or `Location[]`
-local function location_callback(_, method, result)
- if result == nil or vim.tbl_isempty(result) then
- local _ = log.info() and log.info(method, 'No location found')
- return nil
- end
-
- -- textDocument/definition can return Location or Location[]
- -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition
-
- if vim.tbl_islist(result) then
- util.jump_to_location(result[1])
-
- if #result > 1 then
- util.set_qflist(util.locations_to_items(result))
- api.nvim_command("copen")
- api.nvim_command("wincmd p")
- end
- else
- util.jump_to_location(result)
- end
-end
-
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration
-M['textDocument/declaration'] = location_callback
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition
-M['textDocument/definition'] = location_callback
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_typeDefinition
-M['textDocument/typeDefinition'] = location_callback
---@see https://microsoft.github.io/language-server-proto