feat(lsp)!: change handler signature #15504

This commit is contained in:
Michael Lingelbach
2021-09-05 10:27:52 -07:00
committed by Justin M. Keyes
parent f8e0011534
commit cd8f6c5fb7
11 changed files with 355 additions and 273 deletions

View File

@@ -202,23 +202,28 @@ responses and notifications from LSP servers.
For |lsp-request|, each |lsp-handler| has this signature: > For |lsp-request|, each |lsp-handler| has this signature: >
function(err, method, result, client_id, bufnr, config) function(err, result, ctx, config)
< <
Parameters: ~ Parameters: ~
{err} (table|nil) {err} (table|nil)
When the language server is unable to complete a When the language server is unable to complete a
request, a table with information about the error request, a table with information about the error
is sent. Otherwise, it is `nil`. See |lsp-response|. is sent. Otherwise, it is `nil`. See |lsp-response|.
{method} (string)
The |lsp-method| name.
{result} (Result | Params | nil) {result} (Result | Params | nil)
When the language server is able to succesfully When the language server is able to succesfully
complete a request, this contains the `result` key complete a request, this contains the `result` key
of the response. See |lsp-response|. of the response. See |lsp-response|.
{client_id} (number) {ctx} (table)
The ID of the |vim.lsp.client|. Context describes additional calling state
{bufnr} (Buffer) associated with the handler. It consists of the
Buffer handle, or 0 for current. 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) {config} (table)
Configuration for the handler. 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: > For |lsp-notification|, each |lsp-handler| has this signature: >
function(err, method, params, client_id, bufnr, config) function(err, result, ctx, config)
< <
Parameters: ~ Parameters: ~
{err} (nil) {err} (nil)
This is always `nil`. This is always `nil`.
See |lsp-notification| See |lsp-notification|
{method} (string) {result} (Result)
The |lsp-method| name.
{params} (Params)
This contains the `params` key of the notification. This contains the `params` key of the notification.
See |lsp-notification| See |lsp-notification|
{client_id} (number) {ctx} (table)
The ID of the |vim.lsp.client| Context describes additional calling state
{bufnr} (nil) associated with the handler. It consists of the
`nil`, as the server doesn't have an associated buffer. following key, value pairs:
{method} (string)
The |lsp-method| name.
{client_id} (number)
The ID of the |vim.lsp.client|.
{config} (table) {config} (table)
Configuration for the handler. Configuration for the handler.
@@ -1355,7 +1363,7 @@ goto_prev({opts}) *vim.lsp.diagnostic.goto_prev()*
{opts} table See |vim.lsp.diagnostic.goto_next()| {opts} table See |vim.lsp.diagnostic.goto_next()|
*vim.lsp.diagnostic.on_publish_diagnostics()* *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" |lsp-handler| for the method "textDocument/publishDiagnostics"
Note: Note:
@@ -1581,7 +1589,7 @@ get({bufnr}) *vim.lsp.codelens.get()*
table ( `CodeLens[]` ) table ( `CodeLens[]` )
*vim.lsp.codelens.on_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` |lsp-handler| for the method `textDocument/codeLens`
refresh() *vim.lsp.codelens.refresh()* refresh() *vim.lsp.codelens.refresh()*
@@ -1614,17 +1622,25 @@ progress_handler({_}, {_}, {params}, {client_id})
See also: ~ See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
*vim.lsp.handlers.signature_help()* hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
signature_help({_}, {method}, {result}, {_}, {bufnr}, {config}) |lsp-handler| for the method "textDocument/hover" >
Parameters: ~
{config} table Configuration table.
• border: (default=nil)
• Add borders to the floating window
• See |vim.api.nvim_open_win()|
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
vim.lsp.handlers.hover, {
-- Use a sharp border with `FloatBorder` highlights
border = "single"
}
)
<
See also: ~ 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"> 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["textDocument/signatureHelp"] = vim.lsp.with(
vim.lsp.handlers.signature_help, { vim.lsp.handlers.signature_help, {
-- Use a sharp border with `FloatBorder` highlights -- Use a sharp border with `FloatBorder` highlights

View File

@@ -1474,14 +1474,12 @@ validate({opt}) *vim.validate()*
vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
=> NOP (success) => NOP (success)
<
> vim.validate{arg1={1, 'table'}}
vim.validate{arg1={1, 'table'}} => error('arg1: expected table, got number')
=> 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={3, function(a) return (a % 2) == 0 end, 'even number'}}
=> error('arg1: expected even number, got 3')
< <
Parameters: ~ Parameters: ~

View File

@@ -474,11 +474,9 @@ Query:iter_matches({self}, {node}, {source}, {start}, {stop})
for id, node in pairs(match) do for id, node in pairs(match) do
local name = query.captures[id] local name = query.captures[id]
-- `node` was captured by the `name` capture in the match -- `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 ... ... use the info here ...
end end
end end

