feat(lsp)!: remove client-server handlers from vim.lsp.handlers

- Partition the handlers in vim.lsp.handlers as:
  - client to server response handlers (RCS)
  - server to client request handlers (RSC)
  - server to client notification handlers (NSC)

  Note use string indexes instead of protocol.methods for improved
  typing in LuaLS (tip: use hover on RCS, RSC or NSC).
This commit is contained in:
Lewis Russell
2024-10-29 09:36:02 +00:00
committed by Lewis Russell
parent f54266dbed
commit 9b357e30fd
14 changed files with 409 additions and 471 deletions

View File

@ -50,6 +50,12 @@ LSP
• *vim.lsp.buf.completion* Use |vim.lsp.completion.trigger()| instead. • *vim.lsp.buf.completion* Use |vim.lsp.completion.trigger()| instead.
• vim.lsp.buf_request_all The `error` key has been renamed to `err` inside • vim.lsp.buf_request_all The `error` key has been renamed to `err` inside
the result parameter of the handler. the result parameter of the handler.
• *vim.lsp.with()* Pass configuration to equivalent
functions in `vim.lsp.buf.*'.
• |vim.lsp.handlers|
No longer support client to server response handlers. Only server to
client requests/notification handlers are supported.
• *vim.lsp.handlers.signature_help()* Use |vim.lsp.buf.signature_help()| instead.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
DEPRECATED IN 0.10 *deprecated-0.10* DEPRECATED IN 0.10 *deprecated-0.10*

View File

@ -195,41 +195,41 @@ Requests and notifications defined by the LSP specification are referred to as
They are also listed below. Note that handlers depend on server support: they They are also listed below. Note that handlers depend on server support: they
won't run if your server doesn't support them. won't run if your server doesn't support them.
- callHierarchy/incomingCalls - `'callHierarchy/incomingCalls'`
- callHierarchy/outgoingCalls - `'callHierarchy/outgoingCalls'`
- textDocument/codeAction - `'textDocument/codeAction'`
- textDocument/completion - `'textDocument/completion'`
- textDocument/declaration* - `'textDocument/declaration'`
- textDocument/definition - `'textDocument/definition'`
- textDocument/diagnostic - `'textDocument/diagnostic'`
- textDocument/documentHighlight - `'textDocument/documentHighlight'`
- textDocument/documentSymbol - `'textDocument/documentSymbol'`
- textDocument/formatting - `'textDocument/formatting'`
- textDocument/hover - `'textDocument/hover'`
- textDocument/implementation* - `'textDocument/implementation'`
- textDocument/inlayHint - `'textDocument/inlayHint'`
- textDocument/prepareTypeHierarchy - `'textDocument/prepareTypeHierarchy'`
- textDocument/publishDiagnostics - `'textDocument/publishDiagnostics'`
- textDocument/rangeFormatting - `'textDocument/rangeFormatting'`
- textDocument/rangesFormatting - `'textDocument/rangesFormatting'`
- textDocument/references - `'textDocument/references'`
- textDocument/rename - `'textDocument/rename'`
- textDocument/semanticTokens/full - `'textDocument/semanticTokens/full'`
- textDocument/semanticTokens/full/delta - `'textDocument/semanticTokens/full/delta'`
- textDocument/signatureHelp - `'textDocument/signatureHelp'`
- textDocument/typeDefinition* - `'textDocument/typeDefinition*'`
- typeHierarchy/subtypes - `'typeHierarchy/subtypes'`
- typeHierarchy/supertypes - `'typeHierarchy/supertypes'`
- window/logMessage - `'window/logMessage'`
- window/showMessage - `'window/showMessage'`
- window/showDocument - `'window/showDocument'`
- window/showMessageRequest - `'window/showMessageRequest'`
- workspace/applyEdit - `'workspace/applyEdit'`
- workspace/configuration - `'workspace/configuration'`
- workspace/executeCommand - `'workspace/executeCommand'`
- workspace/inlayHint/refresh - `'workspace/inlayHint/refresh'`
- workspace/symbol - `'workspace/symbol'`
- workspace/workspaceFolders - `'workspace/workspaceFolders'`
*lsp-handler* *lsp-handler*
LSP handlers are functions that handle |lsp-response|s to requests made by Nvim LSP handlers are functions that handle |lsp-response|s to requests made by Nvim
@ -238,7 +238,7 @@ there is no response, so they can't be handled. |lsp-notification|)
Each response handler has this signature: > Each response handler has this signature: >
function(err, result, ctx, config) function(err, result, ctx)
< <
Parameters: ~ Parameters: ~
• {err} (`table|nil`) Error info dict, or `nil` if the request • {err} (`table|nil`) Error info dict, or `nil` if the request
@ -255,121 +255,53 @@ Each response handler has this signature: >
request. Handlers can compare this to the request. Handlers can compare this to the
current document version to check if the current document version to check if the
response is "stale". See also |b:changedtick|. response is "stale". See also |b:changedtick|.
• {config} (`table`) Handler-defined configuration table, which allows
users to customize handler behavior.
For an example, see:
|vim.lsp.diagnostic.on_publish_diagnostics()|
To configure a particular |lsp-handler|, see:
|lsp-handler-configuration|
Returns: ~ Returns: ~
Two values `result, err` where `err` is shaped like an RPC error: > Two values `result, err` where `err` is shaped like an RPC error: >
{ code, message, data? } { code, message, data? }
< You can use |vim.lsp.rpc.rpc_response_error()| to create this object. < You can use |vim.lsp.rpc.rpc_response_error()| to create this object.
*lsp-handler-configuration*
To configure the behavior of a builtin |lsp-handler|, the convenient method
|vim.lsp.with()| is provided for users.
To configure the behavior of |vim.lsp.diagnostic.on_publish_diagnostics()|,
consider the following example, where a new |lsp-handler| is created using
|vim.lsp.with()| that no longer generates signs for the diagnostics: >lua
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
-- Disable signs
signs = false,
}
)
<
To enable signs, use |vim.lsp.with()| again to create and assign a new
|lsp-handler| to |vim.lsp.handlers| for the associated method: >lua
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
-- Enable signs
signs = true,
}
)
<
To configure a handler on a per-server basis, you can use the {handlers} key
for |vim.lsp.start_client()| >lua
vim.lsp.start_client {
..., -- Other configuration omitted.
handlers = {
["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
-- Disable virtual_text
virtual_text = false,
}
),
},
}
<
or if using "nvim-lspconfig", you can use the {handlers} key of `setup()`:
>lua
require('lspconfig').rust_analyzer.setup {
handlers = {
["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
-- Disable virtual_text
virtual_text = false
}
),
}
}
<
Some handlers do not have an explicitly named handler function (such as
|vim.lsp.diagnostic.on_publish_diagnostics()|). To override these, first
create a reference to the existing handler: >lua
local on_references = vim.lsp.handlers["textDocument/references"]
vim.lsp.handlers["textDocument/references"] = vim.lsp.with(
on_references, {
-- Use location list instead of quickfix list
loclist = true,
}
)
<
*lsp-handler-resolution* *lsp-handler-resolution*
Handlers can be set by: Handlers can be set by (in increasing priority):
- Setting a field in vim.lsp.handlers. *vim.lsp.handlers* - Setting a field in vim.lsp.handlers. *vim.lsp.handlers*
vim.lsp.handlers is a global table that contains the default mapping of `vim.lsp.handlers` is a global table that contains the default mapping of
|lsp-method| names to |lsp-handlers|. To override the handler for the |lsp-method| names to lsp-handlers.
`"textDocument/definition"` method: >lua
vim.lsp.handlers["textDocument/definition"] = my_custom_default_definition Example: >lua
vim.lsp.handlers['textDocument/publishDiagnostics'] = my_custom_diagnostics_handler
< <
Note: this only applies for requests/notifications made by the
server to the client.
- The {handlers} parameter of |vim.lsp.start()|. This sets the default - The {handlers} parameter of |vim.lsp.start()|. This sets the default
|lsp-handler| for the server being started. Example: >lua |lsp-handler| for a specific server.
Example: >lua
vim.lsp.start { vim.lsp.start {
..., -- Other configuration omitted. ..., -- Other configuration omitted.
handlers = { handlers = {
["textDocument/definition"] = my_custom_server_definition ['textDocument/publishDiagnostics'] = my_custom_server_definition
}, },
} }
<
Note: this only applies for requests/notifications made by the
server to the client.
- The {handler} parameter of |vim.lsp.buf_request_all()|. This sets - The {handler} parameter of |vim.lsp.buf_request_all()|. This sets
the |lsp-handler| ONLY for the given request(s). Example: >lua the |lsp-handler| ONLY for the given request(s).
Example: >lua
vim.lsp.buf_request_all( vim.lsp.buf_request_all(
0, 0,
"textDocument/definition", 'textDocument/publishDiagnostics',
my_request_params, my_request_params,
my_handler my_handler
) )
< <
In summary, the |lsp-handler| will be chosen based on the current |lsp-method|
in the following order:
1. Handler passed to |vim.lsp.buf_request_all()|, if any.
2. Handler defined in |vim.lsp.start()|, if any.
3. Handler defined in |vim.lsp.handlers|, if any.
*vim.lsp.log_levels* *vim.lsp.log_levels*
Log levels are defined in |vim.log.levels| Log levels are defined in |vim.log.levels|
@ -955,14 +887,6 @@ tagfunc({pattern}, {flags}) *vim.lsp.tagfunc()*
Return: ~ Return: ~
(`table[]`) tags A list of matching tags (`table[]`) tags A list of matching tags
with({handler}, {override_config}) *vim.lsp.with()*
Function to manage overriding defaults for LSP handlers.
Parameters: ~
• {handler} (`lsp.Handler`) See |lsp-handler|
• {override_config} (`table`) Table containing the keys to override
behavior of the {handler}
============================================================================== ==============================================================================
Lua module: vim.lsp.client *lsp-client* Lua module: vim.lsp.client *lsp-client*
@ -1276,6 +1200,13 @@ Lua module: vim.lsp.buf *lsp-buf*
Extends: |vim.lsp.util.open_floating_preview.Opts| Extends: |vim.lsp.util.open_floating_preview.Opts|
Fields: ~
• {silent}? (`boolean`)
*vim.lsp.buf.signature_help.Opts*
Extends: |vim.lsp.util.open_floating_preview.Opts|
Fields: ~ Fields: ~
• {silent}? (`boolean`) • {silent}? (`boolean`)
@ -1461,10 +1392,14 @@ rename({new_name}, {opts}) *vim.lsp.buf.rename()*
ones where client.name matches this field. ones where client.name matches this field.
• {bufnr}? (`integer`) (default: current buffer) • {bufnr}? (`integer`) (default: current buffer)
signature_help() *vim.lsp.buf.signature_help()* signature_help({config}) *vim.lsp.buf.signature_help()*
Displays signature information about the symbol under the cursor in a Displays signature information about the symbol under the cursor in a
floating window. floating window.
Parameters: ~
• {config} (`vim.lsp.buf.signature_help.Opts?`) See
|vim.lsp.buf.signature_help.Opts|.
type_definition({opts}) *vim.lsp.buf.type_definition()* type_definition({opts}) *vim.lsp.buf.type_definition()*
Jumps to the definition of the type of the symbol under the cursor. Jumps to the definition of the type of the symbol under the cursor.
@ -1514,66 +1449,24 @@ get_namespace({client_id}, {is_pull})
client. Defaults to push client. Defaults to push
*vim.lsp.diagnostic.on_diagnostic()* *vim.lsp.diagnostic.on_diagnostic()*
on_diagnostic({_}, {result}, {ctx}, {config}) on_diagnostic({_}, {result}, {ctx})
|lsp-handler| for the method "textDocument/diagnostic" |lsp-handler| for the method "textDocument/diagnostic"
See |vim.diagnostic.config()| for configuration options. Handler-specific See |vim.diagnostic.config()| for configuration options.
configuration can be set using |vim.lsp.with()|: >lua
vim.lsp.handlers["textDocument/diagnostic"] = vim.lsp.with(
vim.lsp.diagnostic.on_diagnostic, {
-- Enable underline, use default values
underline = true,
-- Enable virtual text, override spacing to 4
virtual_text = {
spacing = 4,
},
-- Use a function to dynamically turn signs off
-- and on, using buffer local variables
signs = function(namespace, bufnr)
return vim.b[bufnr].show_signs == true
end,
-- Disable a feature
update_in_insert = false,
}
)
<
Parameters: ~ Parameters: ~
• {result} (`lsp.DocumentDiagnosticReport`) • {result} (`lsp.DocumentDiagnosticReport`)
• {ctx} (`lsp.HandlerContext`) • {ctx} (`lsp.HandlerContext`)
• {config} (`vim.diagnostic.Opts`) Configuration table (see
|vim.diagnostic.config()|).
*vim.lsp.diagnostic.on_publish_diagnostics()* *vim.lsp.diagnostic.on_publish_diagnostics()*
on_publish_diagnostics({_}, {result}, {ctx}, {config}) on_publish_diagnostics({_}, {result}, {ctx})
|lsp-handler| for the method "textDocument/publishDiagnostics" |lsp-handler| for the method "textDocument/publishDiagnostics"
See |vim.diagnostic.config()| for configuration options. Handler-specific See |vim.diagnostic.config()| for configuration options.
configuration can be set using |vim.lsp.with()|: >lua
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
-- Enable underline, use default values
underline = true,
-- Enable virtual text, override spacing to 4
virtual_text = {
spacing = 4,
},
-- Use a function to dynamically turn signs off
-- and on, using buffer local variables
signs = function(namespace, bufnr)
return vim.b[bufnr].show_signs == true
end,
-- Disable a feature
update_in_insert = false,
}
)
<
Parameters: ~ Parameters: ~
• {result} (`lsp.PublishDiagnosticsParams`) • {result} (`lsp.PublishDiagnosticsParams`)
• {ctx} (`lsp.HandlerContext`) • {ctx} (`lsp.HandlerContext`)
• {config} (`vim.diagnostic.Opts?`) Configuration table (see
|vim.diagnostic.config()|).
============================================================================== ==============================================================================
@ -1823,32 +1716,6 @@ stop({bufnr}, {client_id}) *vim.lsp.semantic_tokens.stop()*
• {client_id} (`integer`) The ID of the |vim.lsp.Client| • {client_id} (`integer`) The ID of the |vim.lsp.Client|
==============================================================================
Lua module: vim.lsp.handlers *lsp-handlers*
*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|. >lua
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
vim.lsp.handlers.signature_help, {
-- Use a sharp border with `FloatBorder` highlights
border = "single"
}
)
<
Parameters: ~
• {result} (`lsp.SignatureHelp?`) Response from the language server
• {ctx} (`lsp.HandlerContext`) Client context
• {config} (`table`) Configuration table.
• border: (default=nil)
• Add borders to the floating window
• See |vim.lsp.util.open_floating_preview()| for more
options
============================================================================== ==============================================================================
Lua module: vim.lsp.util *lsp-util* Lua module: vim.lsp.util *lsp-util*

View File

@ -246,7 +246,7 @@ The following new features were added.
indicator to see if a server supports a feature. Instead use indicator to see if a server supports a feature. Instead use
`client.supports_method(<method>)`. It considers both the dynamic `client.supports_method(<method>)`. It considers both the dynamic
capabilities and static `server_capabilities`. capabilities and static `server_capabilities`.
• `anchor_bias` option to |lsp-handlers| aids in positioning of floating • `anchor_bias` option to lsp-handlers aids in positioning of floating
windows. windows.
• |vim.lsp.util.locations_to_items()| sets the `user_data` of each item to • |vim.lsp.util.locations_to_items()| sets the `user_data` of each item to
the original LSP `Location` or `LocationLink`. the original LSP `Location` or `LocationLink`.

View File

@ -87,7 +87,13 @@ LSP
but no longer trigger the global handlers from `vim.lsp.handlers` but no longer trigger the global handlers from `vim.lsp.handlers`
• |vim.lsp.buf.typehierarchy()| now passes the correct params for each • |vim.lsp.buf.typehierarchy()| now passes the correct params for each
client request. client request.
• |vim.lsp.handlers.signature_help()| is no longer used.
• |vim.lsp.diagnostic.on_publish_diagnostics()| and
|vim.lsp.diagnostic.on_diagnostic()| no longer accept a config parameter and
can no longer be configured with |vim.lsp.with()|.
Instead use: >lua
vim.diagnostic.config(config, vim.lsp.diagnostic.get_namespace(client_id))
<
LUA LUA

View File

@ -1165,6 +1165,7 @@ function lsp.for_each_buffer_client(bufnr, fn)
end end
end end
--- @deprecated
--- Function to manage overriding defaults for LSP handlers. --- Function to manage overriding defaults for LSP handlers.
---@param handler (lsp.Handler) See |lsp-handler| ---@param handler (lsp.Handler) 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}

View File

@ -1,8 +1,8 @@
---@meta ---@meta
error('Cannot require a meta file') error('Cannot require a meta file')
---@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext, config?: table): ...any ---@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext): ...any
---@alias lsp.MultiHandler fun(results: table<integer,{err: lsp.ResponseError?, result: any}>, context: lsp.HandlerContext, config?: table): ...any ---@alias lsp.MultiHandler fun(results: table<integer,{err: lsp.ResponseError?, result: any}>, context: lsp.HandlerContext): ...any
---@class lsp.HandlerContext ---@class lsp.HandlerContext
---@field method string ---@field method string

View File

@ -258,10 +258,72 @@ function M.implementation(opts)
get_locations(ms.textDocument_implementation, opts) get_locations(ms.textDocument_implementation, opts)
end end
local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help')
--- @class vim.lsp.buf.signature_help.Opts : vim.lsp.util.open_floating_preview.Opts
--- @field silent? boolean
-- TODO(lewis6991): support multiple clients
--- Displays signature information about the symbol under the cursor in a --- Displays signature information about the symbol under the cursor in a
--- floating window. --- floating window.
function M.signature_help() --- @param config? vim.lsp.buf.signature_help.Opts
lsp.buf_request(0, ms.textDocument_signatureHelp, client_positional_params()) function M.signature_help(config)
local method = ms.textDocument_signatureHelp
config = config or {}
config.focus_id = method
lsp.buf_request(0, method, client_positional_params(), function(err, result, ctx)
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
if err then
vim.notify(
client.name .. ': ' .. tostring(err.code) .. ': ' .. err.message,
vim.log.levels.ERROR
)
api.nvim_command('redraw')
return
end
if api.nvim_get_current_buf() ~= ctx.bufnr then
-- Ignore result since buffer changed. This happens for slow language servers.
return
end
-- 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 or not result.signatures or not result.signatures[1] then
if config.silent ~= true then
print('No signature help available')
end
return
end
local triggers =
vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters')
local ft = vim.bo[ctx.bufnr].filetype
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
if not lines or vim.tbl_isempty(lines) then
if config.silent ~= true then
print('No signature help available')
end
return
end
local fbuf = util.open_floating_preview(lines, 'markdown', config)
-- Highlight the active parameter.
if hl then
vim.hl.range(
fbuf,
sig_help_ns,
'LspSignatureActiveParameter',
{ hl[1], hl[2] },
{ hl[3], hl[4] }
)
end
end)
end end
--- @deprecated --- @deprecated

View File

@ -261,7 +261,7 @@ end
---@param err lsp.ResponseError? ---@param err lsp.ResponseError?
---@param result lsp.CodeLens[] ---@param result lsp.CodeLens[]
---@param ctx lsp.HandlerContext ---@param ctx lsp.HandlerContext
function M.on_codelens(err, result, ctx, _) function M.on_codelens(err, result, ctx)
if err then if err then
active_refreshes[assert(ctx.bufnr)] = nil active_refreshes[assert(ctx.bufnr)] = nil
log.error('codelens', err) log.error('codelens', err)

View File

@ -9,14 +9,6 @@ local augroup = api.nvim_create_augroup('vim_lsp_diagnostic', {})
local DEFAULT_CLIENT_ID = -1 local DEFAULT_CLIENT_ID = -1
local function get_client_id(client_id)
if client_id == nil then
client_id = DEFAULT_CLIENT_ID
end
return client_id
end
---@param severity lsp.DiagnosticSeverity ---@param severity lsp.DiagnosticSeverity
local function severity_lsp_to_vim(severity) local function severity_lsp_to_vim(severity)
if type(severity) == 'string' then if type(severity) == 'string' then
@ -218,8 +210,7 @@ end
--- @param client_id? integer --- @param client_id? integer
--- @param diagnostics vim.Diagnostic[] --- @param diagnostics vim.Diagnostic[]
--- @param is_pull boolean --- @param is_pull boolean
--- @param config? vim.diagnostic.Opts local function handle_diagnostics(uri, client_id, diagnostics, is_pull)
local function handle_diagnostics(uri, client_id, diagnostics, is_pull, config)
local fname = vim.uri_to_fname(uri) local fname = vim.uri_to_fname(uri)
if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then
@ -231,91 +222,39 @@ local function handle_diagnostics(uri, client_id, diagnostics, is_pull, config)
return return
end end
client_id = get_client_id(client_id) if client_id == nil then
local namespace = M.get_namespace(client_id, is_pull) client_id = DEFAULT_CLIENT_ID
if config then
--- @cast config table<string, table>
for _, opt in pairs(config) do
convert_severity(opt)
end
-- Persist configuration to ensure buffer reloads use the same
-- configuration. To make lsp.with configuration work (See :help
-- lsp-handler-configuration)
vim.diagnostic.config(config, namespace)
end end
local namespace = M.get_namespace(client_id, is_pull)
vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
end end
--- |lsp-handler| for the method "textDocument/publishDiagnostics" --- |lsp-handler| for the method "textDocument/publishDiagnostics"
--- ---
--- See |vim.diagnostic.config()| for configuration options. Handler-specific --- See |vim.diagnostic.config()| for configuration options.
--- configuration can be set using |vim.lsp.with()|:
---
--- ```lua
--- vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
--- vim.lsp.diagnostic.on_publish_diagnostics, {
--- -- Enable underline, use default values
--- underline = true,
--- -- Enable virtual text, override spacing to 4
--- virtual_text = {
--- spacing = 4,
--- },
--- -- Use a function to dynamically turn signs off
--- -- and on, using buffer local variables
--- signs = function(namespace, bufnr)
--- return vim.b[bufnr].show_signs == true
--- end,
--- -- Disable a feature
--- update_in_insert = false,
--- }
--- )
--- ```
--- ---
---@param _ lsp.ResponseError? ---@param _ lsp.ResponseError?
---@param result lsp.PublishDiagnosticsParams ---@param result lsp.PublishDiagnosticsParams
---@param ctx lsp.HandlerContext ---@param ctx lsp.HandlerContext
---@param config? vim.diagnostic.Opts Configuration table (see |vim.diagnostic.config()|). function M.on_publish_diagnostics(_, result, ctx)
function M.on_publish_diagnostics(_, result, ctx, config) handle_diagnostics(result.uri, ctx.client_id, result.diagnostics, false)
handle_diagnostics(result.uri, ctx.client_id, result.diagnostics, false, config)
end end
--- |lsp-handler| for the method "textDocument/diagnostic" --- |lsp-handler| for the method "textDocument/diagnostic"
--- ---
--- See |vim.diagnostic.config()| for configuration options. Handler-specific --- See |vim.diagnostic.config()| for configuration options.
--- configuration can be set using |vim.lsp.with()|:
---
--- ```lua
--- vim.lsp.handlers["textDocument/diagnostic"] = vim.lsp.with(
--- vim.lsp.diagnostic.on_diagnostic, {
--- -- Enable underline, use default values
--- underline = true,
--- -- Enable virtual text, override spacing to 4
--- virtual_text = {
--- spacing = 4,
--- },
--- -- Use a function to dynamically turn signs off
--- -- and on, using buffer local variables
--- signs = function(namespace, bufnr)
--- return vim.b[bufnr].show_signs == true
--- end,
--- -- Disable a feature
--- update_in_insert = false,
--- }
--- )
--- ```
--- ---
---@param _ lsp.ResponseError? ---@param _ lsp.ResponseError?
---@param result lsp.DocumentDiagnosticReport ---@param result lsp.DocumentDiagnosticReport
---@param ctx lsp.HandlerContext ---@param ctx lsp.HandlerContext
---@param config vim.diagnostic.Opts Configuration table (see |vim.diagnostic.config()|). function M.on_diagnostic(_, result, ctx)
function M.on_diagnostic(_, result, ctx, config)
if result == nil or result.kind == 'unchanged' then if result == nil or result.kind == 'unchanged' then
return return
end end
handle_diagnostics(ctx.params.textDocument.uri, ctx.client_id, result.items, true, config) handle_diagnostics(ctx.params.textDocument.uri, ctx.client_id, result.items, true)
end end
--- Clear push diagnostics and diagnostic cache. --- Clear push diagnostics and diagnostic cache.

View File

@ -5,10 +5,21 @@ local util = require('vim.lsp.util')
local api = vim.api local api = vim.api
local completion = require('vim.lsp.completion') local completion = require('vim.lsp.completion')
--- @type table<string,lsp.Handler> --- @type table<string, lsp.Handler>
local M = {} local M = {}
-- FIXME: DOC: Expose in vimdocs --- @deprecated
--- Client to server response handlers.
--- @type table<vim.lsp.protocol.Method.ClientToServer, lsp.Handler>
local RCS = {}
--- Server to client request handlers.
--- @type table<vim.lsp.protocol.Method.ServerToClient, lsp.Handler>
local RSC = {}
--- Server to client notification handlers.
--- @type table<vim.lsp.protocol.Method.ServerToClient, lsp.Handler>
local NSC = {}
--- Writes to error buffer. --- Writes to error buffer.
---@param ... string Will be concatenated before being written ---@param ... string Will be concatenated before being written
@ -18,14 +29,15 @@ local function err_message(...)
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[ms.workspace_executeCommand] = function(_, _, _, _) RCS[ms.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
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
---@param params lsp.ProgressParams ---@param params lsp.ProgressParams
---@param ctx lsp.HandlerContext ---@param ctx lsp.HandlerContext
M[ms.dollar_progress] = function(_, params, ctx) ---@diagnostic disable-next-line:no-unknown
RSC[ms.dollar_progress] = function(_, params, ctx)
local client = vim.lsp.get_client_by_id(ctx.client_id) local client = vim.lsp.get_client_by_id(ctx.client_id)
if not client then if not client then
err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update')
@ -59,26 +71,26 @@ M[ms.dollar_progress] = function(_, params, ctx)
end end
--- @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
---@param result lsp.WorkDoneProgressCreateParams ---@param params lsp.WorkDoneProgressCreateParams
---@param ctx lsp.HandlerContext ---@param ctx lsp.HandlerContext
M[ms.window_workDoneProgress_create] = function(_, result, ctx) RSC[ms.window_workDoneProgress_create] = function(_, params, ctx)
local client = vim.lsp.get_client_by_id(ctx.client_id) local client = vim.lsp.get_client_by_id(ctx.client_id)
if not client then if not client then
err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update')
return vim.NIL return vim.NIL
end end
client.progress:push(result) client.progress:push(params)
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
---@param result lsp.ShowMessageRequestParams ---@param params lsp.ShowMessageRequestParams
M[ms.window_showMessageRequest] = function(_, result) RSC[ms.window_showMessageRequest] = function(_, params)
local actions = result.actions or {} local actions = params.actions or {}
local co, is_main = coroutine.running() local co, is_main = coroutine.running()
if co and not is_main then if co and not is_main then
local opts = { local opts = {
prompt = result.message .. ': ', prompt = params.message .. ': ',
format_item = function(action) format_item = function(action)
return (action.title:gsub('\r\n', '\\r\\n')):gsub('\n', '\\n') return (action.title:gsub('\r\n', '\\r\\n')):gsub('\n', '\\n')
end, end,
@ -92,7 +104,7 @@ M[ms.window_showMessageRequest] = function(_, result)
end) end)
return coroutine.yield() return coroutine.yield()
else else
local option_strings = { result.message, '\nRequest Actions:' } local option_strings = { params.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')
@ -108,19 +120,19 @@ M[ms.window_showMessageRequest] = function(_, result)
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
--- @param result lsp.RegistrationParams --- @param params lsp.RegistrationParams
M[ms.client_registerCapability] = function(_, result, ctx) RSC[ms.client_registerCapability] = function(_, params, ctx)
local client_id = ctx.client_id local client_id = ctx.client_id
local client = assert(vim.lsp.get_client_by_id(client_id)) local client = assert(vim.lsp.get_client_by_id(client_id))
client.dynamic_capabilities:register(result.registrations) client.dynamic_capabilities:register(params.registrations)
for bufnr, _ in pairs(client.attached_buffers) do for bufnr, _ in pairs(client.attached_buffers) do
vim.lsp._set_defaults(client, bufnr) vim.lsp._set_defaults(client, bufnr)
end end
---@type string[] ---@type string[]
local unsupported = {} local unsupported = {}
for _, reg in ipairs(result.registrations) do for _, reg in ipairs(params.registrations) do
if reg.method == ms.workspace_didChangeWatchedFiles then if reg.method == ms.workspace_didChangeWatchedFiles then
vim.lsp._watchfiles.register(reg, ctx) vim.lsp._watchfiles.register(reg, ctx)
elseif not client.dynamic_capabilities:supports_registration(reg.method) then elseif not client.dynamic_capabilities:supports_registration(reg.method) then
@ -139,13 +151,13 @@ M[ms.client_registerCapability] = function(_, result, ctx)
end end
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability
--- @param result lsp.UnregistrationParams --- @param params lsp.UnregistrationParams
M[ms.client_unregisterCapability] = function(_, result, ctx) RSC[ms.client_unregisterCapability] = function(_, params, ctx)
local client_id = ctx.client_id local client_id = ctx.client_id
local client = assert(vim.lsp.get_client_by_id(client_id)) local client = assert(vim.lsp.get_client_by_id(client_id))
client.dynamic_capabilities:unregister(result.unregisterations) client.dynamic_capabilities:unregister(params.unregisterations)
for _, unreg in ipairs(result.unregisterations) do for _, unreg in ipairs(params.unregisterations) do
if unreg.method == ms.workspace_didChangeWatchedFiles then if unreg.method == ms.workspace_didChangeWatchedFiles then
vim.lsp._watchfiles.unregister(unreg, ctx) vim.lsp._watchfiles.unregister(unreg, ctx)
end end
@ -153,20 +165,20 @@ M[ms.client_unregisterCapability] = function(_, result, ctx)
return vim.NIL return vim.NIL
end end
-- TODO(lewis6991): Do we need to notify other servers?
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
M[ms.workspace_applyEdit] = function(_, workspace_edit, ctx) RSC[ms.workspace_applyEdit] = function(_, params, ctx)
assert( assert(
workspace_edit, params,
'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification' 'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification'
) )
-- TODO(ashkan) Do something more with label? -- TODO(ashkan) Do something more with label?
local client_id = ctx.client_id local client_id = ctx.client_id
local client = assert(vim.lsp.get_client_by_id(client_id)) local client = assert(vim.lsp.get_client_by_id(client_id))
if workspace_edit.label then if params.label then
print('Workspace edit', workspace_edit.label) print('Workspace edit', params.label)
end end
local status, result = local status, result = pcall(util.apply_workspace_edit, params.edit, client.offset_encoding)
pcall(util.apply_workspace_edit, workspace_edit.edit, client.offset_encoding)
return { return {
applied = status, applied = status,
failureReason = result, failureReason = result,
@ -182,8 +194,8 @@ local function lookup_section(table, section)
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
--- @param result lsp.ConfigurationParams --- @param params lsp.ConfigurationParams
M[ms.workspace_configuration] = function(_, result, ctx) RSC[ms.workspace_configuration] = function(_, params, ctx)
local client_id = ctx.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)
if not client then if not client then
@ -194,12 +206,12 @@ M[ms.workspace_configuration] = function(_, result, ctx)
) )
return return
end end
if not result.items then if not params.items then
return {} return {}
end end
local response = {} local response = {}
for _, item in ipairs(result.items) do for _, item in ipairs(params.items) do
if item.section then if item.section then
local value = lookup_section(client.settings, item.section) local value = lookup_section(client.settings, item.section)
-- For empty sections with no explicit '' key, return settings as is -- For empty sections with no explicit '' key, return settings as is
@ -216,7 +228,7 @@ M[ms.workspace_configuration] = function(_, result, ctx)
end end
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders
M[ms.workspace_workspaceFolders] = function(_, _, ctx) RSC[ms.workspace_workspaceFolders] = function(_, _, ctx)
local client_id = ctx.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)
if not client then if not client then
@ -226,19 +238,22 @@ M[ms.workspace_workspaceFolders] = function(_, _, ctx)
return client.workspace_folders or vim.NIL return client.workspace_folders or vim.NIL
end end
M[ms.textDocument_publishDiagnostics] = function(...) NSC[ms.textDocument_publishDiagnostics] = function(...)
return vim.lsp.diagnostic.on_publish_diagnostics(...) return vim.lsp.diagnostic.on_publish_diagnostics(...)
end end
M[ms.textDocument_diagnostic] = function(...) --- @private
RCS[ms.textDocument_diagnostic] = function(...)
return vim.lsp.diagnostic.on_diagnostic(...) return vim.lsp.diagnostic.on_diagnostic(...)
end end
M[ms.textDocument_codeLens] = function(...) --- @private
RCS[ms.textDocument_codeLens] = function(...)
return vim.lsp.codelens.on_codelens(...) return vim.lsp.codelens.on_codelens(...)
end end
M[ms.textDocument_inlayHint] = function(...) --- @private
RCS[ms.textDocument_inlayHint] = function(...)
return vim.lsp.inlay_hint.on_inlayhint(...) return vim.lsp.inlay_hint.on_inlayhint(...)
end end
@ -251,6 +266,7 @@ end
---@param title_fn fun(ctx: lsp.HandlerContext): string Function to call to generate list title ---@param title_fn fun(ctx: lsp.HandlerContext): string Function to call to generate list title
---@return lsp.Handler ---@return lsp.Handler
local function response_to_list(map_result, entity, title_fn) local function response_to_list(map_result, entity, title_fn)
--- @diagnostic disable-next-line:redundant-parameter
return function(_, result, ctx, config) 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')
@ -274,8 +290,9 @@ local function response_to_list(map_result, entity, title_fn)
end end
end end
--- @deprecated remove in 0.13
--- @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[ms.textDocument_documentSymbol] = response_to_list( RCS[ms.textDocument_documentSymbol] = response_to_list(
util.symbols_to_items, util.symbols_to_items,
'document symbols', 'document symbols',
function(ctx) function(ctx)
@ -284,13 +301,15 @@ M[ms.textDocument_documentSymbol] = response_to_list(
end end
) )
--- @deprecated remove in 0.13
--- @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[ms.workspace_symbol] = response_to_list(util.symbols_to_items, 'symbols', function(ctx) RCS[ms.workspace_symbol] = response_to_list(util.symbols_to_items, 'symbols', function(ctx)
return string.format("Symbols matching '%s'", ctx.params.query) return string.format("Symbols matching '%s'", ctx.params.query)
end) end)
--- @deprecated remove in 0.13
--- @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[ms.textDocument_rename] = function(_, result, ctx, _) RCS[ms.textDocument_rename] = function(_, result, ctx)
if not result then if not result then
vim.notify("Language server couldn't provide rename result", vim.log.levels.INFO) vim.notify("Language server couldn't provide rename result", vim.log.levels.INFO)
return return
@ -299,8 +318,9 @@ M[ms.textDocument_rename] = function(_, result, ctx, _)
util.apply_workspace_edit(result, client.offset_encoding) util.apply_workspace_edit(result, client.offset_encoding)
end end
--- @deprecated remove in 0.13
--- @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[ms.textDocument_rangeFormatting] = function(_, result, ctx, _) RCS[ms.textDocument_rangeFormatting] = function(_, result, ctx)
if not result then if not result then
return return
end end
@ -308,8 +328,9 @@ M[ms.textDocument_rangeFormatting] = function(_, result, ctx, _)
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
end end
--- @deprecated remove in 0.13
--- @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[ms.textDocument_formatting] = function(_, result, ctx, _) RCS[ms.textDocument_formatting] = function(_, result, ctx)
if not result then if not result then
return return
end end
@ -319,7 +340,7 @@ end
--- @deprecated remove in 0.13 --- @deprecated remove in 0.13
--- @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[ms.textDocument_completion] = function(_, result, _, _) RCS[ms.textDocument_completion] = function(_, result, _)
if vim.tbl_isempty(result or {}) then if vim.tbl_isempty(result or {}) then
return return
end end
@ -355,6 +376,7 @@ end
--- - border: (default=nil) --- - border: (default=nil)
--- - Add borders to the floating window --- - Add borders to the floating window
--- - See |vim.lsp.util.open_floating_preview()| for more options. --- - See |vim.lsp.util.open_floating_preview()| for more options.
--- @diagnostic disable-next-line:redundant-parameter
function M.hover(_, result, ctx, config) function M.hover(_, result, ctx, config)
config = config or {} config = config or {}
config.focus_id = ctx.method config.focus_id = ctx.method
@ -388,10 +410,11 @@ end
--- @deprecated remove in 0.13 --- @deprecated remove in 0.13
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
--- @diagnostic disable-next-line: deprecated --- @diagnostic disable-next-line: deprecated
M[ms.textDocument_hover] = M.hover RCS[ms.textDocument_hover] = M.hover
local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help')
--- @deprecated remove in 0.13
--- |lsp-handler| for the method "textDocument/signatureHelp". --- |lsp-handler| for the method "textDocument/signatureHelp".
--- ---
--- The active parameter is highlighted with |hl-LspSignatureActiveParameter|. --- The active parameter is highlighted with |hl-LspSignatureActiveParameter|.
@ -412,6 +435,7 @@ local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help')
--- - border: (default=nil) --- - border: (default=nil)
--- - Add borders to the floating window --- - Add borders to the floating window
--- - See |vim.lsp.util.open_floating_preview()| for more options --- - See |vim.lsp.util.open_floating_preview()| for more options
--- @diagnostic disable-next-line:redundant-parameter
function M.signature_help(_, result, ctx, config) function M.signature_help(_, result, ctx, config)
config = config or {} config = config or {}
config.focus_id = ctx.method config.focus_id = ctx.method
@ -452,11 +476,14 @@ function M.signature_help(_, result, ctx, config)
return fbuf, fwin return fbuf, fwin
end end
--- @deprecated remove in 0.13
--- @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[ms.textDocument_signatureHelp] = M.signature_help --- @diagnostic disable-next-line:deprecated
RCS[ms.textDocument_signatureHelp] = M.signature_help
--- @deprecated remove in 0.13
--- @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[ms.textDocument_documentHighlight] = function(_, result, ctx, _) RCS[ms.textDocument_documentHighlight] = function(_, result, ctx)
if not result then if not result then
return return
end end
@ -499,11 +526,13 @@ local function make_call_hierarchy_handler(direction)
end end
end end
--- @deprecated remove in 0.13
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_incomingCalls --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_incomingCalls
M[ms.callHierarchy_incomingCalls] = make_call_hierarchy_handler('from') RCS[ms.callHierarchy_incomingCalls] = make_call_hierarchy_handler('from')
--- @deprecated remove in 0.13
--- @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[ms.callHierarchy_outgoingCalls] = make_call_hierarchy_handler('to') RCS[ms.callHierarchy_outgoingCalls] = make_call_hierarchy_handler('to')
--- Displays type hierarchy in the quickfix window. --- Displays type hierarchy in the quickfix window.
local function make_type_hierarchy_handler() local function make_type_hierarchy_handler()
@ -538,17 +567,19 @@ local function make_type_hierarchy_handler()
end end
end end
--- @deprecated remove in 0.13
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_incomingCalls --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_incomingCalls
M[ms.typeHierarchy_subtypes] = make_type_hierarchy_handler() RCS[ms.typeHierarchy_subtypes] = make_type_hierarchy_handler()
--- @deprecated remove in 0.13
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_outgoingCalls --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_outgoingCalls
M[ms.typeHierarchy_supertypes] = make_type_hierarchy_handler() RCS[ms.typeHierarchy_supertypes] = make_type_hierarchy_handler()
--- @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
--- @param result lsp.LogMessageParams --- @param params lsp.LogMessageParams
M[ms.window_logMessage] = function(_, result, ctx, _) NSC['window/logMessage'] = function(_, params, ctx)
local message_type = result.type local message_type = params.type
local message = result.message local message = params.message
local client_id = ctx.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)
@ -564,14 +595,14 @@ M[ms.window_logMessage] = function(_, result, ctx, _)
else else
log.debug(message) log.debug(message)
end end
return result return params
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
--- @param result lsp.ShowMessageParams --- @param params lsp.ShowMessageParams
M[ms.window_showMessage] = function(_, result, ctx, _) NSC['window/showMessage'] = function(_, params, ctx)
local message_type = result.type local message_type = params.type
local message = result.message local message = params.message
local client_id = ctx.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)
@ -585,15 +616,16 @@ M[ms.window_showMessage] = function(_, result, ctx, _)
local message_type_name = protocol.MessageType[message_type] local message_type_name = protocol.MessageType[message_type]
api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message))
end end
return result return params
end end
--- @private
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showDocument --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showDocument
--- @param result lsp.ShowDocumentParams --- @param params lsp.ShowDocumentParams
M[ms.window_showDocument] = function(_, result, ctx, _) RSC[ms.window_showDocument] = function(_, params, ctx)
local uri = result.uri local uri = params.uri
if result.external then if params.external then
-- TODO(lvimuser): ask the user for confirmation -- TODO(lvimuser): ask the user for confirmation
local cmd, err = vim.ui.open(uri) local cmd, err = vim.ui.open(uri)
local ret = cmd and cmd:wait(2000) or nil local ret = cmd and cmd:wait(2000) or nil
@ -621,35 +653,39 @@ M[ms.window_showDocument] = function(_, result, ctx, _)
local location = { local location = {
uri = uri, uri = uri,
range = result.selection, range = params.selection,
} }
local success = util.show_document(location, client.offset_encoding, { local success = util.show_document(location, client.offset_encoding, {
reuse_win = true, reuse_win = true,
focus = result.takeFocus, focus = params.takeFocus,
}) })
return { success = success or false } return { success = success or false }
end end
---@see https://microsoft.github.io/language-server-protocol/specification/#workspace_inlayHint_refresh ---@see https://microsoft.github.io/language-server-protocol/specification/#workspace_inlayHint_refresh
M[ms.workspace_inlayHint_refresh] = function(err, result, ctx, config) RSC[ms.workspace_inlayHint_refresh] = function(err, result, ctx)
return vim.lsp.inlay_hint.on_refresh(err, result, ctx, config) return vim.lsp.inlay_hint.on_refresh(err, result, ctx)
end end
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#semanticTokens_refreshRequest ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#semanticTokens_refreshRequest
M[ms.workspace_semanticTokens_refresh] = function(err, result, ctx, _config) RSC[ms.workspace_semanticTokens_refresh] = function(err, result, ctx)
return vim.lsp.semantic_tokens._refresh(err, result, ctx) return vim.lsp.semantic_tokens._refresh(err, result, ctx)
end end
--- @nodoc
--- @type table<string, lsp.Handler>
M = vim.tbl_extend('force', M, RSC, NSC, RCS)
-- 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
--- @diagnostic disable-next-line:redundant-parameter
M[k] = function(err, result, ctx, config) M[k] = function(err, result, ctx, config)
if log.trace() then if log.trace() then
log.trace('default_handler', ctx.method, { log.trace('default_handler', ctx.method, {
err = err, err = err,
result = result, result = result,
ctx = vim.inspect(ctx), ctx = vim.inspect(ctx),
config = config,
}) })
end end
@ -670,6 +706,7 @@ for k, fn in pairs(M) do
return return
end end
--- @diagnostic disable-next-line:redundant-parameter
return fn(err, result, ctx, config) return fn(err, result, ctx, config)
end end
end end

View File

@ -37,7 +37,7 @@ local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {})
---@param result lsp.InlayHint[]? ---@param result lsp.InlayHint[]?
---@param ctx lsp.HandlerContext ---@param ctx lsp.HandlerContext
---@private ---@private
function M.on_inlayhint(err, result, ctx, _) function M.on_inlayhint(err, result, ctx)
if err then if err then
log.error('inlayhint', err) log.error('inlayhint', err)
return return
@ -87,7 +87,7 @@ end
--- |lsp-handler| for the method `workspace/inlayHint/refresh` --- |lsp-handler| for the method `workspace/inlayHint/refresh`
---@param ctx lsp.HandlerContext ---@param ctx lsp.HandlerContext
---@private ---@private
function M.on_refresh(err, _, ctx, _) function M.on_refresh(err, _, ctx)
if err then if err then
return vim.NIL return vim.NIL
end end

View File

@ -618,10 +618,110 @@ function protocol.resolve_capabilities(server_capabilities)
return server_capabilities return server_capabilities
end end
-- Generated by gen_lsp.lua, keep at end of file.
--- @alias vim.lsp.protocol.Method.ClientToServer
--- | 'callHierarchy/incomingCalls',
--- | 'callHierarchy/outgoingCalls',
--- | 'codeAction/resolve',
--- | 'codeLens/resolve',
--- | 'completionItem/resolve',
--- | 'documentLink/resolve',
--- | '$/setTrace',
--- | 'exit',
--- | 'initialize',
--- | 'initialized',
--- | 'inlayHint/resolve',
--- | 'notebookDocument/didChange',
--- | 'notebookDocument/didClose',
--- | 'notebookDocument/didOpen',
--- | 'notebookDocument/didSave',
--- | 'shutdown',
--- | 'textDocument/codeAction',
--- | 'textDocument/codeLens',
--- | 'textDocument/colorPresentation',
--- | 'textDocument/completion',
--- | 'textDocument/declaration',
--- | 'textDocument/definition',
--- | 'textDocument/diagnostic',
--- | 'textDocument/didChange',
--- | 'textDocument/didClose',
--- | 'textDocument/didOpen',
--- | 'textDocument/didSave',
--- | 'textDocument/documentColor',
--- | 'textDocument/documentHighlight',
--- | 'textDocument/documentLink',
--- | 'textDocument/documentSymbol',
--- | 'textDocument/foldingRange',
--- | 'textDocument/formatting',
--- | 'textDocument/hover',
--- | 'textDocument/implementation',
--- | 'textDocument/inlayHint',
--- | 'textDocument/inlineCompletion',
--- | 'textDocument/inlineValue',
--- | 'textDocument/linkedEditingRange',
--- | 'textDocument/moniker',
--- | 'textDocument/onTypeFormatting',
--- | 'textDocument/prepareCallHierarchy',
--- | 'textDocument/prepareRename',
--- | 'textDocument/prepareTypeHierarchy',
--- | 'textDocument/rangeFormatting',
--- | 'textDocument/rangesFormatting',
--- | 'textDocument/references',
--- | 'textDocument/rename',
--- | 'textDocument/selectionRange',
--- | 'textDocument/semanticTokens/full',
--- | 'textDocument/semanticTokens/full/delta',
--- | 'textDocument/semanticTokens/range',
--- | 'textDocument/signatureHelp',
--- | 'textDocument/typeDefinition',
--- | 'textDocument/willSave',
--- | 'textDocument/willSaveWaitUntil',
--- | 'typeHierarchy/subtypes',
--- | 'typeHierarchy/supertypes',
--- | 'window/workDoneProgress/cancel',
--- | 'workspaceSymbol/resolve',
--- | 'workspace/diagnostic',
--- | 'workspace/didChangeConfiguration',
--- | 'workspace/didChangeWatchedFiles',
--- | 'workspace/didChangeWorkspaceFolders',
--- | 'workspace/didCreateFiles',
--- | 'workspace/didDeleteFiles',
--- | 'workspace/didRenameFiles',
--- | 'workspace/executeCommand',
--- | 'workspace/symbol',
--- | 'workspace/willCreateFiles',
--- | 'workspace/willDeleteFiles',
--- | 'workspace/willRenameFiles',
--- @alias vim.lsp.protocol.Method.ServerToClient
--- | 'client/registerCapability',
--- | 'client/unregisterCapability',
--- | '$/logTrace',
--- | 'telemetry/event',
--- | 'textDocument/publishDiagnostics',
--- | 'window/logMessage',
--- | 'window/showDocument',
--- | 'window/showMessage',
--- | 'window/showMessageRequest',
--- | 'window/workDoneProgress/create',
--- | 'workspace/applyEdit',
--- | 'workspace/codeLens/refresh',
--- | 'workspace/configuration',
--- | 'workspace/diagnostic/refresh',
--- | 'workspace/foldingRange/refresh',
--- | 'workspace/inlayHint/refresh',
--- | 'workspace/inlineValue/refresh',
--- | 'workspace/semanticTokens/refresh',
--- | 'workspace/workspaceFolders',
--- @alias vim.lsp.protocol.Method
--- | vim.lsp.protocol.Method.ClientToServer
--- | vim.lsp.protocol.Method.ServerToClient
-- Generated by gen_lsp.lua, keep at end of file. -- Generated by gen_lsp.lua, keep at end of file.
--- ---
---@enum vim.lsp.protocol.Methods --- @enum vim.lsp.protocol.Methods
---@see https://microsoft.github.io/language-server-protocol/specification/#metaModel --- @see https://microsoft.github.io/language-server-protocol/specification/#metaModel
--- LSP method names. --- LSP method names.
protocol.Methods = { protocol.Methods = {
--- A request to resolve the incoming calls for a given `CallHierarchyItem`. --- A request to resolve the incoming calls for a given `CallHierarchyItem`.

View File

@ -58,14 +58,6 @@ end
---@param protocol vim._gen_lsp.Protocol ---@param protocol vim._gen_lsp.Protocol
local function gen_methods(protocol) local function gen_methods(protocol)
local output = {
'-- Generated by gen_lsp.lua, keep at end of file.',
'---',
'---@enum vim.lsp.protocol.Methods',
'---@see https://microsoft.github.io/language-server-protocol/specification/#metaModel',
'--- LSP method names.',
'protocol.Methods = {',
}
local indent = (' '):rep(2) local indent = (' '):rep(2)
--- @class vim._gen_lsp.Request --- @class vim._gen_lsp.Request
@ -98,6 +90,41 @@ local function gen_methods(protocol)
table.sort(all, function(a, b) table.sort(all, function(a, b)
return to_luaname(a.method) < to_luaname(b.method) return to_luaname(a.method) < to_luaname(b.method)
end) end)
local output = {
'-- Generated by gen_lsp.lua, keep at end of file.',
'--- @alias vim.lsp.protocol.Method.ClientToServer',
}
for _, item in ipairs(all) do
if item.method and item.messageDirection == 'clientToServer' then
output[#output + 1] = ("--- | '%s',"):format(item.method)
end
end
vim.list_extend(output, {
'',
'--- @alias vim.lsp.protocol.Method.ServerToClient',
})
for _, item in ipairs(all) do
if item.method and item.messageDirection == 'serverToClient' then
output[#output + 1] = ("--- | '%s',"):format(item.method)
end
end
vim.list_extend(output, {
'',
'--- @alias vim.lsp.protocol.Method',
'--- | vim.lsp.protocol.Method.ClientToServer',
'--- | vim.lsp.protocol.Method.ServerToClient',
'',
'-- Generated by gen_lsp.lua, keep at end of file.',
'---',
'--- @enum vim.lsp.protocol.Methods',
'--- @see https://microsoft.github.io/language-server-protocol/specification/#metaModel',
'--- LSP method names.',
'protocol.Methods = {',
})
for _, item in ipairs(all) do for _, item in ipairs(all) do
if item.method then if item.method then
if item.documentation then if item.documentation then

View File

@ -120,85 +120,6 @@ describe('vim.lsp.diagnostic', function()
end) end)
describe('vim.lsp.diagnostic.on_publish_diagnostics', function() describe('vim.lsp.diagnostic.on_publish_diagnostics', function()
it('allows configuring the virtual text via vim.lsp.with', function()
local expected_spacing = 10
local extmarks = exec_lua(function()
_G.PublishDiagnostics = vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
virtual_text = {
spacing = expected_spacing,
},
})
_G.PublishDiagnostics(nil, {
uri = fake_uri,
diagnostics = {
_G.make_error('Delayed Diagnostic', 4, 4, 4, 4),
},
}, { client_id = client_id })
return _G.get_extmarks(diagnostic_bufnr, client_id)
end)
local spacing = extmarks[1][4].virt_text[1][1]
eq(expected_spacing, #spacing)
end)
it('allows configuring the virtual text via vim.lsp.with using a function', function()
local expected_spacing = 10
local extmarks = exec_lua(function()
_G.PublishDiagnostics = vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
virtual_text = function()
return {
spacing = expected_spacing,
}
end,
})
_G.PublishDiagnostics(nil, {
uri = fake_uri,
diagnostics = {
_G.make_error('Delayed Diagnostic', 4, 4, 4, 4),
},
}, { client_id = client_id })
return _G.get_extmarks(diagnostic_bufnr, client_id)
end)
local spacing = extmarks[1][4].virt_text[1][1]
eq(expected_spacing, #spacing)
end)
it('allows filtering via severity limit', function()
local get_extmark_count_with_severity = function(severity_limit)
return exec_lua(function()
_G.PublishDiagnostics = vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
underline = false,
virtual_text = {
severity = { min = severity_limit },
},
})
_G.PublishDiagnostics(nil, {
uri = fake_uri,
diagnostics = {
_G.make_warning('Delayed Diagnostic', 4, 4, 4, 4),
},
}, { client_id = client_id })
return #_G.get_extmarks(diagnostic_bufnr, client_id)
end, client_id, fake_uri, severity_limit)
end
-- No messages with Error or higher
eq(0, get_extmark_count_with_severity('ERROR'))
-- But now we don't filter it
eq(1, get_extmark_count_with_severity('WARN'))
eq(1, get_extmark_count_with_severity('HINT'))
end)
it('correctly handles UTF-16 offsets', function() it('correctly handles UTF-16 offsets', function()
local line = 'All 💼 and no 🎉 makes Jack a dull 👦' local line = 'All 💼 and no 🎉 makes Jack a dull 👦'
local result = exec_lua(function() local result = exec_lua(function()
@ -380,34 +301,6 @@ describe('vim.lsp.diagnostic', function()
eq(1, diagnostics[1].severity) eq(1, diagnostics[1].severity)
end) end)
it('allows configuring the virtual text via vim.lsp.with', function()
local expected_spacing = 10
local extmarks = exec_lua(function()
_G.Diagnostic = vim.lsp.with(vim.lsp.diagnostic.on_diagnostic, {
virtual_text = {
spacing = expected_spacing,
},
})
_G.Diagnostic(nil, {
kind = 'full',
items = {
_G.make_error('Pull Diagnostic', 4, 4, 4, 4),
},
}, {
params = {
textDocument = { uri = fake_uri },
},
uri = fake_uri,
client_id = client_id,
}, {})
return _G.get_extmarks(diagnostic_bufnr, client_id)
end)
eq(2, #extmarks)
eq(expected_spacing, #extmarks[1][4].virt_text[1][1])
end)
it('clears diagnostics when client detaches', function() it('clears diagnostics when client detaches', function()
exec_lua(function() exec_lua(function()
vim.lsp.diagnostic.on_diagnostic(nil, { vim.lsp.diagnostic.on_diagnostic(nil, {