feat(lsp)!: change handler signature

Previously, the handler signature was:

  function(err, method, params, client_id, bufnr, config)

In order to better support external plugins that wish to extend the
protocol, there is other information which would be advantageous to
forward to the client, such as the original params of the request that
generated the callback.

In order to do this, we would need to break symmetry of the handlers, to
add an additional "params" as the 7th argument.

Instead, this PR changes the signature of the handlers to:

  function(err, result, ctx, config)

where ctx (the context) includes params, client_id, and bufnr. This also leaves
flexibility for future use-cases.

BREAKING_CHANGE: changes the signature of the built-in client handlers, requiring
updating handler calls
This commit is contained in:
Michael Lingelbach
2021-08-28 00:12:30 -04:00
parent 5d633546bf
commit df17d7844e
9 changed files with 248 additions and 230 deletions

View File

@@ -202,19 +202,24 @@ 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|.
{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)
@@ -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|
{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} (nil)
`nil`, as the server doesn't have an associated buffer.
The ID of the |vim.lsp.client|.
{config} (table)
Configuration for the handler.

View File

@@ -678,7 +678,7 @@ function lsp.start_client(config)
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
@@ -692,7 +692,7 @@ function lsp.start_client(config)
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)
return handler(nil, params, {method=method, client_id=client_id})
end
local _ = log.debug() and log.debug("server_request: no handler found for", method)
return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound)
@@ -896,7 +896,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
@@ -917,7 +917,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
@@ -1276,7 +1276,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
@@ -1316,8 +1316,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()
@@ -1423,7 +1423,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?
@@ -1498,8 +1498,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

View File

@@ -433,7 +433,7 @@ local function code_action_request(params)
for _, r in pairs(results) do
vim.list_extend(actions, r.result or {})
end
vim.lsp.handlers[method](nil, method, actions, nil, bufnr)
vim.lsp.handlers[method](nil, actions, {bufnr=bufnr, method=method})
end)
end

View File

@@ -196,17 +196,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

View File

@@ -1020,15 +1020,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)
@@ -1204,13 +1205,15 @@ function M.redraw(bufnr, client_id)
-- the user may have set with vim.lsp.with.
vim.lsp.handlers["textDocument/publishDiagnostics"](
nil,
"textDocument/publishDiagnostics",
{
uri = vim.uri_from_bufnr(bufnr),
diagnostics = M.get(bufnr, client_id),
},
client_id,
bufnr
{
method = "textDocument/publishDiagnostics",
client_id = client_id,
bufnr = bufnr,
}
)
end

View File