View File

@@ -681,7 +681,7 @@ function lsp.start_client(config)
local handler = resolve_handler(method) local handler = resolve_handler(method)
if handler then if handler then
-- Method name is provided here for convenience. -- Method name is provided here for convenience.
handler(nil, method, params, client_id) handler(nil, params, {method=method, client_id=client_id})
end end
end end
@@ -695,7 +695,7 @@ function lsp.start_client(config)
local handler = resolve_handler(method) local handler = resolve_handler(method)
if handler then if handler then
local _ = log.debug() and log.debug("server_request: found handler for", method) 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 end
local _ = log.debug() and log.debug("server_request: no handler found for", method) local _ = log.debug() and log.debug("server_request: no handler found for", method)
return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound)
@@ -894,7 +894,7 @@ function lsp.start_client(config)
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr) local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr)
return rpc.request(method, params, function(err, result) 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)
end end
@@ -915,7 +915,7 @@ function lsp.start_client(config)
--@see |vim.lsp.buf_request_sync()| --@see |vim.lsp.buf_request_sync()|
function client.request_sync(method, params, timeout_ms, bufnr) function client.request_sync(method, params, timeout_ms, bufnr)
local request_result = nil local request_result = nil
local function _sync_handler(err, _, result) local function _sync_handler(err, result)
request_result = { err = err, result = result } request_result = { err = err, result = result }
end end
@@ -1274,7 +1274,7 @@ function lsp.buf_request(bufnr, method, params, handler)
local unsupported_err = lsp._unsupported_method(method) local unsupported_err = lsp._unsupported_method(method)
handler = handler or lsp.handlers[method] handler = handler or lsp.handlers[method]
if handler then if handler then
handler(unsupported_err, method, bufnr) handler(unsupported_err, nil, {method=method, bufnr=bufnr})
end end
return return
end end
@@ -1314,8 +1314,8 @@ function lsp.buf_request_all(bufnr, method, params, callback)
end end
end) end)
local function _sync_handler(err, _, result, client_id) local function _sync_handler(err, result, ctx)
request_results[client_id] = { error = err, result = result } request_results[ctx.client_id] = { error = err, result = result }
result_count = result_count + 1 result_count = result_count + 1
set_expected_result_count() set_expected_result_count()
@@ -1421,7 +1421,7 @@ function lsp.omnifunc(findstart, base)
local params = util.make_position_params() local params = util.make_position_params()
local items = {} 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 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) local matches = util.text_document_completion_list_to_complete_items(result, prefix)
-- TODO(ashkan): is this the best way to do this? -- TODO(ashkan): is this the best way to do this?
@@ -1496,8 +1496,8 @@ end
--@param handler (function) See |lsp-handler| --@param handler (function) See |lsp-handler|
--@param override_config (table) Table containing the keys to override behavior of the {handler} --@param override_config (table) Table containing the keys to override behavior of the {handler}
function lsp.with(handler, override_config) function lsp.with(handler, override_config)
return function(err, method, params, client_id, bufnr, config) return function(err, result, ctx, config)
return handler(err, method, params, client_id, bufnr, vim.tbl_deep_extend("force", config or {}, override_config)) return handler(err, result, ctx, vim.tbl_deep_extend("force", config or {}, override_config))
end end
end end

View File

@@ -422,6 +422,21 @@ function M.clear_references()
util.buf_clear_references() util.buf_clear_references()
end 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 --- Selects a code action from the input list that is available at the current
--- cursor position. --- cursor position.
-- --

View File

@@ -192,17 +192,17 @@ end
--- |lsp-handler| for the method `textDocument/codeLens` --- |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)) 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 -- Eager display for any resolved (and unresolved) lenses and refresh them
-- once resolved. -- once resolved.
M.display(result, bufnr, client_id) M.display(result, ctx.bufnr, ctx.client_id)
resolve_lenses(result, bufnr, client_id, function() resolve_lenses(result, ctx.bufnr, ctx.client_id, function()
M.display(result, bufnr, client_id) M.display(result, ctx.bufnr, ctx.client_id)
active_refreshes[bufnr] = nil active_refreshes[ctx.bufnr] = nil
end) end)
end end

View File

