aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp
diff options
context:
space:
mode:
authorTJ DeVries <devries.timothyj@gmail.com>2020-08-25 09:52:22 -0400
committerGitHub <noreply@github.com>2020-08-25 09:52:22 -0400
commit43202964f39eed4e28fd8ad6ad0ec79a1868c33f (patch)
treeda9abce08cca5b7502f0a5a6320eba52ee42b8e8 /runtime/lua/vim/lsp
parent8a333afec53a48c90de7b0bde30f9db2d02d1898 (diff)
parent9d9edebceb73657899256430ed3e2f49c92898a8 (diff)
downloadrneovim-43202964f39eed4e28fd8ad6ad0ec79a1868c33f.tar.gz
rneovim-43202964f39eed4e28fd8ad6ad0ec79a1868c33f.tar.bz2
rneovim-43202964f39eed4e28fd8ad6ad0ec79a1868c33f.zip
Merge pull request #12708 from runiq/lsp-doc
Add docs for some methods in vim.lsp
Diffstat (limited to 'runtime/lua/vim/lsp')
-rw-r--r--runtime/lua/vim/lsp/buf.lua98
-rw-r--r--runtime/lua/vim/lsp/callbacks.lua49
-rw-r--r--runtime/lua/vim/lsp/log.lua17
-rw-r--r--runtime/lua/vim/lsp/protocol.lua11
-rw-r--r--runtime/lua/vim/lsp/rpc.lua136
-rw-r--r--runtime/lua/vim/lsp/util.lua308
6 files changed, 520 insertions, 99 deletions
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 2e27617997..208082f241 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -7,14 +7,41 @@ local list_extend = vim.list_extend
local M = {}
+--@private
+--- Returns nil if {status} is false or nil, otherwise returns the rest of the
+--- arguments.
local function ok_or_nil(status, ...)
if not status then return end
return ...
end
+
+--@private
+--- Swallows errors.
+---
+--@param fn Function to run
+--@param ... Function arguments
+--@returns Result of `fn(...)` if there are no errors, otherwise nil.
+--- Returns nil if errors occur during {fn}, otherwise returns
local function npcall(fn, ...)
return ok_or_nil(pcall(fn, ...))
end
+--@private
+--- Sends an async request to all active clients attached to the current
+--- buffer.
+---
+--@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|.
+--
+--@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.
+---
+--@see |vim.lsp.buf_request()|
local function request(method, params, callback)
validate {
method = {method, 's'};
@@ -23,9 +50,10 @@ local function request(method, params, callback)
return vim.lsp.buf_request(0, method, params, callback)
end
---- Sends a notification through all clients associated with current buffer.
---
---@return `true` if server responds.
+--- Checks whether the language servers attached to the current buffer are
+--- ready.
+---
+--@returns `true` if server responds.
function M.server_ready()
return not not vim.lsp.buf_notify(0, "window/progress", {})
end
@@ -74,6 +102,12 @@ end
--- Retrieves the completion items at the current cursor position. Can only be
--- called in Insert mode.
+---
+--@param context (context support not yet implemented) Additional information
+--- about the context in which a completion was triggered (how it was triggered,
+--- and by which trigger character, if applicable)
+---
+--@see |vim.lsp.protocol.constants.CompletionTriggerKind|
function M.completion(context)
local params = util.make_position_params()
params.context = context
@@ -82,20 +116,27 @@ end
--- Formats the current buffer.
---
---- The optional {options} table can be used to specify FormattingOptions, a
---- list of which is available at
---- https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting.
+--@param options (optional, table) Can be used to specify FormattingOptions.
--- Some unspecified options will be automatically derived from the current
--- Neovim options.
+--
+--@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
function M.formatting(options)
local params = util.make_formatting_params(options)
return request('textDocument/formatting', params)
end
---- Perform |vim.lsp.buf.formatting()| synchronously.
+--- Performs |vim.lsp.buf.formatting()| synchronously.
---
--- Useful for running on save, to make sure buffer is formatted prior to being
---- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|.
+--- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|. Example:
+---
+--- <pre>
+--- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()]]
+--- </pre>
+---
+--@param options Table with valid `FormattingOptions` entries
+--@param timeout_ms (number) Request timeout
function M.formatting_sync(options, timeout_ms)
local params = util.make_formatting_params(options)
local result = vim.lsp.buf_request_sync(0, "textDocument/formatting", params, timeout_ms)
@@ -104,6 +145,13 @@ function M.formatting_sync(options, timeout_ms)
vim.lsp.util.apply_text_edits(result)
end
+--- Formats a given range.
+---
+--@param options Table with valid `FormattingOptions` entries.
+--@param start_pos ({number, number}, optional) mark-indexed position.
+---Defaults to the start of the last visual selection.
+--@param start_pos ({number, number}, optional) mark-indexed position.
+---Defaults to the end of the last visual selection.
function M.range_formatting(options, start_pos, end_pos)
validate {
options = {options, 't', true};
@@ -138,8 +186,10 @@ function M.range_formatting(options, start_pos, end_pos)
return request('textDocument/rangeFormatting', params)
end
---- Renames all references to the symbol under the cursor. If {new_name} is not
---- provided, the user will be prompted for a new name using |input()|.
+--- Renames all references to the symbol under the cursor.
+---
+--@param new_name (string) If not provided, the user will be prompted for a new
+---name using |input()|.
function M.rename(new_name)
-- TODO(ashkan) use prepareRename
-- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position.
@@ -152,6 +202,8 @@ end
--- Lists all the references to the symbol under the cursor in the quickfix window.
---
+--@param context (table) Context for the request
+--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
function M.references(context)
validate { context = { context, 't', true } }
local params = util.make_position_params()
@@ -169,6 +221,7 @@ function M.document_symbol()
request('textDocument/documentSymbol', params)
end
+--@private
local function pick_call_hierarchy_item(call_hierarchy_items)
if not call_hierarchy_items then return end
if #call_hierarchy_items == 1 then
@@ -186,6 +239,9 @@ local function pick_call_hierarchy_item(call_hierarchy_items)
return choice
end
+--- Lists all the call sites of the symbol under the cursor in the
+--- |quickfix| window. If the symbol can resolve to multiple
+--- items, the user can pick one in the |inputlist|.
function M.incoming_calls()
local params = util.make_position_params()
request('textDocument/prepareCallHierarchy', params, function(_, _, result)
@@ -194,6 +250,9 @@ function M.incoming_calls()
end)
end
+--- Lists all the items that are called by the symbol under the
+--- cursor in the |quickfix| window. If the symbol can resolve to
+--- multiple items, the user can pick one in the |inputlist|.
function M.outgoing_calls()
local params = util.make_position_params()
request('textDocument/prepareCallHierarchy', params, function(_, _, result)
@@ -204,9 +263,11 @@ end
--- Lists all symbols in the current workspace in the quickfix window.
---
---- The list is filtered against the optional argument {query};
---- if the argument is omitted from the call, the user is prompted to enter a string on the command line.
---- An empty string means no filtering is done.
+--- The list is filtered against {query}; if the argument is omitted from the
+--- call, the user is prompted to enter a string on the command line. An empty
+--- string means no filtering is done.
+---
+--@param query (string, optional)
function M.workspace_symbol(query)
query = query or npcall(vfn.input, "Query: ")
local params = {query = query}
@@ -227,10 +288,17 @@ function M.document_highlight()
request('textDocument/documentHighlight', params)
end
+--- Removes document highlights from current buffer.
+---
function M.clear_references()
util.buf_clear_references()
end
+--- Selects a code action from the input list that is available at the current
+--- cursor position.
+--
+--@param context: (table, optional) Valid `CodeActionContext` object
+--@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() }
@@ -239,6 +307,10 @@ function M.code_action(context)
request('textDocument/codeAction', params)
end
+--- Executes an LSP server command.
+---
+--@param command A valid `ExecuteCommandParams` object
+--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
function M.execute_command(command)
validate {
command = { command.command, 's' },
diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua
index 1ed58995d0..0ee03e6a2f 100644
--- a/runtime/lua/vim/lsp/callbacks.lua
+++ b/runtime/lua/vim/lsp/callbacks.lua
@@ -7,17 +7,24 @@ 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")
@@ -51,6 +58,7 @@ M['textDocument/codeAction'] = function(_, _, actions)
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?
@@ -64,6 +72,7 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit)
}
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
@@ -102,6 +111,7 @@ M['textDocument/publishDiagnostics'] = function(_, _, result)
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))
@@ -109,6 +119,13 @@ M['textDocument/references'] = function(_, _, result)
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
@@ -116,24 +133,30 @@ local symbol_callback = function(_, _, 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))
@@ -146,6 +169,7 @@ M['textDocument/completion'] = function(_, _, result)
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
@@ -166,6 +190,12 @@ M['textDocument/hover'] = function(_, method, result)
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')
@@ -188,11 +218,16 @@ local function location_callback(_, method, 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-protocol/specifications/specification-current/#textDocument_implementation
M['textDocument/implementation'] = location_callback
+--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
M['textDocument/signatureHelp'] = function(_, method, result)
util.focusable_preview(method, function()
if not (result and result.signatures and result.signatures[1]) then
@@ -208,15 +243,21 @@ M['textDocument/signatureHelp'] = function(_, method, result)
end)
end
+--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
M['textDocument/documentHighlight'] = function(_, _, result, _)
if not result then return end
local bufnr = api.nvim_get_current_buf()
util.buf_highlight_references(bufnr, result)
end
--- direction is "from" for incoming calls and "to" for outgoing calls
+--@private
+---
+--- Displays call hierarchy in the quickfix window.
+---
+--@param direction `"from"` for incoming calls and `"to"` for outgoing calls
+--@returns `CallHierarchyIncomingCall[]` if {direction} is `"from"`,
+--@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`,
local make_call_hierarchy_callback = function(direction)
- -- result is a CallHierarchy{Incoming,Outgoing}Call[]
return function(_, _, result)
if not result then return end
local items = {}
@@ -237,10 +278,13 @@ local make_call_hierarchy_callback = function(direction)
end
end
+--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/incomingCalls
M['callHierarchy/incomingCalls'] = make_call_hierarchy_callback('from')
+--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/outgoingCalls
M['callHierarchy/outgoingCalls'] = make_call_hierarchy_callback('to')
+--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/logMessage
M['window/logMessage'] = function(_, _, result, client_id)
local message_type = result.type
local message = result.message
@@ -261,6 +305,7 @@ M['window/logMessage'] = function(_, _, result, client_id)
return result
end
+--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/showMessage
M['window/showMessage'] = function(_, _, result, client_id)
local message_type = result.type
local message = result.message
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index 696ce43a59..587a65cd96 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -2,6 +2,9 @@
local log = {}
+-- FIXME: DOC
+-- Should be exposed in the vim docs.
+--
-- Log level dictionary with reverse lookup as well.
--
-- Can be used to lookup the number from the name or the name from the number.
@@ -21,12 +24,14 @@ local log_date_format = "%FT%H:%M:%S%z"
do
local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/"
+ --@private
local function path_join(...)
return table.concat(vim.tbl_flatten{...}, path_sep)
end
local logfilename = path_join(vim.fn.stdpath('data'), 'lsp.log')
- --- Return the log filename.
+ --- Returns the log filename.
+ --@returns (string) log filename
function log.get_filename()
return logfilename
end
@@ -36,6 +41,9 @@ do
for level, levelnr in pairs(log.levels) do
-- Also export the log level on the root object.
log[level] = levelnr
+ -- FIXME: DOC
+ -- Should be exposed in the vim docs.
+ --
-- Set the lowercase name as the main use function.
-- If called without arguments, it will check whether the log level is
-- greater than or equal to this one. When called with arguments, it will
@@ -74,6 +82,8 @@ end
-- interfere with iterating the levels
vim.tbl_add_reverse_lookup(log.levels)
+--- Sets the current log level.
+--@param level (string or number) One of `vim.lsp.log.levels`
function log.set_level(level)
if type(level) == 'string' then
current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level))
@@ -84,8 +94,9 @@ function log.set_level(level)
end
end
--- Return whether the level is sufficient for logging.
--- @param level number log level
+--- Checks whether the level is sufficient for logging.
+--@param level number log level
+--@returns (bool) true if would log, false if not
function log.should_log(level)
return level >= current_log_level
end
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index ef5e08680e..4e926381e0 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -2,6 +2,11 @@
local protocol = {}
+--@private
+--- Returns {a} if it is not nil, otherwise returns {b}.
+---
+--@param a
+--@param b
local function ifnil(a, b)
if a == nil then return b end
return a
@@ -9,12 +14,14 @@ end
--[=[
--- Useful for interfacing with:
--- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md
+--@private
+--- Useful for interfacing with:
+--- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md
function transform_schema_comments()
nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]]
nvim.command [[silent! '<,'>s/^\(\s*\) \* \=\(.*\)/\1--\2/]]
end
+--@private
function transform_schema_to_table()
transform_schema_comments()
nvim.command [[silent! '<,'>s/: \S\+//]]
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 81c92bfe05..680e1ba6ae 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -5,6 +5,11 @@ 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.fn.json_encode, data)
if status then
@@ -13,6 +18,11 @@ local function json_encode(data)
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.fn.json_decode, data)
if status then
@@ -22,17 +32,26 @@ local function json_decode(data)
end
end
+--@private
+--- Checks whether a given path exists and is a directory.
+--@param filename (string) path to check
+--@returns (bool)
local function is_dir(filename)
local stat = vim.loop.fs_stat(filename)
return stat and stat.type == 'directory' or false
end
local NIL = vim.NIL
+--@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
end
+--@private
--- Merges current process env with the given env and returns the result as
--- a list of "k=v" strings.
---
@@ -42,6 +61,8 @@ end
--- in: { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", }
--- out: { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", }
--- </pre>
+--@param env (table) table of environment variable assignments
+--@returns (table) list of `"k=v"` strings
local function env_merge(env)
if env == nil then
return env
@@ -56,6 +77,11 @@ local function env_merge(env)
return final_env
end
+--@private
+--- Embeds the given string into a table and correctly computes `Content-Length`.
+---
+--@param encoded_message (string)
+--@returns (table) table containing encoded message and `Content-Length` attribute
local function format_message_with_content_length(encoded_message)
return table.concat {
'Content-Length: '; tostring(#encoded_message); '\r\n\r\n';
@@ -63,8 +89,11 @@ local function format_message_with_content_length(encoded_message)
}
end
---- Parse an LSP Message's header
--- @param header: The header to parse.
+--@private
+--- Parses an LSP Message's header
+---
+--@param header: The header to parse.
+--@returns Parsed headers
local function parse_headers(header)
if type(header) ~= 'string' then
return nil
@@ -92,6 +121,8 @@ end
-- case insensitive pattern.
local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end)
+--@private
+--- The actual workhorse.
local function request_parser_loop()
local buffer = ''
while true do
@@ -138,6 +169,10 @@ local client_errors = vim.tbl_add_reverse_lookup {
SERVER_RESULT_CALLBACK_ERROR = 7;
}
+--- Constructs an error message from an LSP error object.
+---
+--@param err (table) The error object
+--@returns (string) The formatted error message
local function format_rpc_error(err)
validate {
err = { err, 't' };
@@ -182,23 +217,69 @@ local function rpc_response_error(code, message, data)
end
local default_handlers = {}
+--@private
+--- Default handler for notifications sent to an LSP server.
+---
+--@param method (string) The invoked LSP method
+--@param params (table): Parameters for the invoked LSP method
function default_handlers.notification(method, params)
local _ = log.debug() and log.debug('notification', method, params)
end
+--@private
+--- Default handler for requests sent to an LSP server.
+---
+--@param method (string) The invoked LSP method
+--@param params (table): Parameters for the invoked LSP method
+--@returns `nil` and `vim.lsp.protocol.ErrorCodes.MethodNotFound`.
function default_handlers.server_request(method, params)
local _ = log.debug() and log.debug('server_request', method, params)
return nil, rpc_response_error(protocol.ErrorCodes.MethodNotFound)
end
+--@private
+--- Default handler for when a client exits.
+---
+--@param code (number): Exit code
+--@param signal (number): Number describing the signal used to terminate (if
+---any)
function default_handlers.on_exit(code, signal)
- local _ = log.info() and log.info("client exit", { code = code, signal = signal })
+ local _ = log.info() and log.info("client_exit", { code = code, signal = signal })
end
+--@private
+--- Default handler for client errors.
+---
+--@param code (number): Error code
+--@param err (any): Details about the error
+---any)
function default_handlers.on_error(code, err)
local _ = log.error() and log.error('client_error:', client_errors[code], err)
end
---- Create and start an RPC client.
--- @param cmd [
-local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_params)
+--- Starts an LSP server process and create an LSP RPC client object to
+--- interact with it.
+---
+--@param cmd (string) Command to start the LSP server.
+--@param cmd_args (table) List of additional string arguments to pass to {cmd}.
+--@param handlers (table, optional) Handlers for LSP message types. Valid
+---handler names are:
+--- - `"notification"`
+--- - `"server_request"`
+--- - `"on_error"`
+--- - `"on_exit"`
+--@param extra_spawn_params (table, optional) Additional context for the LSP
+--- server process. May contain:
+--- - {cwd} (string) Working directory for the LSP server process
+--- - {env} (table) Additional environment variables for LSP server process
+--@returns Client RPC object.
+---
+--@returns Methods:
+--- - `notify()` |vim.lsp.rpc.notify()|
+--- - `request()` |vim.lsp.rpc.request()|
+---
+--@returns Members:
+--- - {pid} (number) The LSP server's PID.
+--- - {handle} A handle for low-level interaction with the LSP server process
+--- |vim.loop|.
+local function start(cmd, cmd_args, handlers, extra_spawn_params)
local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params})
validate {
cmd = { cmd, 's' };
@@ -242,6 +323,11 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
local handle, pid
do
+ --@private
+ --- Callback for |vim.loop.spawn()| Closes all streams and runs the
+ --- `on_exit` handler.
+ --@param code (number) Exit code
+ --@param signal (number) Signal that was used to terminate (if any)
local function onexit(code, signal)
stdin:close()
stdout:close()
@@ -265,6 +351,12 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
handle, pid = uv.spawn(cmd, spawn_params, onexit)
end
+ --@private
+ --- 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()|
+ --@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", payload)
if handle:is_closing() then return false end
@@ -276,7 +368,14 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
return true
end
- local function send_notification(method, params)
+ -- FIXME: DOC: Should be placed on the RPC client object returned by
+ -- `start()`
+ --
+ --- Sends a notification to the LSP server.
+ --@param method (string) The invoked LSP method
+ --@param params (table): Parameters for the invoked LSP method
+ --@returns (bool) `true` if notification could be sent, `false` if not
+ local function notify(method, params)
local _ = log.debug() and log.debug("rpc.notify", method, params)
return encode_and_send {
jsonrpc = "2.0";
@@ -285,6 +384,8 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
}
end
+ --@private
+ --- sends an error object to the remote LSP process.
local function send_response(request_id, err, result)
return encode_and_send {
id = request_id;
@@ -294,7 +395,16 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
}
end
- local function send_request(method, params, callback)
+ -- FIXME: DOC: Should be placed on the RPC client object returned by
+ -- `start()`
+ --
+ --- Sends a request to the LSP server and runs {callback} upon response.
+ ---
+ --@param method (string) The invoked LSP method
+ --@param params (table) Parameters for the invoked LSP method
+ --@param callback (function) Callback to invoke
+ --@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not
+ local function request(method, params, callback)
validate {
callback = { callback, 'f' };
}
@@ -320,11 +430,13 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
end
end)
+ --@private
local function on_error(errkind, ...)
assert(client_errors[errkind])
-- TODO what to do if this fails?
pcall(handlers.on_error, errkind, ...)
end
+ --@private
local function pcall_handler(errkind, status, head, ...)
if not status then
on_error(errkind, head, ...)
@@ -332,6 +444,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
end
return status, head, ...
end
+ --@private
local function try_call(errkind, fn, ...)
return pcall_handler(errkind, pcall(fn, ...))
end
@@ -340,6 +453,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
-- time and log them. This would require storing the timestamp. I could call
-- them with an error then, perhaps.
+ --@private
local function handle_body(body)
local decoded, err = json_decode(body)
if not decoded then
@@ -458,13 +572,13 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para
return {
pid = pid;
handle = handle;
- request = send_request;
- notify = send_notification;
+ request = request;
+ notify = notify
}
end
return {
- start = create_and_start_client;
+ start = start;
rpc_response_error = rpc_response_error;
format_rpc_error = format_rpc_error;
client_errors = client_errors;
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 33fca29ecd..3ec7311d65 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -7,6 +7,7 @@ local highlight = require 'vim.highlight'
local M = {}
+-- FIXME: DOC: Expose in vimdocs
--- Diagnostics received from the server via `textDocument/publishDiagnostics`
-- by buffer.
--
@@ -33,18 +34,30 @@ local M = {}
M.diagnostics_by_buf = {}
local split = vim.split
+--@private
local function split_lines(value)
return split(value, '\n', true)
end
+--@private
local function ok_or_nil(status, ...)
if not status then return end
return ...
end
+--@private
local function npcall(fn, ...)
return ok_or_nil(pcall(fn, ...))
end
+--- Replaces text in a range with new text.
+---
+--- CAUTION: Changes in-place!
+---
+--@param lines (table) Original list of strings
+--@param A (table) Start position; a 2-tuple of {line, col} numbers
+--@param B (table) End position; a 2-tuple of {line, col} numbers
+--@param new_lines A list of strings to replace the original
+--@returns (table) The modified {lines} object
function M.set_lines(lines, A, B, new_lines)
-- 0-indexing to 1-indexing
local i_0 = A[1] + 1
@@ -78,6 +91,7 @@ function M.set_lines(lines, A, B, new_lines)
return lines
end
+--@private
local function sort_by_key(fn)
return function(a,b)
local ka, kb = fn(a), fn(b)
@@ -91,13 +105,15 @@ local function sort_by_key(fn)
return false
end
end
+--@private
local edit_sort_key = sort_by_key(function(e)
return {e.A[1], e.A[2], e.i}
end)
+--@private
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
--- Returns a zero-indexed column, since set_lines() does the conversion to
--- 1-indexed
+--- Returns a zero-indexed column, since set_lines() does the conversion to
+--- 1-indexed
local function get_line_byte_from_position(bufnr, position)
-- LSP's line and characters are 0-indexed
-- Vim's line and columns are 1-indexed
@@ -114,6 +130,9 @@ local function get_line_byte_from_position(bufnr, position)
return col
end
+--- Applies a list of text edits to a buffer.
+--@param text_edits (table) list of `TextEdit` objects
+--@param buf_nr (number) Buffer id
function M.apply_text_edits(text_edits, bufnr)
if not next(text_edits) then return end
if not api.nvim_buf_is_loaded(bufnr) then
@@ -168,20 +187,30 @@ end
-- function M.glob_to_regex(glob)
-- end
--- textDocument/completion response returns one of CompletionItem[], CompletionList or null.
--- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion
+--- Can be used to extract the completion items from a
+--- `textDocument/completion` request, which may return one of
+--- `CompletionItem[]`, `CompletionList` or null.
+--@param result (table) The result of a `textDocument/completion` request
+--@returns (table) List of completion items
+--@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion
function M.extract_completion_items(result)
if type(result) == 'table' and result.items then
+ -- result is a `CompletionList`
return result.items
elseif result ~= nil then
+ -- result is `CompletionItem[]`
return result
else
+ -- result is `null`
return {}
end
end