aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
authorRom Grk <romgrk.cc@gmail.com>2020-11-03 03:11:08 -0500
committerRom Grk <romgrk.cc@gmail.com>2020-11-03 03:11:08 -0500
commit13e0ca3e194a438383b8451e19090355aa6c29dc (patch)
tree0ac33ef5ac829b7c74dd15f34b17540c3186c356 /runtime/lua/vim
parentc7c865214655f7d88fde85ed4947f07319c14182 (diff)
parent5b5848f2fb1f4b7995bb8a59d94b6766d2182070 (diff)
downloadrneovim-13e0ca3e194a438383b8451e19090355aa6c29dc.tar.gz
rneovim-13e0ca3e194a438383b8451e19090355aa6c29dc.tar.bz2
rneovim-13e0ca3e194a438383b8451e19090355aa6c29dc.zip
Merge branch 'master' into add-scroll-events
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/lsp.lua108
-rw-r--r--runtime/lua/vim/lsp/callbacks.lua28
-rw-r--r--runtime/lua/vim/lsp/protocol.lua27
-rw-r--r--runtime/lua/vim/lsp/rpc.lua19
-rw-r--r--runtime/lua/vim/lsp/util.lua17
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua2
-rw-r--r--runtime/lua/vim/treesitter/query.lua2
7 files changed, 134 insertions, 69 deletions
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 585528dd5a..1a0015e2db 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -25,6 +25,27 @@ local lsp = {
-- format_rpc_error = lsp_rpc.format_rpc_error;
}
+-- maps request name to the required resolved_capability in the client.
+lsp._request_name_to_capability = {
+ ['textDocument/hover'] = 'hover';
+ ['textDocument/signatureHelp'] = 'signature_help';
+ ['textDocument/definition'] = 'goto_definition';
+ ['textDocument/implementation'] = 'implementation';
+ ['textDocument/declaration'] = 'declaration';
+ ['textDocument/typeDefinition'] = 'type_definition';
+ ['textDocument/documentSymbol'] = 'document_symbol';
+ ['textDocument/workspaceSymbol'] = 'workspace_symbol';
+ ['textDocument/prepareCallHierarchy'] = 'call_hierarchy';
+ ['textDocument/rename'] = 'rename';
+ ['textDocument/codeAction'] = 'code_action';
+ ['workspace/executeCommand'] = 'execute_command';
+ ['textDocument/references'] = 'find_references';
+ ['textDocument/rangeFormatting'] = 'document_range_formatting';
+ ['textDocument/formatting'] = 'document_formatting';
+ ['textDocument/completion'] = 'completion';
+ ['textDocument/documentHighlight'] = 'document_highlight';
+}
+
-- TODO improve handling of scratch buffers with LSP attached.
--@private
@@ -51,6 +72,16 @@ local function resolve_bufnr(bufnr)
end
--@private
+--- callback 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)
+ local msg = string.format("method %s is not supported by any of the servers registered for the current buffer", method)
+ log.warn(msg)
+ return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg)
+end
+
+--@private
--- Checks whether a given path is a directory.
---
--@param filename (string) path to check
@@ -397,9 +428,8 @@ end
--@param trace: "off" | "messages" | "verbose" | nil passed directly to the language
--- server in the initialize request. Invalid/empty values will default to "off"
---
---@returns Client id. |vim.lsp.get_client_by_id()| Note: client is only
---- available after it has been initialized, which may happen after a small
---- delay (or never if there is an error). Use `on_init` to do any actions once
+--@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be
+--- fully initialized. Use `on_init` to do any actions once
--- the client has been initialized.
function lsp.start_client(config)
local cleaned_config = validate_client_config(config)
@@ -576,6 +606,15 @@ function lsp.start_client(config)
-- These are the cleaned up capabilities we use for dynamically deciding
-- when to send certain events to clients.
client.resolved_capabilities = protocol.resolve_capabilities(client.server_capabilities)
+ client.supports_method = function(method)
+ local required_capability = lsp._request_name_to_capability[method]
+ -- if we don't know about the method, assume that the client supports it.
+ if not required_capability then
+ return true
+ end
+
+ return client.resolved_capabilities[required_capability]
+ end
if config.on_init then
local status, err = pcall(config.on_init, client, result)
if not status then
@@ -599,19 +638,6 @@ function lsp.start_client(config)
end
--@private
- --- Throws error for a method that is not supported by the current LSP
- --- server.
- ---
- --@param method (string) an LSP method name not supported by the LSP server.
- --@returns (error) a 'MethodNotFound' JSON-RPC error response.
- local function unsupported_method(method)
- local msg = "server doesn't support "..method
- local _ = log.warn() and log.warn(msg)
- err_message(msg)
- return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg)
- end
-
- --@private
--- Sends a request to the server.
---
--- This is a thin wrapper around {client.rpc.request} with some additional
@@ -638,20 +664,6 @@ function lsp.start_client(config)
or error(string.format("not found: %q request callback for client %q.", method, client.name))
end
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback, bufnr)
- -- TODO keep these checks or just let it go anyway?
- if (not client.resolved_capabilities.hover and method == 'textDocument/hover')
- or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp')
- or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition')
- or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation')
- or (not client.resolved_capabilities.declaration and method == 'textDocument/declaration')
- or (not client.resolved_capabilities.type_definition and method == 'textDocument/typeDefinition')
- or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol')
- or (not client.resolved_capabilities.workspace_symbol and method == 'textDocument/workspaceSymbol')
- or (not client.resolved_capabilities.call_hierarchy and method == 'textDocument/prepareCallHierarchy')
- then
- callback(unsupported_method(method), method, nil, client_id, bufnr)
- return
- end
return rpc.request(method, params, function(err, result)
callback(err, method, result, client_id, bufnr)
end)
@@ -910,14 +922,14 @@ function lsp.buf_is_attached(bufnr, client_id)
return (all_buffer_active_clients[bufnr] or {})[client_id] == true
end
---- Gets an active client by id, or nil if the id is invalid or the
---- client is not yet initialized.
----
+--- Gets a client by id, or nil if the id is invalid.
+--- The returned client may not yet be fully initialized.
+--
--@param client_id client id number
---
--@returns |vim.lsp.client| object, or nil
function lsp.get_client_by_id(client_id)
- return active_clients[client_id]
+ return active_clients[client_id] or uninitialized_clients[client_id]
end
--- Stops a client(s).
@@ -998,16 +1010,32 @@ function lsp.buf_request(bufnr, method, params, callback)
callback = { callback, 'f', true };
}
local client_request_ids = {}
- for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr)
- local request_success, request_id = client.request(method, params, callback, 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.
- if request_success then
- client_request_ids[client_id] = request_id
+ local method_supported = false
+ 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)
+
+ -- 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.
+ if request_success then
+ client_request_ids[client_id] = request_id
+ end
end
end)
+ -- if no clients support the given method, call the callback 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)
+ end
+ return
+ end
+
local function _cancel_all_requests()
for client_id, request_id in pairs(client_request_ids) do
local client = active_clients[client_id]
diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua
index 4e7a8333a0..3270d1d2a9 100644
--- a/runtime/lua/vim/lsp/callbacks.lua
+++ b/runtime/lua/vim/lsp/callbacks.lua
@@ -82,18 +82,6 @@ M['textDocument/publishDiagnostics'] = function(_, _, result)
return
end
- -- 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_clear_diagnostics(bufnr)
-
-- 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.
@@ -104,7 +92,23 @@ M['textDocument/publishDiagnostics'] = function(_, _, result)
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)
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 4e926381e0..70862320c5 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -632,15 +632,18 @@ function protocol.make_client_capabilities()
codeActionLiteralSupport = {
codeActionKind = {
- valueSet = {};
+ valueSet = vim.tbl_values(protocol.CodeActionKind);
};
};
};
completion = {
dynamicRegistration = false;
completionItem = {
+ -- Until we can actually expand snippet, move cursor and allow for true snippet experience,
+ -- this should be disabled out of the box.
+ -- However, users can turn this back on if they have a snippet plugin.
+ snippetSupport = false;
- snippetSupport = true;
commitCharactersSupport = false;
preselectSupport = false;
deprecatedSupport = false;
@@ -703,6 +706,10 @@ function protocol.make_client_capabilities()
};
hierarchicalDocumentSymbolSupport = true;
};
+ rename = {
+ dynamicRegistration = false;
+ prepareSupport = true;
+ };
};
workspace = {
symbol = {
@@ -914,6 +921,7 @@ function protocol.resolve_capabilities(server_capabilities)
return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync))
end
end
+ general_properties.completion = server_capabilities.completionProvider ~= nil
general_properties.hover = server_capabilities.hoverProvider or false
general_properties.goto_definition = server_capabilities.definitionProvider or false
general_properties.find_references = server_capabilities.referencesProvider or false
@@ -923,14 +931,21 @@ function protocol.resolve_capabilities(server_capabilities)
general_properties.document_formatting = server_capabilities.documentFormattingProvider or false
general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false
general_properties.call_hierarchy = server_capabilities.callHierarchyProvider or false
+ general_properties.execute_command = server_capabilities.executeCommandProvider ~= nil
+
+ if server_capabilities.renameProvider == nil then
+ general_properties.rename = false
+ elseif type(server_capabilities.renameProvider) == 'boolean' then
+ general_properties.rename = server_capabilities.renameProvider
+ else
+ general_properties.rename = true
+ end
if server_capabilities.codeActionProvider == nil then
general_properties.code_action = false
- elseif type(server_capabilities.codeActionProvider) == 'boolean' then
+ elseif type(server_capabilities.codeActionProvider) == 'boolean'
+ or type(server_capabilities.codeActionProvider) == 'table' then
general_properties.code_action = server_capabilities.codeActionProvider
- elseif type(server_capabilities.codeActionProvider) == 'table' then
- -- TODO(ashkan) support CodeActionKind
- general_properties.code_action = false
else
error("The server sent invalid codeActionProvider")
end
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 749a51fecc..17c411f952 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -42,13 +42,28 @@ local function is_dir(filename)
end
local NIL = vim.NIL
+
+--@private
+local recursive_convert_NIL
+recursive_convert_NIL = function(v, tbl_processed)
+ if v == NIL then
+ return nil
+ elseif not tbl_processed[v] and type(v) == 'table' then
+ tbl_processed[v] = true
+ return vim.tbl_map(function(x)
+ return recursive_convert_NIL(x, tbl_processed)
+ end, v)
+ end
+
+ return v
+end
+
--@private
--- Returns its argument, but converts `vim.NIL` to Lua `nil`.
--@param v (any) Argument
--@returns (any)
local function convert_NIL(v)
- if v == NIL then return nil end
- return v
+ return recursive_convert_NIL(v, {})
end
--@private
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index b5f171a985..9ed19b938d 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -214,13 +214,16 @@ end
function M.apply_text_document_edit(text_document_edit)
local text_document = text_document_edit.textDocument
local bufnr = vim.uri_to_bufnr(text_document.uri)
- if text_document.version then
- -- `VersionedTextDocumentIdentifier`s version may be null https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
- if text_document.version ~= vim.NIL and M.buf_versions[bufnr] ~= nil and M.buf_versions[bufnr] > text_document.version then
- print("Buffer ", text_document.uri, " newer than edits.")
- return
- end
+
+ -- `VersionedTextDocumentIdentifier`s version may be null
+ -- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
+ if text_document.version
+ and M.buf_versions[bufnr]
+ and M.buf_versions[bufnr] > text_document.version then
+ print("Buffer ", text_document.uri, " newer than edits.")
+ return
end
+
M.apply_text_edits(text_document_edit.edits, bufnr)
end
@@ -532,7 +535,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help)
}
--]=]
-- TODO highlight parameter
- if parameter.documentation and parameter.documentation ~= vim.NIL then
+ if parameter.documentation then
M.convert_input_to_markdown_lines(parameter.documentation, contents)
end
end
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index decde08019..6714bb6354 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -157,7 +157,7 @@ local function on_line_impl(self, buf, line)
a.nvim_buf_set_extmark(buf, ns, start_row, start_col,
{ end_line = end_row, end_col = end_col,
hl_group = hl,
- ephemeral = true
+ ephemeral = true,
})
end
if start_row > line then
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 2903c5905c..cc7dc2656d 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -25,7 +25,7 @@ local function filter_files(file_list)
end
end
- return { main, unpack(after) }
+ return main and { main, unpack(after) } or after
end
local function runtime_query_path(lang, query_name)