@@ -1008,15 +1008,16 @@ end
--- - Update diagnostics in InsertMode or wait until InsertLeave --- - Update diagnostics in InsertMode or wait until InsertLeave
--- - severity_sort: (default=false) --- - severity_sort: (default=false)
--- - Sort diagnostics (and thus signs and virtual text) --- - Sort diagnostics (and thus signs and virtual text)
function M.on_publish_diagnostics(_, _, params, client_id, _, config) function M.on_publish_diagnostics(_, result, ctx, config)
local uri = params.uri local client_id = ctx.client_id
local uri = result.uri
local bufnr = vim.uri_to_bufnr(uri) local bufnr = vim.uri_to_bufnr(uri)
if not bufnr then if not bufnr then
return return
end end
local diagnostics = params.diagnostics local diagnostics = result.diagnostics
if config and if_nil(config.severity_sort, false) then if config and if_nil(config.severity_sort, false) then
table.sort(diagnostics, function(a, b) return a.severity > b.severity end) 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) save_extmarks(bufnr, client_id)
end end
-- }}} --- Redraw diagnostics for the given buffer and client
-- Diagnostic User Functions {{{ ---
--- 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} --- Open a floating window with the diagnostics from {line_nr}
--- ---

View File

@@ -17,21 +17,21 @@ local function err_message(...)
api.nvim_command("redraw") api.nvim_command("redraw")
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand --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 -- Error handling is done implicitly by wrapping all handlers; see end of this file
end end
-- @msg of type ProgressParams ---@private
-- Basically a token of type number/string local function progress_handler(_, result, ctx, _)
local function progress_handler(_, _, params, client_id) local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(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) local client_name = client and client.name or string.format("id=%d", client_id)
if not client then if not client then
err_message("LSP[", client_name, "] client has shut down after sending the message") err_message("LSP[", client_name, "] client has shut down after sending the message")
end end
local val = params.value -- unspecified yet local val = result.value -- unspecified yet
local token = params.token -- string or number local token = result.token -- string or number
if val.kind then if val.kind then
@@ -62,10 +62,11 @@ end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
M['$/progress'] = progress_handler M['$/progress'] = progress_handler
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create --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 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) local client_name = client and client.name or string.format("id=%d", client_id)
if not client then if not client then
err_message("LSP[", client_name, "] client has shut down after sending the message") 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 return vim.NIL
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest --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 local actions = result.actions
print(params.message) print(result.message)
local option_strings = {params.message, "\nRequest Actions:"} local option_strings = {result.message, "\nRequest Actions:"}
for i, action in ipairs(actions) do for i, action in ipairs(actions) do
local title = action.title:gsub('\r\n', '\\r\\n') local title = action.title:gsub('\r\n', '\\r\\n')
title = title:gsub('\n', '\\n') title = title:gsub('\n', '\\n')
@@ -95,8 +96,9 @@ M['window/showMessageRequest'] = function(_, _, params)
end end
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability --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 ".. local warning_tpl = "The language server %s triggers a registerCapability "..
"handler despite dynamicRegistration set to false. ".. "handler despite dynamicRegistration set to false. "..
"Report upstream, this warning is harmless" "Report upstream, this warning is harmless"
@@ -107,25 +109,25 @@ M['client/registerCapability'] = function(_, _, _, client_id)
return vim.NIL return vim.NIL
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
M['textDocument/codeAction'] = function(_, _, actions) M['textDocument/codeAction'] = function(_, result)
if actions == nil or vim.tbl_isempty(actions) then if result == nil or vim.tbl_isempty(result) then
print("No code actions available") print("No code actions available")
return return
end end
local option_strings = {"Code Actions:"} local option_strings = {"Code actions:"}
for i, action in ipairs(actions) do for i, action in ipairs(result) do
local title = action.title:gsub('\r\n', '\\r\\n') local title = action.title:gsub('\r\n', '\\r\\n')
title = title:gsub('\n', '\\n') title = title:gsub('\n', '\\n')
table.insert(option_strings, string.format("%d. %s", i, title)) table.insert(option_strings, string.format("%d. %s", i, title))
end end
local choice = vim.fn.inputlist(option_strings) local choice = vim.fn.inputlist(option_strings)
if choice < 1 or choice > #actions then if choice < 1 or choice > #result then
return return
end end
local action_chosen = actions[choice] local action_chosen = result[choice]
-- textDocument/codeAction can return either Command[] or CodeAction[]. -- textDocument/codeAction can return either Command[] or CodeAction[].
-- If it is a CodeAction, it can have either an edit, a command or both. -- If it is a CodeAction, it can have either an edit, a command or both.
-- Edits should be executed first -- Edits should be executed first
@@ -155,29 +157,30 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit)
} }
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration --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) local client = vim.lsp.get_client_by_id(client_id)
if not client then if not client then
err_message("LSP[id=", client_id, "] client has shut down after sending the message") err_message("LSP[id=", client_id, "] client has shut down after sending the message")
return return
end end
if not params.items then if not result.items then
return {} return {}
end end
local result = {} local response = {}
for _, item in ipairs(params.items) do for _, item in ipairs(result.items) do
if item.section then if item.section then
local value = util.lookup_section(client.config.settings, item.section) or vim.NIL local value = util.lookup_section(client.config.settings, item.section) or vim.NIL
-- For empty sections with no explicit '' key, return settings as is -- For empty sections with no explicit '' key, return settings as is
if value == vim.NIL and item.section == '' then if value == vim.NIL and item.section == '' then
value = client.config.settings or vim.NIL value = client.config.settings or vim.NIL
end end
table.insert(result, value) table.insert(response, value)
end end
end end
return result return response
end end
M['textDocument/publishDiagnostics'] = function(...) M['textDocument/publishDiagnostics'] = function(...)
@@ -190,52 +193,63 @@ end
--@private ---@private
--- Return a function that converts LSP responses to quickfix items and opens the qflist --- Return a function that converts LSP responses to list items and opens the list
-- ---
--@param map_result function `((resp, bufnr) -> list)` to convert the response --- The returned function has an optional {config} parameter that accepts a table
--@param entity name of the resource used in a `not found` error message --- with the following keys:
local function response_to_qflist(map_result, entity) ---
return function(_, _, result, _, bufnr) --- 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 if not result or vim.tbl_isempty(result) then
vim.notify('No ' .. entity .. ' found') vim.notify('No ' .. entity .. ' found')
else else
util.set_qflist(map_result(result, bufnr)) config = config or {}
api.nvim_command("copen") 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 end
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
M['textDocument/references'] = response_to_qflist(util.locations_to_items, '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 --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') 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 --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol
M['workspace/symbol'] = response_to_qflist(util.symbols_to_items, 'symbols') M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols')
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename --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 if not result then return end
util.apply_workspace_edit(result) util.apply_workspace_edit(result)
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting
M['textDocument/rangeFormatting'] = function(_, _, result) M['textDocument/rangeFormatting'] = function(_, result, ctx, _)
if not result then return end if not result then return end
util.apply_text_edits(result) util.apply_text_edits(result, ctx.bufnr)
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
M['textDocument/formatting'] = function(_, _, result) M['textDocument/formatting'] = function(_, result, ctx, _)
if not result then return end if not result then return end
util.apply_text_edits(result) util.apply_text_edits(result, ctx.bufnr)
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion --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 if vim.tbl_isempty(result or {}) then return end
local row, col = unpack(api.nvim_win_get_cursor(0)) local row, col = unpack(api.nvim_win_get_cursor(0))
local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1])
@@ -260,9 +274,9 @@ end
--- - border: (default=nil) --- - border: (default=nil)
--- - Add borders to the floating window --- - Add borders to the floating window
--- - See |vim.api.nvim_open_win()| --- - See |vim.api.nvim_open_win()|
function M.hover(_, method, result, _, _, config) function M.hover(_, result, ctx, config)
config = config or {} config = config or {}
config.focus_id = method config.focus_id = ctx.method
if not (result and result.contents) then if not (result and result.contents) then
-- return { 'No information available' } -- return { 'No information available' }
return return
@@ -281,13 +295,13 @@ M['textDocument/hover'] = M.hover
--@private --@private
--- Jumps to a location. Used as a handler for multiple LSP methods. --- Jumps to a location. Used as a handler for multiple LSP methods.
--@param _ (not used) ---@param _ (not used)
--@param method (string) LSP method name ---@param result (table) result of LSP method; a location or a list of locations.
--@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[]` ---(`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 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 return nil
end end
@@ -328,17 +342,19 @@ M['textDocument/implementation'] = location_handler
--- - border: (default=nil) --- - border: (default=nil)
--- - Add borders to the floating window --- - Add borders to the floating window
--- - See |vim.api.nvim_open_win()| --- - 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 = 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 -- 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 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 if not (result and result.signatures and result.signatures[1]) then
print('No signature help available') print('No signature help available')
return return
end end
local ft = api.nvim_buf_get_option(bufnr, 'filetype') local client = vim.lsp.get_client_by_id(ctx.client_id)
local lines = util.convert_signature_help_to_markdown_lines(result, ft) 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) lines = util.trim_empty_lines(lines)
if vim.tbl_isempty(lines) then if vim.tbl_isempty(lines) then
print('No signature help available') print('No signature help available')
@@ -350,10 +366,10 @@ end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
M['textDocument/signatureHelp'] = M.signature_help M['textDocument/signatureHelp'] = M.signature_help
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight --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 if not result then return end
util.buf_highlight_references(bufnr, result) util.buf_highlight_references(ctx.bufnr, result)
end end
--@private --@private
@@ -364,7 +380,7 @@ end
--@returns `CallHierarchyIncomingCall[]` if {direction} is `"from"`, --@returns `CallHierarchyIncomingCall[]` if {direction} is `"from"`,
--@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`, --@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`,
local make_call_hierarchy_handler = function(direction) local make_call_hierarchy_handler = function(direction)
return function(_, _, result) return function(_, result)
if not result then return end if not result then return end
local items = {} local items = {}
for _, call_hierarchy_call in pairs(result) do 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 --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/outgoingCalls
M['callHierarchy/outgoingCalls'] = make_call_hierarchy_handler('to') M['callHierarchy/outgoingCalls'] = make_call_hierarchy_handler('to')
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/logMessage --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_type = result.type
local message = result.message local message = result.message
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(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) local client_name = client and client.name or string.format("id=%d", client_id)
if not client then if not client then
@@ -410,10 +427,11 @@ M['window/logMessage'] = function(_, _, result, client_id)
return result return result
end end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/showMessage --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_type = result.type
local message = result.message local message = result.message
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(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) local client_name = client and client.name or string.format("id=%d", client_id)
if not client then if not client then
@@ -430,12 +448,14 @@ end
-- Add boilerplate error validation and logging for all of these. -- Add boilerplate error validation and logging for all of these.
for k, fn in pairs(M) do for k, fn in pairs(M) do
M[k] = function(err, method, params, client_id, bufnr, config) M[k] = function(err, result, ctx, config)
local _ = log.debug() and log.debug('default_handler', method, { local _ = log.debug() and log.debug('default_handler', ctx.method, {
params = params, client_id = client_id, err = err, bufnr = bufnr, config = config err = err, result = result, ctx=vim.inspect(ctx), config = config
}) })
if err then 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: -- LSP spec:
-- interface ResponseError: -- interface ResponseError:
-- code: integer; -- code: integer;
@@ -444,7 +464,7 @@ for k, fn in pairs(M) do
return err_message(tostring(err.code) .. ': ' .. err.message) return err_message(tostring(err.code) .. ': ' .. err.message)
end end
return fn(err, method, params, client_id, bufnr, config) return fn(err, result, ctx, config)
end end
end end