@@ -18,19 +18,20 @@ local function err_message(...)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
M['workspace/executeCommand'] = function()
M['workspace/executeCommand'] = function(_, _, _, _)
-- Error handling is done implicitly by wrapping all handlers; see end of this file
end
---@private
local function progress_handler(_, _, params, client_id)
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,9 +63,10 @@ end
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)
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,11 +76,11 @@ M['window/workDoneProgress/create'] = function(_, _, params, client_id)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
M['window/showMessageRequest'] = function(_, _, params)
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,7 +97,8 @@ M['window/showMessageRequest'] = function(_, _, params)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
M['client/registerCapability'] = function(_, _, _, client_id)
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,24 +110,24 @@ M['client/registerCapability'] = function(_, _, _, client_id)
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
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
@@ -155,28 +158,29 @@ 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)
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(...)
@@ -200,16 +204,16 @@ end
---@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, _, bufnr, config)
return function(_,result, ctx, config)
if not result or vim.tbl_isempty(result) then
vim.notify('No ' .. entity .. ' found')
else
config = config or {}
if config.loclist then
util.set_loclist(map_result(result, bufnr))
util.set_loclist(map_result(result, ctx.bufnr))
api.nvim_command("lopen")
else
util.set_qflist(map_result(result, bufnr))
util.set_qflist(map_result(result, ctx.bufnr))
api.nvim_command("copen")
end
end
@@ -227,25 +231,25 @@ M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'docu
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)
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, _, bufnr)
M['textDocument/rangeFormatting'] = function(_, result, ctx, _)
if not result then return end
util.apply_text_edits(result, bufnr)
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, _, bufnr)
M['textDocument/formatting'] = function(_, result, ctx, _)
if not result then return end
util.apply_text_edits(result, bufnr)
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)
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])
@@ -270,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
@@ -292,12 +296,12 @@ 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 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
@@ -339,9 +343,9 @@ 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, client_id, 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
@@ -350,9 +354,9 @@ function M.signature_help(_, method, result, client_id, bufnr, config)
end
return
end
local client = vim.lsp.get_client_by_id(client_id)
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(bufnr, 'filetype')
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
@@ -372,9 +376,9 @@ end
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, _)
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
@@ -385,7 +389,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
@@ -411,9 +415,10 @@ M['callHierarchy/incomingCalls'] = make_call_hierarchy_handler('from')
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)
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
@@ -432,9 +437,10 @@ M['window/logMessage'] = function(_, _, result, client_id)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessage
M['window/showMessage'] = function(_, _, result, client_id)
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
@@ -451,14 +457,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.debug() and log.debug('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(client_id)
local client_name = client and client.name or string.format("client_id=%d", client_id)
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;
@@ -467,7 +473,7 @@ for k, fn in pairs(M) do
return err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message)
end
return fn(err, method, params, client_id, bufnr, config)
return fn(err, result, ctx, config)
end
end

View File

@@ -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)

View File

@@ -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),
@@ -251,8 +251,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})
vim.lsp.diagnostic.disable(diagnostic_bufnr, 1)
@@ -290,8 +290,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),
@@ -467,14 +467,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)
]])
@@ -482,7 +482,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),
@@ -490,7 +490,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" })
]])
@@ -502,12 +502,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}
)
]]
@@ -519,12 +519,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}
)
]]
@@ -541,12 +541,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}
)
]]
@@ -583,12 +583,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}
)
]]
@@ -637,12 +637,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}
)
]]
@@ -679,12 +679,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}
)
]]
@@ -709,12 +709,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(
@@ -746,12 +746,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(
@@ -779,12 +779,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)
@@ -870,10 +870,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)
@@ -895,13 +895,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()
@@ -916,20 +916,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()

View File

@@ -217,7 +217,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";
@@ -242,7 +242,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";
@@ -270,8 +270,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";
@@ -293,12 +293,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 {
@@ -310,9 +310,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 = {
@@ -320,13 +320,13 @@ 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;
@@ -336,19 +336,19 @@ describe('LSP', 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";
@@ -372,7 +372,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";
@@ -406,7 +406,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";
@@ -414,7 +414,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)
@@ -447,7 +448,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";
@@ -480,8 +481,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 {
@@ -512,9 +513,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;
@@ -523,9 +524,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 {
@@ -555,12 +556,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;
@@ -569,9 +570,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 {
@@ -598,12 +599,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;
@@ -612,9 +613,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 {
@@ -641,8 +642,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";
@@ -650,8 +651,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;
@@ -660,9 +661,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 {
@@ -690,8 +691,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";
@@ -699,8 +700,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;
@@ -709,9 +710,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 {
@@ -739,8 +740,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";
@@ -748,8 +749,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;
@@ -759,9 +760,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 {
@@ -788,13 +789,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;
@@ -803,9 +804,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 {
@@ -832,8 +833,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";
@@ -844,8 +845,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;
@@ -854,9 +855,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 {
@@ -883,8 +884,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";
@@ -896,8 +897,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;
@@ -908,9 +909,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 {
@@ -925,22 +926,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 {
@@ -964,9 +965,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;
@@ -1980,7 +1981,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)
@@ -2027,7 +2028,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()
]=])
@@ -2053,7 +2054,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)
@@ -2101,7 +2102,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()
]=])