mirror of
https://github.com/neovim/neovim.git
synced 2026-01-30 08:34:30 +00:00
Merge #15796 release: Nvim 0.5.1
This commit is contained in:
@@ -137,8 +137,8 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
|
||||
# version string, else they are combined with the result of `git describe`.
|
||||
set(NVIM_VERSION_MAJOR 0)
|
||||
set(NVIM_VERSION_MINOR 5)
|
||||
set(NVIM_VERSION_PATCH 0)
|
||||
set(NVIM_VERSION_PRERELEASE "") # for package maintainers
|
||||
set(NVIM_VERSION_PATCH 2)
|
||||
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
|
||||
|
||||
# API level
|
||||
set(NVIM_API_LEVEL 7) # Bump this after any API change.
|
||||
|
||||
@@ -202,23 +202,28 @@ responses and notifications from LSP servers.
|
||||
|
||||
For |lsp-request|, each |lsp-handler| has this signature: >
|
||||
|
||||
function(err, method, result, client_id, bufnr, config)
|
||||
function(err, result, ctx, config)
|
||||
<
|
||||
Parameters: ~
|
||||
{err} (table|nil)
|
||||
When the language server is unable to complete a
|
||||
request, a table with information about the error
|
||||
is sent. Otherwise, it is `nil`. See |lsp-response|.
|
||||
{method} (string)
|
||||
The |lsp-method| name.
|
||||
{result} (Result | Params | nil)
|
||||
When the language server is able to succesfully
|
||||
complete a request, this contains the `result` key
|
||||
of the response. See |lsp-response|.
|
||||
{client_id} (number)
|
||||
The ID of the |vim.lsp.client|.
|
||||
{bufnr} (Buffer)
|
||||
Buffer handle, or 0 for current.
|
||||
{ctx} (table)
|
||||
Context describes additional calling state
|
||||
associated with the handler. It consists of the
|
||||
following key, value pairs:
|
||||
|
||||
{method} (string)
|
||||
The |lsp-method| name.
|
||||
{client_id} (number)
|
||||
The ID of the |vim.lsp.client|.
|
||||
{bufnr} (Buffer)
|
||||
Buffer handle, or 0 for current.
|
||||
{config} (table)
|
||||
Configuration for the handler.
|
||||
|
||||
@@ -238,21 +243,24 @@ For |lsp-request|, each |lsp-handler| has this signature: >
|
||||
|
||||
For |lsp-notification|, each |lsp-handler| has this signature: >
|
||||
|
||||
function(err, method, params, client_id, bufnr, config)
|
||||
function(err, result, ctx, config)
|
||||
<
|
||||
Parameters: ~
|
||||
{err} (nil)
|
||||
This is always `nil`.
|
||||
See |lsp-notification|
|
||||
{method} (string)
|
||||
The |lsp-method| name.
|
||||
{params} (Params)
|
||||
{result} (Result)
|
||||
This contains the `params` key of the notification.
|
||||
See |lsp-notification|
|
||||
{client_id} (number)
|
||||
The ID of the |vim.lsp.client|
|
||||
{bufnr} (nil)
|
||||
`nil`, as the server doesn't have an associated buffer.
|
||||
{ctx} (table)
|
||||
Context describes additional calling state
|
||||
associated with the handler. It consists of the
|
||||
following key, value pairs:
|
||||
|
||||
{method} (string)
|
||||
The |lsp-method| name.
|
||||
{client_id} (number)
|
||||
The ID of the |vim.lsp.client|.
|
||||
{config} (table)
|
||||
Configuration for the handler.
|
||||
|
||||
@@ -1355,7 +1363,7 @@ goto_prev({opts}) *vim.lsp.diagnostic.goto_prev()*
|
||||
{opts} table See |vim.lsp.diagnostic.goto_next()|
|
||||
|
||||
*vim.lsp.diagnostic.on_publish_diagnostics()*
|
||||
on_publish_diagnostics({_}, {_}, {params}, {client_id}, {_}, {config})
|
||||
on_publish_diagnostics({_}, {result}, {ctx}, {config})
|
||||
|lsp-handler| for the method "textDocument/publishDiagnostics"
|
||||
|
||||
Note:
|
||||
@@ -1581,7 +1589,7 @@ get({bufnr}) *vim.lsp.codelens.get()*
|
||||
table ( `CodeLens[]` )
|
||||
|
||||
*vim.lsp.codelens.on_codelens()*
|
||||
on_codelens({err}, {_}, {result}, {client_id}, {bufnr})
|
||||
on_codelens({err}, {result}, {ctx}, {_})
|
||||
|lsp-handler| for the method `textDocument/codeLens`
|
||||
|
||||
refresh() *vim.lsp.codelens.refresh()*
|
||||
@@ -1614,17 +1622,25 @@ progress_handler({_}, {_}, {params}, {client_id})
|
||||
See also: ~
|
||||
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
||||
|
||||
*vim.lsp.handlers.signature_help()*
|
||||
signature_help({_}, {method}, {result}, {_}, {bufnr}, {config})
|
||||
Parameters: ~
|
||||
{config} table Configuration table.
|
||||
• border: (default=nil)
|
||||
• Add borders to the floating window
|
||||
• See |vim.api.nvim_open_win()|
|
||||
hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
|
||||
|lsp-handler| for the method "textDocument/hover" >
|
||||
|
||||
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
|
||||
vim.lsp.handlers.hover, {
|
||||
-- Use a sharp border with `FloatBorder` highlights
|
||||
border = "single"
|
||||
}
|
||||
)
|
||||
<
|
||||
See also: ~
|
||||
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration@seehttps://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition@seehttps://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_typeDefinition@seehttps://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_implementation|lsp-handler| for the method "textDocument/signatureHelp">
|
||||
|
||||
*vim.lsp.handlers.signature_help()*
|
||||
signature_help({_}, {result}, {ctx}, {config})
|
||||
|lsp-handler| for the method "textDocument/signatureHelp". The
|
||||
active parameter is highlighted with
|
||||
|hl-LspSignatureActiveParameter|. >
|
||||
|
||||
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
|
||||
vim.lsp.handlers.signature_help, {
|
||||
-- Use a sharp border with `FloatBorder` highlights
|
||||
|
||||
@@ -1474,14 +1474,12 @@ validate({opt}) *vim.validate()*
|
||||
|
||||
vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
|
||||
=> NOP (success)
|
||||
<
|
||||
>
|
||||
vim.validate{arg1={1, 'table'}}
|
||||
=> error('arg1: expected table, got number')
|
||||
<
|
||||
>
|
||||
vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
|
||||
=> error('arg1: expected even number, got 3')
|
||||
|
||||
vim.validate{arg1={1, 'table'}}
|
||||
=> error('arg1: expected table, got number')
|
||||
|
||||
vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
|
||||
=> error('arg1: expected even number, got 3')
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
|
||||
@@ -474,11 +474,9 @@ Query:iter_matches({self}, {node}, {source}, {start}, {stop})
|
||||
for id, node in pairs(match) do
|
||||
local name = query.captures[id]
|
||||
-- `node` was captured by the `name` capture in the match
|
||||
<
|
||||
>
|
||||
local node_data = metadata[id] -- Node level metadata
|
||||
<
|
||||
>
|
||||
|
||||
local node_data = metadata[id] -- Node level metadata
|
||||
|
||||
... use the info here ...
|
||||
end
|
||||
end
|
||||
|
||||
@@ -218,7 +218,7 @@ local function validate_client_config(config)
|
||||
config = { config, 't' };
|
||||
}
|
||||
validate {
|
||||
root_dir = { config.root_dir, is_dir, "directory" };
|
||||
root_dir = { config.root_dir, optional_validator(is_dir), "directory" };
|
||||
handlers = { config.handlers, "t", true };
|
||||
capabilities = { config.capabilities, "t", true };
|
||||
cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" };
|
||||
@@ -380,7 +380,7 @@ do
|
||||
end
|
||||
state.pending_change = function()
|
||||
state.pending_change = nil
|
||||
if client.is_stopped() then
|
||||
if client.is_stopped() or not vim.api.nvim_buf_is_valid(bufnr) then
|
||||
return
|
||||
end
|
||||
local contentChanges
|
||||
@@ -455,12 +455,15 @@ local function text_document_did_open_handler(bufnr, client)
|
||||
vim.schedule(function()
|
||||
vim.lsp.handlers["textDocument/publishDiagnostics"](
|
||||
nil,
|
||||
"textDocument/publishDiagnostics",
|
||||
{
|
||||
diagnostics = vim.lsp.diagnostic.get(bufnr, client.id),
|
||||
uri = vim.uri_from_bufnr(bufnr),
|
||||
},
|
||||
client.id
|
||||
{
|
||||
method="textDocument/publishDiagnostics",
|
||||
client_id=client.id,
|
||||
bufnr=bufnr,
|
||||
}
|
||||
)
|
||||
end)
|
||||
end
|
||||
@@ -555,7 +558,7 @@ end
|
||||
---
|
||||
--- The following parameters describe fields in the {config} table.
|
||||
---
|
||||
--@param root_dir: (required, string) Directory where the LSP server will base
|
||||
--@param root_dir: (string) Directory where the LSP server will base
|
||||
--- its rootUri on initialization.
|
||||
---
|
||||
--@param cmd: (required, string or list treated like |jobstart()|) Base command
|
||||
@@ -590,6 +593,10 @@ end
|
||||
--- as `initializationOptions`. See `initialize` in the LSP spec.
|
||||
---
|
||||
--@param name (string, default=client-id) Name in log messages.
|
||||
--
|
||||
--@param workspace_folders (table) List of workspace folders passed to the
|
||||
--- language server. Defaults to root_dir if not set. See `workspaceFolders` in
|
||||
--- the LSP spec
|
||||
---
|
||||
--@param get_language_id function(bufnr, filetype) -> language ID as string.
|
||||
--- Defaults to the filetype.
|
||||
@@ -677,11 +684,11 @@ function lsp.start_client(config)
|
||||
--@param method (string) LSP method name
|
||||
--@param params (table) The parameters for that method.
|
||||
function dispatch.notification(method, params)
|
||||
local _ = log.debug() and log.debug('notification', method, params)
|
||||
local _ = log.trace() and log.trace('notification', method, params)
|
||||
local handler = resolve_handler(method)
|
||||
if handler then
|
||||
-- Method name is provided here for convenience.
|
||||
handler(nil, method, params, client_id)
|
||||
handler(nil, params, {method=method, client_id=client_id})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -691,13 +698,13 @@ function lsp.start_client(config)
|
||||
--@param method (string) LSP method name
|
||||
--@param params (table) The parameters for that method
|
||||
function dispatch.server_request(method, params)
|
||||
local _ = log.debug() and log.debug('server_request', method, params)
|
||||
local _ = log.trace() and log.trace('server_request', method, params)
|
||||
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)
|
||||
local _ = log.trace() and log.trace("server_request: found handler for", method)
|
||||
return handler(nil, params, {method=method, client_id=client_id})
|
||||
end
|
||||
local _ = log.debug() and log.debug("server_request: no handler found for", method)
|
||||
local _ = log.warn() and log.warn("server_request: no handler found for", method)
|
||||
return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound)
|
||||
end
|
||||
|
||||
@@ -775,6 +782,14 @@ function lsp.start_client(config)
|
||||
off = 'off'; messages = 'messages'; verbose = 'verbose';
|
||||
}
|
||||
local version = vim.version()
|
||||
|
||||
if config.root_dir and not config.workspace_folders then
|
||||
config.workspace_folders = {{
|
||||
uri = vim.uri_from_fname(config.root_dir);
|
||||
name = string.format("%s", config.root_dir);
|
||||
}};
|
||||
end
|
||||
|
||||
local initialize_params = {
|
||||
-- The process Id of the parent process that started the server. Is null if
|
||||
-- the process has not been started by another process. If the parent
|
||||
@@ -793,7 +808,7 @@ function lsp.start_client(config)
|
||||
rootPath = config.root_dir;
|
||||
-- The rootUri of the workspace. Is null if no folder is open. If both
|
||||
-- `rootPath` and `rootUri` are set `rootUri` wins.
|
||||
rootUri = vim.uri_from_fname(config.root_dir);
|
||||
rootUri = config.root_dir and vim.uri_from_fname(config.root_dir);
|
||||
-- User provided initialization options.
|
||||
initializationOptions = config.init_options;
|
||||
-- The capabilities provided by the client (editor or tool)
|
||||
@@ -815,16 +830,13 @@ function lsp.start_client(config)
|
||||
-- -- workspace folder in the user interface.
|
||||
-- name
|
||||
-- }
|
||||
workspaceFolders = {{
|
||||
uri = vim.uri_from_fname(config.root_dir);
|
||||
name = string.format("%s", config.root_dir);
|
||||
}};
|
||||
workspaceFolders = config.workspace_folders,
|
||||
}
|
||||
if config.before_init then
|
||||
-- TODO(ashkan) handle errors here.
|
||||
pcall(config.before_init, initialize_params, config)
|
||||
end
|
||||
local _ = log.debug() and log.debug(log_prefix, "initialize_params", initialize_params)
|
||||
local _ = log.trace() and log.trace(log_prefix, "initialize_params", initialize_params)
|
||||
rpc.request('initialize', initialize_params, function(init_err, result)
|
||||
assert(not init_err, tostring(init_err))
|
||||
assert(result, "server sent empty result")
|
||||
@@ -894,7 +906,7 @@ function lsp.start_client(config)
|
||||
|
||||
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr)
|
||||
return rpc.request(method, params, function(err, result)
|
||||
handler(err, method, result, client_id, bufnr)
|
||||
handler(err, result, {method=method, client_id=client_id, bufnr=bufnr})
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -915,7 +927,7 @@ function lsp.start_client(config)
|
||||
--@see |vim.lsp.buf_request_sync()|
|
||||
function client.request_sync(method, params, timeout_ms, bufnr)
|
||||
local request_result = nil
|
||||
local function _sync_handler(err, _, result)
|
||||
local function _sync_handler(err, result)
|
||||
request_result = { err = err, result = result }
|
||||
end
|
||||
|
||||
@@ -1274,7 +1286,7 @@ function lsp.buf_request(bufnr, method, params, handler)
|
||||
local unsupported_err = lsp._unsupported_method(method)
|
||||
handler = handler or lsp.handlers[method]
|
||||
if handler then
|
||||
handler(unsupported_err, method, bufnr)
|
||||
handler(unsupported_err, nil, {method=method, bufnr=bufnr})
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -1314,8 +1326,8 @@ function lsp.buf_request_all(bufnr, method, params, callback)
|
||||
end
|
||||
end)
|
||||
|
||||
local function _sync_handler(err, _, result, client_id)
|
||||
request_results[client_id] = { error = err, result = result }
|
||||
local function _sync_handler(err, result, ctx)
|
||||
request_results[ctx.client_id] = { error = err, result = result }
|
||||
result_count = result_count + 1
|
||||
set_expected_result_count()
|
||||
|
||||
@@ -1421,7 +1433,7 @@ function lsp.omnifunc(findstart, base)
|
||||
local params = util.make_position_params()
|
||||
|
||||
local items = {}
|
||||
lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, _, result)
|
||||
lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result)
|
||||
if err or not result or vim.fn.mode() ~= "i" then return end
|
||||
local matches = util.text_document_completion_list_to_complete_items(result, prefix)
|
||||
-- TODO(ashkan): is this the best way to do this?
|
||||
@@ -1496,8 +1508,8 @@ end
|
||||
--@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))
|
||||
return function(err, result, ctx, config)
|
||||
return handler(err, result, ctx, vim.tbl_deep_extend("force", config or {}, override_config))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ local function select_client(method)
|
||||
|
||||
if #clients > 1 then
|
||||
local choices = {}
|
||||
for k,v in ipairs(clients) do
|
||||
for k,v in pairs(clients) do
|
||||
table.insert(choices, string.format("%d %s", k, v.name))
|
||||
end
|
||||
local user_choice = vim.fn.confirm(
|
||||
@@ -204,9 +204,9 @@ function M.formatting_seq_sync(options, timeout_ms, order)
|
||||
local clients = vim.tbl_values(vim.lsp.buf_get_clients());
|
||||
|
||||
-- sort the clients according to `order`
|
||||
for _, client_name in ipairs(order or {}) do
|
||||
for _, client_name in pairs(order or {}) do
|
||||
-- if the client exists, move to the end of the list
|
||||
for i, client in ipairs(clients) do
|
||||
for i, client in pairs(clients) do
|
||||
if client.name == client_name then
|
||||
table.insert(clients, table.remove(clients, i))
|
||||
break
|
||||
@@ -215,7 +215,7 @@ function M.formatting_seq_sync(options, timeout_ms, order)
|
||||
end
|
||||
|
||||
-- loop through the clients and make synchronous formatting requests
|
||||
for _, client in ipairs(clients) do
|
||||
for _, client in pairs(clients) do
|
||||
if client.resolved_capabilities.document_formatting then
|
||||
local params = util.make_formatting_params(options)
|
||||
local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf())
|
||||
@@ -286,7 +286,7 @@ local function pick_call_hierarchy_item(call_hierarchy_items)
|
||||
return call_hierarchy_items[1]
|
||||
end
|
||||
local items = {}
|
||||
for i, item in ipairs(call_hierarchy_items) do
|
||||
for i, item in pairs(call_hierarchy_items) do
|
||||
local entry = item.detail or item.name
|
||||
table.insert(items, string.format("%d. %s", i, entry))
|
||||
end
|
||||
@@ -300,13 +300,21 @@ end
|
||||
--@private
|
||||
local function call_hierarchy(method)
|
||||
local params = util.make_position_params()
|
||||
request('textDocument/prepareCallHierarchy', params, function(err, _, result)
|
||||
request('textDocument/prepareCallHierarchy', params, function(err, result, ctx)
|
||||
if err then
|
||||
vim.notify(err.message, vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
local call_hierarchy_item = pick_call_hierarchy_item(result)
|
||||
vim.lsp.buf_request(0, method, { item = call_hierarchy_item })
|
||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||
if client then
|
||||
client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
|
||||
else
|
||||
vim.notify(string.format(
|
||||
'Client with id=%d disappeared during call hierarchy request', ctx.client_id),
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -328,8 +336,8 @@ end
|
||||
---
|
||||
function M.list_workspace_folders()
|
||||
local workspace_folders = {}
|
||||
for _, client in ipairs(vim.lsp.buf_get_clients()) do
|
||||
for _, folder in ipairs(client.workspaceFolders) do
|
||||
for _, client in pairs(vim.lsp.buf_get_clients()) do
|
||||
for _, folder in pairs(client.workspaceFolders) do
|
||||
table.insert(workspace_folders, folder.name)
|
||||
end
|
||||
end
|
||||
@@ -347,9 +355,9 @@ function M.add_workspace_folder(workspace_folder)
|
||||
return
|
||||
end
|
||||
local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}})
|
||||
for _, client in ipairs(vim.lsp.buf_get_clients()) do
|
||||
for _, client in pairs(vim.lsp.buf_get_clients()) do
|
||||
local found = false
|
||||
for _, folder in ipairs(client.workspaceFolders) do
|
||||
for _, folder in pairs(client.workspaceFolders) do
|
||||
if folder.name == workspace_folder then
|
||||
found = true
|
||||
print(workspace_folder, "is already part of this workspace")
|
||||
@@ -371,8 +379,8 @@ function M.remove_workspace_folder(workspace_folder)
|
||||
vim.api.nvim_command("redraw")
|
||||
if not (workspace_folder and #workspace_folder > 0) then return end
|
||||
local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}})
|
||||
for _, client in ipairs(vim.lsp.buf_get_clients()) do
|
||||
for idx, folder in ipairs(client.workspaceFolders) do
|
||||
for _, client in pairs(vim.lsp.buf_get_clients()) do
|
||||
for idx, folder in pairs(client.workspaceFolders) do
|
||||
if folder.name == workspace_folder then
|
||||
vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params)
|
||||
client.workspaceFolders[idx] = nil
|
||||
@@ -422,6 +430,21 @@ function M.clear_references()
|
||||
util.buf_clear_references()
|
||||
end
|
||||
|
||||
--- Requests code actions from all clients and calls the handler exactly once
|
||||
--- with all aggregated results
|
||||
---@private
|
||||
local function code_action_request(params)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local method = 'textDocument/codeAction'
|
||||
vim.lsp.buf_request_all(bufnr, method, params, function(results)
|
||||
local actions = {}
|
||||
for _, r in pairs(results) do
|
||||
vim.list_extend(actions, r.result or {})
|
||||
end
|
||||
vim.lsp.handlers[method](nil, actions, {bufnr=bufnr, method=method})
|
||||
end)
|
||||
end
|
||||
|
||||
--- Selects a code action from the input list that is available at the current
|
||||
--- cursor position.
|
||||
--
|
||||
|
||||
@@ -169,7 +169,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
|
||||
if lens.command then
|
||||
countdown()
|
||||
else
|
||||
client.request('codeLens/resolve', lens, function(_, _, result)
|
||||
client.request('codeLens/resolve', lens, function(_, result)
|
||||
if result and result.command then
|
||||
lens.command = result.command
|
||||
-- Eager display to have some sort of incremental feedback
|
||||
@@ -192,17 +192,17 @@ end
|
||||
|
||||
--- |lsp-handler| for the method `textDocument/codeLens`
|
||||
---
|
||||
function M.on_codelens(err, _, result, client_id, bufnr)
|
||||
function M.on_codelens(err, result, ctx, _)
|
||||
assert(not err, vim.inspect(err))
|
||||
|
||||
M.save(result, bufnr, client_id)
|
||||
M.save(result, ctx.bufnr, ctx.client_id)
|
||||
|
||||
-- Eager display for any resolved (and unresolved) lenses and refresh them
|
||||
-- once resolved.
|
||||
M.display(result, bufnr, client_id)
|
||||
resolve_lenses(result, bufnr, client_id, function()
|
||||
M.display(result, bufnr, client_id)
|
||||
active_refreshes[bufnr] = nil
|
||||
M.display(result, ctx.bufnr, ctx.client_id)
|
||||
resolve_lenses(result, ctx.bufnr, ctx.client_id, function()
|
||||
M.display(result, ctx.bufnr, ctx.client_id)
|
||||
active_refreshes[ctx.bufnr] = nil
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -1008,15 +1008,16 @@ end
|
||||
--- - Update diagnostics in InsertMode or wait until InsertLeave
|
||||
--- - severity_sort: (default=false)
|
||||
--- - Sort diagnostics (and thus signs and virtual text)
|
||||
function M.on_publish_diagnostics(_, _, params, client_id, _, config)
|
||||
local uri = params.uri
|
||||
function M.on_publish_diagnostics(_, result, ctx, config)
|
||||
local client_id = ctx.client_id
|
||||
local uri = result.uri
|
||||
local bufnr = vim.uri_to_bufnr(uri)
|
||||
|
||||
if not bufnr then
|
||||
return
|
||||
end
|
||||
|
||||
local diagnostics = params.diagnostics
|
||||
local diagnostics = result.diagnostics
|
||||
|
||||
if config and if_nil(config.severity_sort, false) then
|
||||
table.sort(diagnostics, function(a, b) return a.severity > b.severity end)
|
||||
@@ -1164,8 +1165,41 @@ function M.display(diagnostics, bufnr, client_id, config)
|
||||
save_extmarks(bufnr, client_id)
|
||||
end
|
||||
|
||||
-- }}}
|
||||
-- Diagnostic User Functions {{{
|
||||
--- Redraw diagnostics for the given buffer and client
|
||||
---
|
||||
--- This calls the "textDocument/publishDiagnostics" handler manually using
|
||||
--- the cached diagnostics already received from the server. This can be useful
|
||||
--- for redrawing diagnostics after making changes in diagnostics
|
||||
--- configuration. |lsp-handler-configuration|
|
||||
---
|
||||
---@param bufnr (optional, number): Buffer handle, defaults to current
|
||||
---@param client_id (optional, number): Redraw diagnostics for the given
|
||||
--- client. The default is to redraw diagnostics for all attached
|
||||
--- clients.
|
||||
function M.redraw(bufnr, client_id)
|
||||
bufnr = get_bufnr(bufnr)
|
||||
if not client_id then
|
||||
return vim.lsp.for_each_buffer_client(bufnr, function(client)
|
||||
M.redraw(bufnr, client.id)
|
||||
end)
|
||||
end
|
||||
|
||||
-- We need to invoke the publishDiagnostics handler directly instead of just
|
||||
-- calling M.display so that we can preserve any custom configuration options
|
||||
-- the user may have set with vim.lsp.with.
|
||||
vim.lsp.handlers["textDocument/publishDiagnostics"](
|
||||
nil,
|
||||
{
|
||||
uri = vim.uri_from_bufnr(bufnr),
|
||||
diagnostics = M.get(bufnr, client_id),
|
||||
},
|
||||
{
|
||||
method = "textDocument/publishDiagnostics",
|
||||
client_id = client_id,
|
||||
bufnr = bufnr,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
--- Open a floating window with the diagnostics from {line_nr}
|
||||
---
|
||||
|
||||
@@ -17,21 +17,21 @@ local function err_message(...)
|
||||
api.nvim_command("redraw")
|
||||
end
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
||||
M['workspace/executeCommand'] = function()
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
|
||||
M['workspace/executeCommand'] = function(_, _, _, _)
|
||||
-- Error handling is done implicitly by wrapping all handlers; see end of this file
|
||||
end
|
||||
|
||||
-- @msg of type ProgressParams
|
||||
-- Basically a token of type number/string
|
||||
local function progress_handler(_, _, params, client_id)
|
||||
---@private
|
||||
local function progress_handler(_, result, ctx, _)
|
||||
local client_id = ctx.client_id
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
||||
if not client then
|
||||
err_message("LSP[", client_name, "] client has shut down after sending the message")
|
||||
end
|
||||
local val = params.value -- unspecified yet
|
||||
local token = params.token -- string or number
|
||||
local val = result.value -- unspecified yet
|
||||
local token = result.token -- string or number
|
||||
|
||||
|
||||
if val.kind then
|
||||
@@ -62,10 +62,11 @@ end
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
|
||||
M['$/progress'] = progress_handler
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create
|
||||
M['window/workDoneProgress/create'] = function(_, _, params, client_id)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create
|
||||
M['window/workDoneProgress/create'] = function(_, result, ctx)
|
||||
local client_id = ctx.client_id
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
local token = params.token -- string or number
|
||||
local token = result.token -- string or number
|
||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
||||
if not client then
|
||||
err_message("LSP[", client_name, "] client has shut down after sending the message")
|
||||
@@ -74,12 +75,12 @@ M['window/workDoneProgress/create'] = function(_, _, params, client_id)
|
||||
return vim.NIL
|
||||
end
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
|
||||
M['window/showMessageRequest'] = function(_, _, params)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
|
||||
M['window/showMessageRequest'] = function(_, result)
|
||||
|
||||
local actions = params.actions
|
||||
print(params.message)
|
||||
local option_strings = {params.message, "\nRequest Actions:"}
|
||||
local actions = result.actions
|
||||
print(result.message)
|
||||
local option_strings = {result.message, "\nRequest Actions:"}
|
||||
for i, action in ipairs(actions) do
|
||||
local title = action.title:gsub('\r\n', '\\r\\n')
|
||||
title = title:gsub('\n', '\\n')
|
||||
@@ -95,8 +96,9 @@ M['window/showMessageRequest'] = function(_, _, params)
|
||||
end
|
||||
end
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
|
||||
M['client/registerCapability'] = function(_, _, _, client_id)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
|
||||
M['client/registerCapability'] = function(_, _, ctx)
|
||||
local client_id = ctx.client_id
|
||||
local warning_tpl = "The language server %s triggers a registerCapability "..
|
||||
"handler despite dynamicRegistration set to false. "..
|
||||
"Report upstream, this warning is harmless"
|
||||
@@ -107,25 +109,25 @@ M['client/registerCapability'] = function(_, _, _, client_id)
|
||||
return vim.NIL
|
||||
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
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
|
||||
M['textDocument/codeAction'] = function(_, result)
|
||||
if result == nil or vim.tbl_isempty(result) then
|
||||
print("No code actions available")
|
||||
return
|
||||
end
|
||||
|
||||
local option_strings = {"Code Actions:"}
|
||||
for i, action in ipairs(actions) do
|
||||
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 > #actions then
|
||||
if choice < 1 or choice > #result then
|
||||
return
|
||||
end
|
||||
local action_chosen = actions[choice]
|
||||
local action_chosen = result[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
|
||||
@@ -141,8 +143,8 @@ 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)
|
||||
--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
|
||||
@@ -155,29 +157,30 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit)
|
||||
}
|
||||
end
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration
|
||||
M['workspace/configuration'] = function(_, _, params, client_id)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration
|
||||
M['workspace/configuration'] = function(_, result, ctx)
|
||||
local client_id = ctx.client_id
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
if not client then
|
||||
err_message("LSP[id=", client_id, "] client has shut down after sending the message")
|
||||
return
|
||||
end
|
||||
if not params.items then
|
||||
if not result.items then
|
||||
return {}
|
||||
end
|
||||
|
||||
local result = {}
|
||||
for _, item in ipairs(params.items) do
|
||||
local response = {}
|
||||
for _, item in ipairs(result.items) do
|
||||
if item.section then
|
||||
local value = util.lookup_section(client.config.settings, item.section) or vim.NIL
|
||||
-- For empty sections with no explicit '' key, return settings as is
|
||||
if value == vim.NIL and item.section == '' then
|
||||
value = client.config.settings or vim.NIL
|
||||
end
|
||||
table.insert(result, value)
|
||||
table.insert(response, value)
|
||||
end
|
||||
end
|
||||
return result
|
||||
return response
|
||||
end
|
||||
|
||||
M['textDocument/publishDiagnostics'] = function(...)
|
||||
@@ -190,52 +193,63 @@ end
|
||||
|
||||
|
||||
|
||||
--@private
|
||||
--- Return a function that converts LSP responses to quickfix items and opens the qflist
|
||||
--
|
||||
--@param map_result function `((resp, bufnr) -> list)` to convert the response
|
||||
--@param entity name of the resource used in a `not found` error message
|
||||
local function response_to_qflist(map_result, entity)
|
||||
return function(_, _, result, _, bufnr)
|
||||
---@private
|
||||
--- Return a function that converts LSP responses to list items and opens the list
|
||||
---
|
||||
--- The returned function has an optional {config} parameter that accepts a table
|
||||
--- with the following keys:
|
||||
---
|
||||
--- loclist: (boolean) use the location list (default is to use the quickfix list)
|
||||
---
|
||||
---@param map_result function `((resp, bufnr) -> list)` to convert the response
|
||||
---@param entity name of the resource used in a `not found` error message
|
||||
local function response_to_list(map_result, entity)
|
||||
return function(_,result, ctx, config)
|
||||
if not result or vim.tbl_isempty(result) then
|
||||
vim.notify('No ' .. entity .. ' found')
|
||||
else
|
||||
util.set_qflist(map_result(result, bufnr))
|
||||
api.nvim_command("copen")
|
||||
config = config or {}
|
||||
if config.loclist then
|
||||
util.set_loclist(map_result(result, ctx.bufnr))
|
||||
api.nvim_command("lopen")
|
||||
else
|
||||
util.set_qflist(map_result(result, ctx.bufnr))
|
||||
api.nvim_command("copen")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
|
||||
M['textDocument/references'] = response_to_qflist(util.locations_to_items, 'references')
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
|
||||
M['textDocument/references'] = response_to_list(util.locations_to_items, 'references')
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
|
||||
M['textDocument/documentSymbol'] = response_to_qflist(util.symbols_to_items, 'document symbols')
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
|
||||
M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols')
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol
|
||||
M['workspace/symbol'] = response_to_qflist(util.symbols_to_items, 'symbols')
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol
|
||||
M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols')
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename
|
||||
M['textDocument/rename'] = function(_, _, result)
|
||||
--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)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting
|
||||
M['textDocument/rangeFormatting'] = function(_, result, ctx, _)
|
||||
if not result then return end
|
||||
util.apply_text_edits(result)
|
||||
util.apply_text_edits(result, ctx.bufnr)
|
||||
end
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
|
||||
M['textDocument/formatting'] = function(_, _, result)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
|
||||
M['textDocument/formatting'] = function(_, result, ctx, _)
|
||||
if not result then return end
|
||||
util.apply_text_edits(result)
|
||||
util.apply_text_edits(result, ctx.bufnr)
|
||||
end
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
|
||||
M['textDocument/completion'] = function(_, _, result)
|
||||
--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])
|
||||
@@ -260,9 +274,9 @@ end
|
||||
--- - border: (default=nil)
|
||||
--- - Add borders to the floating window
|
||||
--- - See |vim.api.nvim_open_win()|
|
||||
function M.hover(_, method, result, _, _, config)
|
||||
function M.hover(_, result, ctx, config)
|
||||
config = config or {}
|
||||
config.focus_id = method
|
||||
config.focus_id = ctx.method
|
||||
if not (result and result.contents) then
|
||||
-- return { 'No information available' }
|
||||
return
|
||||
@@ -281,13 +295,13 @@ M['textDocument/hover'] = M.hover
|
||||
|
||||
--@private
|
||||
--- Jumps to a location. Used as a handler 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.
|
||||
---@param _ (not used)
|
||||
---@param result (table) result of LSP method; a location or a list of locations.
|
||||
---@param ctx (table) table containing the context of the request, including the method
|
||||
---(`textDocument/definition` can return `Location` or `Location[]`
|
||||
local function location_handler(_, method, result)
|
||||
local function location_handler(_, result, ctx, _)
|
||||
if result == nil or vim.tbl_isempty(result) then
|
||||
local _ = log.info() and log.info(method, 'No location found')
|
||||
local _ = log.info() and log.info(ctx.method, 'No location found')
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -328,17 +342,19 @@ M['textDocument/implementation'] = location_handler
|
||||
--- - border: (default=nil)
|
||||
--- - Add borders to the floating window
|
||||
--- - See |vim.api.nvim_open_win()|
|
||||
function M.signature_help(_, method, result, _, bufnr, config)
|
||||
function M.signature_help(_, result, ctx, config)
|
||||
config = config or {}
|
||||
config.focus_id = method
|
||||
config.focus_id = ctx.method
|
||||
-- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler
|
||||
-- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore
|
||||
if not (result and result.signatures and result.signatures[1]) then
|
||||
print('No signature help available')
|
||||
return
|
||||
end
|
||||
local ft = api.nvim_buf_get_option(bufnr, 'filetype')
|
||||
local lines = util.convert_signature_help_to_markdown_lines(result, ft)
|
||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||
local triggers = client.resolved_capabilities.signature_help_trigger_characters
|
||||
local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype')
|
||||
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
|
||||
lines = util.trim_empty_lines(lines)
|
||||
if vim.tbl_isempty(lines) then
|
||||
print('No signature help available')
|
||||
@@ -350,10 +366,10 @@ end
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
|
||||
M['textDocument/signatureHelp'] = M.signature_help
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
|
||||
M['textDocument/documentHighlight'] = function(_, _, result, _, bufnr, _)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
|
||||
M['textDocument/documentHighlight'] = function(_, result, ctx, _)
|
||||
if not result then return end
|
||||
util.buf_highlight_references(bufnr, result)
|
||||
util.buf_highlight_references(ctx.bufnr, result)
|
||||
end
|
||||
|
||||
--@private
|
||||
@@ -364,7 +380,7 @@ end
|
||||
--@returns `CallHierarchyIncomingCall[]` if {direction} is `"from"`,
|
||||
--@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`,
|
||||
local make_call_hierarchy_handler = function(direction)
|
||||
return function(_, _, result)
|
||||
return function(_, result)
|
||||
if not result then return end
|
||||
local items = {}
|
||||
for _, call_hierarchy_call in pairs(result) do
|
||||
@@ -389,10 +405,11 @@ M['callHierarchy/incomingCalls'] = make_call_hierarchy_handler('from')
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/outgoingCalls
|
||||
M['callHierarchy/outgoingCalls'] = make_call_hierarchy_handler('to')
|
||||
|
||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/logMessage
|
||||
M['window/logMessage'] = function(_, _, result, client_id)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage
|
||||
M['window/logMessage'] = function(_, result, ctx, _)
|
||||
local message_type = result.type
|
||||
local message = result.message
|
||||
local client_id = ctx.client_id
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
||||
if not client then
|
||||
@@ -402,7 +419,7 @@ M['window/logMessage'] = function(_, _, result, client_id)
|
||||
log.error(message)
|
||||
elseif message_type == protocol.MessageType.Warning then
|
||||
log.warn(message)
|
||||
elseif message_type == protocol.MessageType.Info then
|
||||
elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then
|
||||
log.info(message)
|
||||
else
|
||||
log.debug(message)
|
||||
@@ -410,10 +427,11 @@ 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)
|
||||
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessage
|
||||
M['window/showMessage'] = function(_, result, ctx, _)
|
||||
local message_type = result.type
|
||||
local message = result.message
|
||||
local client_id = ctx.client_id
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
local client_name = client and client.name or string.format("id=%d", client_id)
|
||||
if not client then
|
||||
@@ -430,12 +448,14 @@ end
|
||||
|
||||
-- Add boilerplate error validation and logging for all of these.
|
||||
for k, fn in pairs(M) do
|
||||
M[k] = function(err, method, params, client_id, bufnr, config)
|
||||
local _ = log.debug() and log.debug('default_handler', method, {
|
||||
params = params, client_id = client_id, err = err, bufnr = bufnr, config = config
|
||||
M[k] = function(err, result, ctx, config)
|
||||
local _ = log.trace() and log.trace('default_handler', ctx.method, {
|
||||
err = err, result = result, ctx=vim.inspect(ctx), config = config
|
||||
})
|
||||
|
||||
if err then
|
||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
||||
local client_name = client and client.name or string.format("client_id=%d", ctx.client_id)
|
||||
-- LSP spec:
|
||||
-- interface ResponseError:
|
||||
-- code: integer;
|
||||
@@ -444,7 +464,7 @@ for k, fn in pairs(M) do
|
||||
return err_message(tostring(err.code) .. ': ' .. err.message)
|
||||
end
|
||||
|
||||
return fn(err, method, params, client_id, bufnr, config)
|
||||
return fn(err, result, ctx, config)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ log.levels = vim.deepcopy(vim.log.levels)
|
||||
|
||||
-- Default log level is warn.
|
||||
local current_log_level = log.levels.WARN
|
||||
local log_date_format = "%FT%H:%M:%S%z"
|
||||
local log_date_format = "%F %H:%M:%S"
|
||||
local format_func = function(arg) return vim.inspect(arg, {newline=''}) end
|
||||
|
||||
do
|
||||
local path_sep = vim.loop.os_uname().version:match("Windows") and "\\" or "/"
|
||||
@@ -33,7 +34,7 @@ do
|
||||
vim.fn.mkdir(vim.fn.stdpath('cache'), "p")
|
||||
local logfile = assert(io.open(logfilename, "a+"))
|
||||
-- Start message for logging
|
||||
logfile:write(string.format("[ START ] %s ] LSP logging initiated\n", os.date(log_date_format)))
|
||||
logfile:write(string.format("[START][%s] LSP logging initiated\n", os.date(log_date_format)))
|
||||
for level, levelnr in pairs(log.levels) do
|
||||
-- Also export the log level on the root object.
|
||||
log[level] = levelnr
|
||||
@@ -56,14 +57,14 @@ do
|
||||
if levelnr < current_log_level then return false end
|
||||
if argc == 0 then return true end
|
||||
local info = debug.getinfo(2, "Sl")
|
||||
local fileinfo = string.format("%s:%s", info.short_src, info.currentline)
|
||||
local parts = { table.concat({"[", level, "]", os.date(log_date_format), "]", fileinfo, "]"}, " ") }
|
||||
local header = string.format("[%s][%s] ...%s:%s", level, os.date(log_date_format), string.sub(info.short_src, #info.short_src - 15), info.currentline)
|
||||
local parts = { header }
|
||||
for i = 1, argc do
|
||||
local arg = select(i, ...)
|
||||
if arg == nil then
|
||||
table.insert(parts, "nil")
|
||||
else
|
||||
table.insert(parts, vim.inspect(arg, {newline=''}))
|
||||
table.insert(parts, format_func(arg))
|
||||
end
|
||||
end
|
||||
logfile:write(table.concat(parts, '\t'), "\n")
|
||||
@@ -88,6 +89,18 @@ function log.set_level(level)
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the current log level.
|
||||
function log.get_level()
|
||||
return current_log_level
|
||||
end
|
||||
|
||||
--- Sets formatting function used to format logs
|
||||
---@param handle function function to apply to logging arguments, pass vim.inspect for multi-line formatting
|
||||
function log.set_format_func(handle)
|
||||
assert(handle == vim.inspect or type(handle) == 'function', "handle must be a function")
|
||||
format_func = handle
|
||||
end
|
||||
|
||||
--- Checks whether the level is sufficient for logging.
|
||||
--@param level number log level
|
||||
--@returns (bool) true if would log, false if not
|
||||
|
||||
@@ -392,7 +392,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
||||
--@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)
|
||||
local _ = log.debug() and log.debug("rpc.send", payload)
|
||||
if handle == nil or handle:is_closing() then return false end
|
||||
-- TODO(ashkan) remove this once we have a Lua json_encode
|
||||
schedule(function()
|
||||
@@ -493,7 +493,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
|
||||
-- on_error(client_errors.INVALID_SERVER_JSON, err)
|
||||
return
|
||||
end
|
||||
local _ = log.debug() and log.debug("decoded", decoded)
|
||||
local _ = log.debug() and log.debug("rpc.receive", decoded)
|
||||
|
||||
if type(decoded.method) == 'string' and decoded.id then
|
||||
-- Server Request
|
||||
|
||||
@@ -148,10 +148,6 @@ 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
|
||||
@@ -176,6 +172,7 @@ local function get_line_byte_from_position(bufnr, position)
|
||||
if ok then
|
||||
return result
|
||||
end
|
||||
return math.min(#lines[1], col)
|
||||
end
|
||||
end
|
||||
return col
|
||||
@@ -239,53 +236,119 @@ function M.get_progress_messages()
|
||||
end
|
||||
|
||||
--- Applies a list of text edits to a buffer.
|
||||
--@param text_edits (table) list of `TextEdit` objects
|
||||
--@param buf_nr (number) Buffer id
|
||||
---@param text_edits table list of `TextEdit` objects
|
||||
---@param bufnr number Buffer id
|
||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
|
||||
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
|
||||
vim.fn.bufload(bufnr)
|
||||
end
|
||||
api.nvim_buf_set_option(bufnr, 'buflisted', true)
|
||||
local start_line, finish_line = math.huge, -1
|
||||
local cleaned = {}
|
||||
for i, e in ipairs(text_edits) do
|
||||
-- adjust start and end column for UTF-16 encoding of non-ASCII characters
|
||||
local start_row = e.range.start.line
|
||||
local start_col = get_line_byte_from_position(bufnr, e.range.start)
|
||||
local end_row = e.range["end"].line
|
||||
local end_col = get_line_byte_from_position(bufnr, e.range['end'])
|
||||
start_line = math.min(e.range.start.line, start_line)
|
||||
finish_line = math.max(e.range["end"].line, finish_line)
|
||||
-- TODO(ashkan) sanity check ranges for overlap.
|
||||
table.insert(cleaned, {
|
||||
i = i;
|
||||
A = {start_row; start_col};
|
||||
B = {end_row; end_col};
|
||||
lines = vim.split(e.newText, '\n', true);
|
||||
|
||||
-- Fix reversed range and indexing each text_edits
|
||||
local index = 0
|
||||
text_edits = vim.tbl_map(function(text_edit)
|
||||
index = index + 1
|
||||
text_edit._index = index
|
||||
|
||||
if text_edit.range.start.line > text_edit.range['end'].line or text_edit.range.start.line == text_edit.range['end'].line and text_edit.range.start.character > text_edit.range['end'].character then
|
||||
local start = text_edit.range.start
|
||||
text_edit.range.start = text_edit.range['end']
|
||||
text_edit.range['end'] = start
|
||||
end
|
||||
return text_edit
|
||||
end, text_edits)
|
||||
|
||||
-- Sort text_edits
|
||||
table.sort(text_edits, function(a, b)
|
||||
if a.range.start.line ~= b.range.start.line then
|
||||
return a.range.start.line > b.range.start.line
|
||||
end
|
||||
if a.range.start.character ~= b.range.start.character then
|
||||
return a.range.start.character > b.range.start.character
|
||||
end
|
||||
if a._index ~= b._index then
|
||||
return a._index > b._index
|
||||
end
|
||||
end)
|
||||
|
||||
-- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here.
|
||||
local has_eol_text_edit = false
|
||||
local max = vim.api.nvim_buf_line_count(bufnr)
|
||||
local len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '')
|
||||
text_edits = vim.tbl_map(function(text_edit)
|
||||
if max <= text_edit.range.start.line then
|
||||
text_edit.range.start.line = max - 1
|
||||
text_edit.range.start.character = len
|
||||
text_edit.newText = '\n' .. text_edit.newText
|
||||
has_eol_text_edit = true
|
||||
end
|
||||
if max <= text_edit.range['end'].line then
|
||||
text_edit.range['end'].line = max - 1
|
||||
text_edit.range['end'].character = len
|
||||
has_eol_text_edit = true
|
||||
end
|
||||
return text_edit
|
||||
end, text_edits)
|
||||
|
||||
-- Some LSP servers are depending on the VSCode behavior.
|
||||
-- The VSCode will re-locate the cursor position after applying TextEdit so we also do it.
|
||||
local is_current_buf = vim.api.nvim_get_current_buf() == bufnr
|
||||
local cursor = (function()
|
||||
if not is_current_buf then
|
||||
return {
|
||||
row = -1,
|
||||
col = -1,
|
||||
}
|
||||
end
|
||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||
return {
|
||||
row = cursor[1] - 1,
|
||||
col = cursor[2],
|
||||
}
|
||||
end)()
|
||||
|
||||
-- Apply text edits.
|
||||
local is_cursor_fixed = false
|
||||
for _, text_edit in ipairs(text_edits) do
|
||||
local e = {
|
||||
start_row = text_edit.range.start.line,
|
||||
start_col = get_line_byte_from_position(bufnr, text_edit.range.start),
|
||||
end_row = text_edit.range['end'].line,
|
||||
end_col = get_line_byte_from_position(bufnr, text_edit.range['end']),
|
||||
text = vim.split(text_edit.newText, '\n', true),
|
||||
}
|
||||
vim.api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text)
|
||||
|
||||
local row_count = (e.end_row - e.start_row) + 1
|
||||
if e.end_row < cursor.row then
|
||||
cursor.row = cursor.row + (#e.text - row_count)
|
||||
is_cursor_fixed = true
|
||||
elseif e.end_row == cursor.row and e.end_col <= cursor.col then
|
||||
cursor.row = cursor.row + (#e.text - row_count)
|
||||
cursor.col = #e.text[#e.text] + (cursor.col - e.end_col)
|
||||
if #e.text == 1 then
|
||||
cursor.col = cursor.col + e.start_col
|
||||
end
|
||||
is_cursor_fixed = true
|
||||
end
|
||||
end
|
||||
|
||||
if is_cursor_fixed then
|
||||
vim.api.nvim_win_set_cursor(0, {
|
||||
cursor.row + 1,
|
||||
math.min(cursor.col, #(vim.api.nvim_buf_get_lines(bufnr, cursor.row, cursor.row + 1, false)[1] or ''))
|
||||
})
|
||||
end
|
||||
|
||||
-- Reverse sort the orders so we can apply them without interfering with
|
||||
-- eachother. Also add i as a sort key to mimic a stable sort.
|
||||
table.sort(cleaned, edit_sort_key)
|
||||
local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false)
|
||||
local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol')
|
||||
local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) <= finish_line + 1
|
||||
if set_eol and (#lines == 0 or #lines[#lines] ~= 0) then
|
||||
table.insert(lines, '')
|
||||
-- Remove final line if needed
|
||||
local fix_eol = has_eol_text_edit
|
||||
fix_eol = fix_eol and api.nvim_buf_get_option(bufnr, 'fixeol')
|
||||
fix_eol = fix_eol and (vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '') == ''
|
||||
if fix_eol then
|
||||
vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {})
|
||||
end
|
||||
|
||||
for i = #cleaned, 1, -1 do
|
||||
local e = cleaned[i]
|
||||
local A = {e.A[1] - start_line, e.A[2]}
|
||||
local B = {e.B[1] - start_line, e.B[2]}
|
||||
lines = M.set_lines(lines, A, B, e.lines)
|
||||
end
|
||||
if set_eol and #lines[#lines] == 0 then
|
||||
table.remove(lines)
|
||||
end
|
||||
api.nvim_buf_set_lines(bufnr, start_line, finish_line + 1, false, lines)
|
||||
end
|
||||
|
||||
-- local valid_windows_path_characters = "[^<>:\"/\\|?*]"
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release date="2021-09-26" version="0.5.1"/>
|
||||
<release date="2021-07-02" version="0.5.0"/>
|
||||
<release date="2020-08-04" version="0.4.4"/>
|
||||
<release date="2019-11-06" version="0.4.3"/>
|
||||
|
||||
@@ -80,8 +80,8 @@ _do_release_commit() {
|
||||
_do_bump_commit() {
|
||||
$__sed -i.bk 's/(NVIM_VERSION_PRERELEASE) ""/\1 "-dev"/' CMakeLists.txt
|
||||
$__sed -i.bk 's/set\((NVIM_VERSION_PATCH) [[:digit:]]/set(\1 ?/' CMakeLists.txt
|
||||
rm CMakeLists.txt.bk
|
||||
rm runtime/nvim.appdata.xml.bk
|
||||
rm -f CMakeLists.txt.bk
|
||||
rm -f runtime/nvim.appdata.xml.bk
|
||||
nvim +'/NVIM_VERSION' +1new +'exe "norm! iUpdate version numbers!!!"' \
|
||||
-O CMakeLists.txt
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('vim.lsp.codelens', function()
|
||||
command = { title = 'Lens1', command = 'Dummy' }
|
||||
},
|
||||
}
|
||||
vim.lsp.codelens.on_codelens(nil, 'textDocument/codeLens', lenses, 1, bufnr)
|
||||
vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr})
|
||||
]], bufnr)
|
||||
|
||||
local stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr)
|
||||
|
||||
@@ -205,8 +205,8 @@ describe('vim.lsp.diagnostic', function()
|
||||
make_warning("Warning 1", 2, 1, 2, 5),
|
||||
}
|
||||
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_1_diags }, 1)
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_2_diags }, 2)
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=1})
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_2_diags }, {client_id=2})
|
||||
return {
|
||||
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1),
|
||||
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2),
|
||||
@@ -258,8 +258,8 @@ describe('vim.lsp.diagnostic', function()
|
||||
make_warning("Warning 1", 2, 1, 2, 5),
|
||||
}
|
||||
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_1_diags }, 1)
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_2_diags }, 2)
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=1})
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_2_diags }, {client_id=2})
|
||||
return {
|
||||
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1),
|
||||
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2),
|
||||
@@ -435,14 +435,14 @@ describe('vim.lsp.diagnostic', function()
|
||||
|
||||
it('should return all diagnostics when no severity is supplied', function()
|
||||
eq(2, exec_lua [[
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, {
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error("Error 1", 1, 1, 1, 5),
|
||||
make_warning("Warning on Server 1", 1, 1, 2, 5),
|
||||
make_error("Error On Other Line", 2, 1, 1, 5),
|
||||
}
|
||||
}, 1)
|
||||
}, {client_id=1})
|
||||
|
||||
return #vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1)
|
||||
]])
|
||||
@@ -450,7 +450,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
|
||||
it('should return only requested diagnostics when severity_limit is supplied', function()
|
||||
eq(2, exec_lua [[
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, {
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error("Error 1", 1, 1, 1, 5),
|
||||
@@ -458,7 +458,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
make_information("Ignored information", 1, 1, 2, 5),
|
||||
make_error("Error On Other Line", 2, 1, 1, 5),
|
||||
}
|
||||
}, 1)
|
||||
}, {client_id=1})
|
||||
|
||||
return #vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1, { severity_limit = "Warning" })
|
||||
]])
|
||||
@@ -470,12 +470,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
exec_lua [[
|
||||
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
|
||||
virtual_text = function() return true end,
|
||||
})(nil, nil, {
|
||||
})(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
]]
|
||||
|
||||
@@ -487,12 +487,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
exec_lua [[
|
||||
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
|
||||
virtual_text = function() return false end,
|
||||
})(nil, nil, {
|
||||
})(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
]]
|
||||
|
||||
@@ -509,12 +509,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
exec_lua [[
|
||||
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
|
||||
update_in_insert = false,
|
||||
})(nil, nil, {
|
||||
})(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
]]
|
||||
|
||||
@@ -551,12 +551,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
return SetVirtualTextOriginal(...)
|
||||
end
|
||||
|
||||
PublishDiagnostics(nil, nil, {
|
||||
PublishDiagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
]]
|
||||
|
||||
@@ -605,12 +605,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
return SetVirtualTextOriginal(...)
|
||||
end
|
||||
|
||||
PublishDiagnostics(nil, nil, {
|
||||
PublishDiagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
]]
|
||||
|
||||
@@ -647,12 +647,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
exec_lua [[
|
||||
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
|
||||
update_in_insert = true,
|
||||
})(nil, nil, {
|
||||
})(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
]]
|
||||
|
||||
@@ -677,12 +677,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
},
|
||||
})
|
||||
|
||||
PublishDiagnostics(nil, nil, {
|
||||
PublishDiagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
|
||||
return vim.api.nvim_buf_get_extmarks(
|
||||
@@ -714,12 +714,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
end,
|
||||
})
|
||||
|
||||
PublishDiagnostics(nil, nil, {
|
||||
PublishDiagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
|
||||
return vim.api.nvim_buf_get_extmarks(
|
||||
@@ -747,12 +747,12 @@ describe('vim.lsp.diagnostic', function()
|
||||
},
|
||||
})
|
||||
|
||||
PublishDiagnostics(nil, nil, {
|
||||
PublishDiagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_warning('Delayed Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
|
||||
return count_of_extmarks_for_client(diagnostic_bufnr, 1)
|
||||
@@ -838,10 +838,10 @@ describe('vim.lsp.diagnostic', function()
|
||||
}
|
||||
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, {
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = diagnostics
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
|
||||
vim.lsp.diagnostic.set_signs(diagnostics, diagnostic_bufnr, 1)
|
||||
@@ -863,13 +863,13 @@ describe('vim.lsp.diagnostic', function()
|
||||
local loc_list = exec_lua [[
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, {
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Farther Diagnostic', 4, 4, 4, 4),
|
||||
make_error('Lower Diagnostic', 1, 1, 1, 1),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
|
||||
vim.lsp.diagnostic.set_loclist()
|
||||
@@ -884,20 +884,20 @@ describe('vim.lsp.diagnostic', function()
|
||||
local loc_list = exec_lua [[
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, {
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_error('Lower Diagnostic', 1, 1, 1, 1),
|
||||
}
|
||||
}, 1
|
||||
}, {client_id=1}
|
||||
)
|
||||
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, {
|
||||
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
|
||||
uri = fake_uri,
|
||||
diagnostics = {
|
||||
make_warning('Farther Diagnostic', 4, 4, 4, 4),
|
||||
}
|
||||
}, 2
|
||||
}, {client_id=2}
|
||||
)
|
||||
|
||||
vim.lsp.diagnostic.set_loclist()
|
||||
|
||||
@@ -130,9 +130,12 @@ local function test_rpc_server(config)
|
||||
end
|
||||
|
||||
describe('LSP', function()
|
||||
before_each(function()
|
||||
clear_notrace()
|
||||
end)
|
||||
|
||||
describe('server_name specified', function()
|
||||
before_each(function()
|
||||
clear_notrace()
|
||||
-- Run an instance of nvim on the file which contains our "scripts".
|
||||
-- Pass TEST_NAME to pick the script.
|
||||
local test_name = "basic_init"
|
||||
@@ -216,7 +219,7 @@ describe('LSP', function()
|
||||
|
||||
it('should run correctly', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "test", {}, 1};
|
||||
{NIL, {}, {method="test", client_id=1}};
|
||||
}
|
||||
test_rpc_server {
|
||||
test_name = "basic_init";
|
||||
@@ -241,7 +244,7 @@ describe('LSP', function()
|
||||
|
||||
it('should fail', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "test", {}, 1};
|
||||
{NIL, {}, {method="test", client_id=1}};
|
||||
}
|
||||
test_rpc_server {
|
||||
test_name = "basic_init";
|
||||
@@ -269,8 +272,8 @@ describe('LSP', function()
|
||||
return
|
||||
end
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1, NIL};
|
||||
{NIL, "test", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="test", client_id=1}};
|
||||
}
|
||||
test_rpc_server {
|
||||
test_name = "basic_init";
|
||||
@@ -292,12 +295,12 @@ describe('LSP', function()
|
||||
|
||||
it('client should return settings via workspace/configuration handler', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "workspace/configuration", { items = {
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, { items = {
|
||||
{ section = "testSetting1" };
|
||||
{ section = "testSetting2" };
|
||||
}}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
}}, { method="workspace/configuration", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -309,9 +312,9 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result, ctx)
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'start' then
|
||||
exec_lua([=[
|
||||
local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID)
|
||||
client.config.settings = {
|
||||
@@ -319,35 +322,34 @@ describe('LSP', function()
|
||||
testSetting2 = false;
|
||||
}]=])
|
||||
end
|
||||
if method == 'workspace/configuration' then
|
||||
local result = exec_lua([=[
|
||||
if ctx.method == 'workspace/configuration' then
|
||||
local server_result = exec_lua([=[
|
||||
local method, params = ...
|
||||
return require'vim.lsp.handlers'['workspace/configuration'](err, method, params, TEST_RPC_CLIENT_ID)]=], method, params)
|
||||
client.notify('workspace/configuration', result)
|
||||
return require'vim.lsp.handlers'['workspace/configuration'](err, params, {method=method, client_id=TEST_RPC_CLIENT_ID})]=], ctx.method, result)
|
||||
client.notify('workspace/configuration', server_result)
|
||||
end
|
||||
if method == 'shutdown' then
|
||||
if ctx.method == 'shutdown' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
}
|
||||
end)
|
||||
it('workspace/configuration returns NIL per section if client was started without config.settings', function()
|
||||
clear_notrace()
|
||||
fake_lsp_server_setup('workspace/configuration no settings')
|
||||
eq({ NIL, NIL, }, exec_lua [[
|
||||
local params = {
|
||||
local result = {
|
||||
items = {
|
||||
{section = 'foo'},
|
||||
{section = 'bar'},
|
||||
}
|
||||
}
|
||||
return vim.lsp.handlers['workspace/configuration'](nil, nil, params, TEST_RPC_CLIENT_ID)
|
||||
return vim.lsp.handlers['workspace/configuration'](nil, result, {client_id=TEST_RPC_CLIENT_ID})
|
||||
]])
|
||||
end)
|
||||
|
||||
it('should verify capabilities sent', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
}
|
||||
test_rpc_server {
|
||||
test_name = "basic_check_capabilities";
|
||||
@@ -371,7 +373,7 @@ describe('LSP', function()
|
||||
|
||||
it('client.supports_methods() should validate capabilities', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
}
|
||||
test_rpc_server {
|
||||
test_name = "capabilities_for_client_supports_method";
|
||||
@@ -405,7 +407,7 @@ describe('LSP', function()
|
||||
|
||||
it('should call unsupported_method when trying to call an unsupported method', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
}
|
||||
test_rpc_server {
|
||||
test_name = "capabilities_for_client_supports_method";
|
||||
@@ -413,7 +415,8 @@ describe('LSP', function()
|
||||
exec_lua([=[
|
||||
BUFFER = vim.api.nvim_get_current_buf()
|
||||
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
|
||||
vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method)
|
||||
vim.lsp.handlers['textDocument/typeDefinition'] = function(err, result, ctx)
|
||||
local method = ctx.method
|
||||
vim.lsp._last_lsp_handler = { err = err; method = method }
|
||||
end
|
||||
vim.lsp._unsupported_method = function(method)
|
||||
@@ -446,7 +449,7 @@ describe('LSP', function()
|
||||
|
||||
it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
}
|
||||
test_rpc_server {
|
||||
test_name = "capabilities_for_client_supports_method";
|
||||
@@ -479,8 +482,8 @@ describe('LSP', function()
|
||||
|
||||
it('should not send didOpen if the buffer closes before init', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -511,9 +514,9 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
on_handler = function(err, result, ctx)
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -522,9 +525,9 @@ describe('LSP', function()
|
||||
|
||||
it('should check the body sent attaching before init', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -554,12 +557,12 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result, ctx)
|
||||
if ctx.method == 'start' then
|
||||
client.notify('finish')
|
||||
end
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -568,9 +571,9 @@ describe('LSP', function()
|
||||
|
||||
it('should check the body sent attaching after init', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -597,12 +600,12 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result, ctx)
|
||||
if ctx.method == 'start' then
|
||||
client.notify('finish')
|
||||
end
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -611,9 +614,9 @@ describe('LSP', function()
|
||||
|
||||
it('should check the body and didChange full', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -640,8 +643,8 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result, ctx)
|
||||
if ctx.method == 'start' then
|
||||
exec_lua [[
|
||||
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
|
||||
"boop";
|
||||
@@ -649,8 +652,8 @@ describe('LSP', function()
|
||||
]]
|
||||
client.notify('finish')
|
||||
end
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -659,9 +662,9 @@ describe('LSP', function()
|
||||
|
||||
it('should check the body and didChange full with noeol', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -689,8 +692,8 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result, ctx)
|
||||
if ctx.method == 'start' then
|
||||
exec_lua [[
|
||||
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
|
||||
"boop";
|
||||
@@ -698,8 +701,8 @@ describe('LSP', function()
|
||||
]]
|
||||
client.notify('finish')
|
||||
end
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -708,9 +711,9 @@ describe('LSP', function()
|
||||
|
||||
it('should check the body and didChange incremental', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -738,8 +741,8 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result, ctx)
|
||||
if ctx.method == 'start' then
|
||||
exec_lua [[
|
||||
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
|
||||
"123boop";
|
||||
@@ -747,8 +750,8 @@ describe('LSP', function()
|
||||
]]
|
||||
client.notify('finish')
|
||||
end
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -758,9 +761,9 @@ describe('LSP', function()
|
||||
-- TODO(askhan) we don't support full for now, so we can disable these tests.
|
||||
pending('should check the body and didChange incremental normal mode editing', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -787,13 +790,13 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result, ctx)
|
||||
if ctx.method == 'start' then
|
||||
helpers.command("normal! 1Go")
|
||||
client.notify('finish')
|
||||
end
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -802,9 +805,9 @@ describe('LSP', function()
|
||||
|
||||
it('should check the body and didChange full with 2 changes', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -831,8 +834,8 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result, ctx)
|
||||
if ctx.method == 'start' then
|
||||
exec_lua [[
|
||||
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
|
||||
"321";
|
||||
@@ -843,8 +846,8 @@ describe('LSP', function()
|
||||
]]
|
||||
client.notify('finish')
|
||||
end
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -853,9 +856,9 @@ describe('LSP', function()
|
||||
|
||||
it('should check the body and didChange full lifecycle', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -882,8 +885,8 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
if method == 'start' then
|
||||
on_handler = function(err, result,ctx)
|
||||
if ctx.method == 'start' then
|
||||
exec_lua [[
|
||||
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
|
||||
"321";
|
||||
@@ -895,8 +898,8 @@ describe('LSP', function()
|
||||
]]
|
||||
client.notify('finish')
|
||||
end
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -907,9 +910,9 @@ describe('LSP', function()
|
||||
describe("parsing tests", function()
|
||||
it('should handle invalid content-length correctly', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -924,22 +927,22 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
on_handler = function(err, result, ctx)
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
end;
|
||||
}
|
||||
end)
|
||||
|
||||
it('should not trim vim.NIL from the end of a list', function()
|
||||
local expected_handlers = {
|
||||
{NIL, "shutdown", {}, 1};
|
||||
{NIL, "finish", {}, 1};
|
||||
{NIL, "workspace/executeCommand", {
|
||||
{NIL, {}, {method="shutdown", client_id=1}};
|
||||
{NIL, {}, {method="finish", client_id=1}};
|
||||
{NIL,{
|
||||
arguments = { "EXTRACT_METHOD", {metadata = {}}, 3, 0, 6123, NIL },
|
||||
command = "refactor.perform",
|
||||
title = "EXTRACT_METHOD"
|
||||
}, 1};
|
||||
{NIL, "start", {}, 1};
|
||||
}, {method="workspace/executeCommand", client_id=1}};
|
||||
{NIL, {}, {method="start", client_id=1}};
|
||||
}
|
||||
local client
|
||||
test_rpc_server {
|
||||
@@ -963,9 +966,9 @@ describe('LSP', function()
|
||||
eq(0, code, "exit code", fake_lsp_logfile)
|
||||
eq(0, signal, "exit signal", fake_lsp_logfile)
|
||||
end;
|
||||
on_handler = function(err, method, params, client_id)
|
||||
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
|
||||
if method == 'finish' then
|
||||
on_handler = function(err, result, ctx)
|
||||
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
|
||||
if ctx.method == 'finish' then
|
||||
client.stop()
|
||||
end
|
||||
end;
|
||||
@@ -1088,6 +1091,30 @@ describe('LSP', function()
|
||||
'å å ɧ 汉语 ↥ 🤦 🦄';
|
||||
}, buf_lines(1))
|
||||
end)
|
||||
it('applies complex edits (reversed range)', function()
|
||||
local edits = {
|
||||
make_edit(0, 0, 0, 0, {"", "12"});
|
||||
make_edit(0, 0, 0, 0, {"3", "foo"});
|
||||
make_edit(0, 1, 0, 1, {"bar", "123"});
|
||||
make_edit(0, #"First line of text", 0, #"First ", {"guy"});
|
||||
make_edit(1, #'Second', 1, 0, {"baz"});
|
||||
make_edit(2, #"Third", 2, #'Th', {"e next"});
|
||||
make_edit(3, #"Fourth", 3, #'', {"another line of text", "before this"});
|
||||
make_edit(3, #"Fourth line of text", 3, #'Fourth', {"!"});
|
||||
}
|
||||
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
|
||||
eq({
|
||||
'';
|
||||
'123';
|
||||
'fooFbar';
|
||||
'123irst guy';
|
||||
'baz line of text';
|
||||
'The next line of text';
|
||||
'another line of text';
|
||||
'before this!';
|
||||
'å å ɧ 汉语 ↥ 🤦 🦄';
|
||||
}, buf_lines(1))
|
||||
end)
|
||||
it('applies non-ASCII characters edits', function()
|
||||
local edits = {
|
||||
make_edit(4, 3, 4, 4, {"ä"});
|
||||
@@ -1116,6 +1143,86 @@ describe('LSP', function()
|
||||
}, buf_lines(1))
|
||||
end)
|
||||
|
||||
describe('cursor position', function()
|
||||
it('don\'t fix the cursor if the range contains the cursor', function()
|
||||
funcs.nvim_win_set_cursor(0, { 2, 6 })
|
||||
local edits = {
|
||||
make_edit(1, 0, 1, 19, 'Second line of text')
|
||||
}
|
||||
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
|
||||
eq({
|
||||
'First line of text';
|
||||
'Second line of text';
|
||||
'Third line of text';
|
||||
'Fourth line of text';
|
||||
'å å ɧ 汉语 ↥ 🤦 🦄';
|
||||
}, buf_lines(1))
|
||||
eq({ 2, 6 }, funcs.nvim_win_get_cursor(0))
|
||||
end)
|
||||
|
||||
it('fix the cursor to the valid column if the content was removed', function()
|
||||
funcs.nvim_win_set_cursor(0, { 2, 6 })
|
||||
local edits = {
|
||||
make_edit(1, 0, 1, 19, '')
|
||||
}
|
||||
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
|
||||
eq({
|
||||
'First line of text';
|
||||
'';
|
||||
'Third line of text';
|
||||
'Fourth line of text';
|
||||
'å å ɧ 汉语 ↥ 🤦 🦄';
|
||||
}, buf_lines(1))
|
||||
eq({ 2, 0 }, funcs.nvim_win_get_cursor(0))
|
||||
end)
|
||||
|
||||
it('fix the cursor row', function()
|
||||
funcs.nvim_win_set_cursor(0, { 3, 0 })
|
||||
local edits = {
|
||||
make_edit(1, 0, 2, 0, '')
|
||||
}
|
||||
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
|
||||
eq({
|
||||
'First line of text';
|
||||
'Third line of text';
|
||||
'Fourth line of text';
|
||||
'å å ɧ 汉语 ↥ 🤦 🦄';
|
||||
}, buf_lines(1))
|
||||
eq({ 2, 0 }, funcs.nvim_win_get_cursor(0))
|
||||
end)
|
||||
|
||||
it('fix the cursor col', function()
|
||||
funcs.nvim_win_set_cursor(0, { 2, 11 })
|
||||
local edits = {
|
||||
make_edit(1, 7, 1, 11, '')
|
||||
}
|
||||
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
|
||||
eq({
|
||||
'First line of text';
|
||||
'Second of text';
|
||||
'Third line of text';
|
||||
'Fourth line of text';
|
||||
'å å ɧ 汉语 ↥ 🤦 🦄';
|
||||
}, buf_lines(1))
|
||||
eq({ 2, 7 }, funcs.nvim_win_get_cursor(0))
|
||||
end)
|
||||
|
||||
it('fix the cursor row and col', function()
|
||||
funcs.nvim_win_set_cursor(0, { 2, 12 })
|
||||
local edits = {
|
||||
make_edit(0, 11, 1, 12, '')
|
||||
}
|
||||
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
|
||||
eq({
|
||||
'First line of text';
|
||||
'Third line of text';
|
||||
'Fourth line of text';
|
||||
'å å ɧ 汉语 ↥ 🤦 🦄';
|
||||
}, buf_lines(1))
|
||||
eq({ 1, 11 }, funcs.nvim_win_get_cursor(0))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('with LSP end line after what Vim considers to be the end line', function()
|
||||
it('applies edits when the last linebreak is considered a new line', function()
|
||||
local edits = {
|
||||
@@ -1223,7 +1330,7 @@ describe('LSP', function()
|
||||
label = nil;
|
||||
edit = {};
|
||||
}
|
||||
return vim.lsp.handlers['workspace/applyEdit'](nil, nil, apply_edit)
|
||||
return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit)
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
@@ -1976,7 +2083,7 @@ describe('LSP', function()
|
||||
describe('vim.lsp.buf.outgoing_calls', function()
|
||||
it('does nothing for an empty response', function()
|
||||
local qflist_count = exec_lua([=[
|
||||
require'vim.lsp.handlers'['callHierarchy/outgoingCalls']()
|
||||
require'vim.lsp.handlers'['callHierarchy/outgoingCalls'](nil, nil, {}, nil)
|
||||
return #vim.fn.getqflist()
|
||||
]=])
|
||||
eq(0, qflist_count)
|
||||
@@ -2023,7 +2130,7 @@ describe('LSP', function()
|
||||
}
|
||||
} }
|
||||
local handler = require'vim.lsp.handlers'['callHierarchy/outgoingCalls']
|
||||
handler(nil, nil, rust_analyzer_response)
|
||||
handler(nil, rust_analyzer_response, {})
|
||||
return vim.fn.getqflist()
|
||||
]=])
|
||||
|
||||
@@ -2047,7 +2154,7 @@ describe('LSP', function()
|
||||
describe('vim.lsp.buf.incoming_calls', function()
|
||||
it('does nothing for an empty response', function()
|
||||
local qflist_count = exec_lua([=[
|
||||
require'vim.lsp.handlers'['callHierarchy/incomingCalls']()
|
||||
require'vim.lsp.handlers'['callHierarchy/incomingCalls'](nil, nil, {})
|
||||
return #vim.fn.getqflist()
|
||||
]=])
|
||||
eq(0, qflist_count)
|
||||
@@ -2095,7 +2202,7 @@ describe('LSP', function()
|
||||
} }
|
||||
|
||||
local handler = require'vim.lsp.handlers'['callHierarchy/incomingCalls']
|
||||
handler(nil, nil, rust_analyzer_response)
|
||||
handler(nil, rust_analyzer_response, {})
|
||||
return vim.fn.getqflist()
|
||||
]=])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user