View File

@@ -32,7 +32,7 @@ describe('vim.lsp.codelens', function()
command = { title = 'Lens1', command = 'Dummy' } 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) ]], bufnr)
local stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', 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), 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, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=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_2_diags }, {client_id=2})
return { return {
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1), vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1),
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2), 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), 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, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=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_2_diags }, {client_id=2})
return { return {
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1), vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1),
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2), 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() it('should return all diagnostics when no severity is supplied', function()
eq(2, exec_lua [[ eq(2, exec_lua [[
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { vim.lsp.diagnostic.on_publish_diagnostics(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error("Error 1", 1, 1, 1, 5), make_error("Error 1", 1, 1, 1, 5),
make_warning("Warning on Server 1", 1, 1, 2, 5), make_warning("Warning on Server 1", 1, 1, 2, 5),
make_error("Error On Other Line", 2, 1, 1, 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) 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() it('should return only requested diagnostics when severity_limit is supplied', function()
eq(2, exec_lua [[ eq(2, exec_lua [[
vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { vim.lsp.diagnostic.on_publish_diagnostics(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error("Error 1", 1, 1, 1, 5), 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_information("Ignored information", 1, 1, 2, 5),
make_error("Error On Other Line", 2, 1, 1, 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" }) return #vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1, { severity_limit = "Warning" })
]]) ]])
@@ -470,12 +470,12 @@ describe('vim.lsp.diagnostic', function()
exec_lua [[ exec_lua [[
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
virtual_text = function() return true end, virtual_text = function() return true end,
})(nil, nil, { })(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Delayed Diagnostic', 4, 4, 4, 4), make_error('Delayed Diagnostic', 4, 4, 4, 4),
} }
}, 1 }, {client_id=1}
) )
]] ]]
@@ -487,12 +487,12 @@ describe('vim.lsp.diagnostic', function()
exec_lua [[ exec_lua [[
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
virtual_text = function() return false end, virtual_text = function() return false end,
})(nil, nil, { })(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Delayed Diagnostic', 4, 4, 4, 4), make_error('Delayed Diagnostic', 4, 4, 4, 4),
} }
}, 1 }, {client_id=1}
) )
]] ]]
@@ -509,12 +509,12 @@ describe('vim.lsp.diagnostic', function()
exec_lua [[ exec_lua [[
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
update_in_insert = false, update_in_insert = false,
})(nil, nil, { })(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Delayed Diagnostic', 4, 4, 4, 4), make_error('Delayed Diagnostic', 4, 4, 4, 4),
} }
}, 1 }, {client_id=1}
) )
]] ]]
@@ -551,12 +551,12 @@ describe('vim.lsp.diagnostic', function()
return SetVirtualTextOriginal(...) return SetVirtualTextOriginal(...)
end end
PublishDiagnostics(nil, nil, { PublishDiagnostics(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Delayed Diagnostic', 4, 4, 4, 4), make_error('Delayed Diagnostic', 4, 4, 4, 4),
} }
}, 1 }, {client_id=1}
) )
]] ]]
@@ -605,12 +605,12 @@ describe('vim.lsp.diagnostic', function()
return SetVirtualTextOriginal(...) return SetVirtualTextOriginal(...)
end end
PublishDiagnostics(nil, nil, { PublishDiagnostics(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Delayed Diagnostic', 4, 4, 4, 4), make_error('Delayed Diagnostic', 4, 4, 4, 4),
} }
}, 1 }, {client_id=1}
) )
]] ]]
@@ -647,12 +647,12 @@ describe('vim.lsp.diagnostic', function()
exec_lua [[ exec_lua [[
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
update_in_insert = true, update_in_insert = true,
})(nil, nil, { })(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Delayed Diagnostic', 4, 4, 4, 4), 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, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Delayed Diagnostic', 4, 4, 4, 4), make_error('Delayed Diagnostic', 4, 4, 4, 4),
} }
}, 1 }, {client_id=1}
) )
return vim.api.nvim_buf_get_extmarks( return vim.api.nvim_buf_get_extmarks(
@@ -714,12 +714,12 @@ describe('vim.lsp.diagnostic', function()
end, end,
}) })
PublishDiagnostics(nil, nil, { PublishDiagnostics(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Delayed Diagnostic', 4, 4, 4, 4), make_error('Delayed Diagnostic', 4, 4, 4, 4),
} }
}, 1 }, {client_id=1}
) )
return vim.api.nvim_buf_get_extmarks( return vim.api.nvim_buf_get_extmarks(
@@ -747,12 +747,12 @@ describe('vim.lsp.diagnostic', function()
}, },
}) })
PublishDiagnostics(nil, nil, { PublishDiagnostics(nil, {
uri = fake_uri, uri = fake_uri,
diagnostics = { diagnostics = {
make_warning('Delayed Diagnostic', 4, 4, 4, 4), make_warning('Delayed Diagnostic', 4, 4, 4, 4),
} }
}, 1 }, {client_id=1}
) )
return count_of_extmarks_for_client(diagnostic_bufnr, 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.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, uri = fake_uri,
diagnostics = diagnostics diagnostics = diagnostics
}, 1 }, {client_id=1}
) )
vim.lsp.diagnostic.set_signs(diagnostics, diagnostic_bufnr, 1) vim.lsp.diagnostic.set_signs(diagnostics, diagnostic_bufnr, 1)
@@ -863,13 +863,13 @@ describe('vim.lsp.diagnostic', function()
local loc_list = exec_lua [[ local loc_list = exec_lua [[
vim.api.nvim_win_set_buf(0, diagnostic_bufnr) 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, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Farther Diagnostic', 4, 4, 4, 4), make_error('Farther Diagnostic', 4, 4, 4, 4),
make_error('Lower Diagnostic', 1, 1, 1, 1), make_error('Lower Diagnostic', 1, 1, 1, 1),
} }
}, 1 }, {client_id=1}
) )
vim.lsp.diagnostic.set_loclist() vim.lsp.diagnostic.set_loclist()
@@ -884,20 +884,20 @@ describe('vim.lsp.diagnostic', function()
local loc_list = exec_lua [[ local loc_list = exec_lua [[
vim.api.nvim_win_set_buf(0, diagnostic_bufnr) 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, uri = fake_uri,
diagnostics = { diagnostics = {
make_error('Lower Diagnostic', 1, 1, 1, 1), 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, uri = fake_uri,
diagnostics = { diagnostics = {
make_warning('Farther Diagnostic', 4, 4, 4, 4), make_warning('Farther Diagnostic', 4, 4, 4, 4),
} }
}, 2 }, {client_id=2}
) )
vim.lsp.diagnostic.set_loclist() vim.lsp.diagnostic.set_loclist()

View File

@@ -216,7 +216,7 @@ describe('LSP', function()
it('should run correctly', function() it('should run correctly', function()
local expected_handlers = { local expected_handlers = {
{NIL, "test", {}, 1}; {NIL, {}, {method="test", client_id=1}};
} }
test_rpc_server { test_rpc_server {
test_name = "basic_init"; test_name = "basic_init";
@@ -241,7 +241,7 @@ describe('LSP', function()
it('should fail', function() it('should fail', function()
local expected_handlers = { local expected_handlers = {
{NIL, "test", {}, 1}; {NIL, {}, {method="test", client_id=1}};
} }
test_rpc_server { test_rpc_server {
test_name = "basic_init"; test_name = "basic_init";
@@ -269,8 +269,8 @@ describe('LSP', function()
return return
end end
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1, NIL}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "test", {}, 1}; {NIL, {}, {method="test", client_id=1}};
} }
test_rpc_server { test_rpc_server {
test_name = "basic_init"; test_name = "basic_init";
@@ -292,12 +292,12 @@ describe('LSP', function()
it('client should return settings via workspace/configuration handler', function() it('client should return settings via workspace/configuration handler', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "workspace/configuration", { items = { {NIL, { items = {
{ section = "testSetting1" }; { section = "testSetting1" };
{ section = "testSetting2" }; { section = "testSetting2" };
}}, 1}; }}, { method="workspace/configuration", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -309,9 +309,9 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'start' then if ctx.method == 'start' then
exec_lua([=[ exec_lua([=[
local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID) local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID)
client.config.settings = { client.config.settings = {
@@ -319,13 +319,13 @@ describe('LSP', function()
testSetting2 = false; testSetting2 = false;
}]=]) }]=])
end end
if method == 'workspace/configuration' then if ctx.method == 'workspace/configuration' then
local result = exec_lua([=[ local server_result = exec_lua([=[
local method, params = ... local method, params = ...
return require'vim.lsp.handlers'['workspace/configuration'](err, method, params, TEST_RPC_CLIENT_ID)]=], method, params) return require'vim.lsp.handlers'['workspace/configuration'](err, params, {method=method, client_id=TEST_RPC_CLIENT_ID})]=], ctx.method, result)
client.notify('workspace/configuration', result) client.notify('workspace/configuration', server_result)
end end
if method == 'shutdown' then if ctx.method == 'shutdown' then
client.stop() client.stop()
end end
end; end;
@@ -335,19 +335,19 @@ describe('LSP', function()
clear_notrace() clear_notrace()
fake_lsp_server_setup('workspace/configuration no settings') fake_lsp_server_setup('workspace/configuration no settings')
eq({ NIL, NIL, }, exec_lua [[ eq({ NIL, NIL, }, exec_lua [[
local params = { local result = {
items = { items = {
{section = 'foo'}, {section = 'foo'},
{section = 'bar'}, {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) end)
it('should verify capabilities sent', function() it('should verify capabilities sent', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
} }
test_rpc_server { test_rpc_server {
test_name = "basic_check_capabilities"; test_name = "basic_check_capabilities";
@@ -371,7 +371,7 @@ describe('LSP', function()
it('client.supports_methods() should validate capabilities', function() it('client.supports_methods() should validate capabilities', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
} }
test_rpc_server { test_rpc_server {
test_name = "capabilities_for_client_supports_method"; test_name = "capabilities_for_client_supports_method";
@@ -405,7 +405,7 @@ describe('LSP', function()
it('should call unsupported_method when trying to call an unsupported method', function() it('should call unsupported_method when trying to call an unsupported method', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
} }
test_rpc_server { test_rpc_server {
test_name = "capabilities_for_client_supports_method"; test_name = "capabilities_for_client_supports_method";
@@ -413,7 +413,8 @@ describe('LSP', function()
exec_lua([=[ exec_lua([=[
BUFFER = vim.api.nvim_get_current_buf() BUFFER = vim.api.nvim_get_current_buf()
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID) 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 } vim.lsp._last_lsp_handler = { err = err; method = method }
end end
vim.lsp._unsupported_method = function(method) vim.lsp._unsupported_method = function(method)
@@ -446,7 +447,7 @@ describe('LSP', function()
it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function() it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
} }
test_rpc_server { test_rpc_server {
test_name = "capabilities_for_client_supports_method"; test_name = "capabilities_for_client_supports_method";
@@ -479,8 +480,8 @@ describe('LSP', function()
it('should not send didOpen if the buffer closes before init', function() it('should not send didOpen if the buffer closes before init', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -511,9 +512,9 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -522,9 +523,9 @@ describe('LSP', function()
it('should check the body sent attaching before init', function() it('should check the body sent attaching before init', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -554,12 +555,12 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
if method == 'start' then if ctx.method == 'start' then
client.notify('finish') client.notify('finish')
end end
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -568,9 +569,9 @@ describe('LSP', function()
it('should check the body sent attaching after init', function() it('should check the body sent attaching after init', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -597,12 +598,12 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
if method == 'start' then if ctx.method == 'start' then
client.notify('finish') client.notify('finish')
end end
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -611,9 +612,9 @@ describe('LSP', function()
it('should check the body and didChange full', function() it('should check the body and didChange full', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -640,8 +641,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
if method == 'start' then if ctx.method == 'start' then
exec_lua [[ exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"boop"; "boop";
@@ -649,8 +650,8 @@ describe('LSP', function()
]] ]]
client.notify('finish') client.notify('finish')
end end
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -659,9 +660,9 @@ describe('LSP', function()
it('should check the body and didChange full with noeol', function() it('should check the body and didChange full with noeol', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -689,8 +690,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
if method == 'start' then if ctx.method == 'start' then
exec_lua [[ exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"boop"; "boop";
@@ -698,8 +699,8 @@ describe('LSP', function()
]] ]]
client.notify('finish') client.notify('finish')
end end
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -708,9 +709,9 @@ describe('LSP', function()
it('should check the body and didChange incremental', function() it('should check the body and didChange incremental', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -738,8 +739,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
if method == 'start' then if ctx.method == 'start' then
exec_lua [[ exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"123boop"; "123boop";
@@ -747,8 +748,8 @@ describe('LSP', function()
]] ]]
client.notify('finish') client.notify('finish')
end end
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -758,9 +759,9 @@ describe('LSP', function()
-- TODO(askhan) we don't support full for now, so we can disable these tests. -- 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() pending('should check the body and didChange incremental normal mode editing', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -787,13 +788,13 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
if method == 'start' then if ctx.method == 'start' then
helpers.command("normal! 1Go") helpers.command("normal! 1Go")
client.notify('finish') client.notify('finish')
end end
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -802,9 +803,9 @@ describe('LSP', function()
it('should check the body and didChange full with 2 changes', function() it('should check the body and didChange full with 2 changes', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -831,8 +832,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
if method == 'start' then if ctx.method == 'start' then
exec_lua [[ exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"321"; "321";
@@ -843,8 +844,8 @@ describe('LSP', function()
]] ]]
client.notify('finish') client.notify('finish')
end end
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -853,9 +854,9 @@ describe('LSP', function()
it('should check the body and didChange full lifecycle', function() it('should check the body and didChange full lifecycle', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -882,8 +883,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result,ctx)
if method == 'start' then if ctx.method == 'start' then
exec_lua [[ exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"321"; "321";
@@ -895,8 +896,8 @@ describe('LSP', function()
]] ]]
client.notify('finish') client.notify('finish')
end end
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -907,9 +908,9 @@ describe('LSP', function()
describe("parsing tests", function() describe("parsing tests", function()
it('should handle invalid content-length correctly', function() it('should handle invalid content-length correctly', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -924,22 +925,22 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
end; end;
} }
end) end)
it('should not trim vim.NIL from the end of a list', function() it('should not trim vim.NIL from the end of a list', function()
local expected_handlers = { local expected_handlers = {
{NIL, "shutdown", {}, 1}; {NIL, {}, {method="shutdown", client_id=1}};
{NIL, "finish", {}, 1}; {NIL, {}, {method="finish", client_id=1}};
{NIL, "workspace/executeCommand", { {NIL,{
arguments = { "EXTRACT_METHOD", {metadata = {}}, 3, 0, 6123, NIL }, arguments = { "EXTRACT_METHOD", {metadata = {}}, 3, 0, 6123, NIL },
command = "refactor.perform", command = "refactor.perform",
title = "EXTRACT_METHOD" title = "EXTRACT_METHOD"
}, 1}; }, {method="workspace/executeCommand", client_id=1}};
{NIL, "start", {}, 1}; {NIL, {}, {method="start", client_id=1}};
} }
local client local client
test_rpc_server { test_rpc_server {
@@ -963,9 +964,9 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile) eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile)
end; end;
on_handler = function(err, method, params, client_id) on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
if method == 'finish' then if ctx.method == 'finish' then
client.stop() client.stop()
end end
end; end;
@@ -1976,7 +1977,7 @@ describe('LSP', function()
describe('vim.lsp.buf.outgoing_calls', function() describe('vim.lsp.buf.outgoing_calls', function()
it('does nothing for an empty response', function() it('does nothing for an empty response', function()
local qflist_count = exec_lua([=[ local qflist_count = exec_lua([=[
require'vim.lsp.handlers'['callHierarchy/outgoingCalls']() require'vim.lsp.handlers'['callHierarchy/outgoingCalls'](nil, nil, {}, nil)
return #vim.fn.getqflist() return #vim.fn.getqflist()
]=]) ]=])
eq(0, qflist_count) eq(0, qflist_count)
@@ -2023,7 +2024,7 @@ describe('LSP', function()
} }
} } } }
local handler = require'vim.lsp.handlers'['callHierarchy/outgoingCalls'] local handler = require'vim.lsp.handlers'['callHierarchy/outgoingCalls']
handler(nil, nil, rust_analyzer_response) handler(nil, rust_analyzer_response, {})
return vim.fn.getqflist() return vim.fn.getqflist()
]=]) ]=])
@@ -2047,7 +2048,7 @@ describe('LSP', function()
describe('vim.lsp.buf.incoming_calls', function() describe('vim.lsp.buf.incoming_calls', function()
it('does nothing for an empty response', function() it('does nothing for an empty response', function()
local qflist_count = exec_lua([=[ local qflist_count = exec_lua([=[
require'vim.lsp.handlers'['callHierarchy/incomingCalls']() require'vim.lsp.handlers'['callHierarchy/incomingCalls'](nil, nil, {})
return #vim.fn.getqflist() return #vim.fn.getqflist()
]=]) ]=])
eq(0, qflist_count) eq(0, qflist_count)
@@ -2095,7 +2096,7 @@ describe('LSP', function()
} } } }
local handler = require'vim.lsp.handlers'['callHierarchy/incomingCalls'] local handler = require'vim.lsp.handlers'['callHierarchy/incomingCalls']
handler(nil, nil, rust_analyzer_response) handler(nil, rust_analyzer_response, {})
return vim.fn.getqflist() return vim.fn.getqflist()
]=]) ]=])