handlers.lua (26128B)
1 local log = require('vim.lsp.log') 2 local protocol = require('vim.lsp.protocol') 3 local util = require('vim.lsp.util') 4 local api = vim.api 5 local completion = require('vim.lsp.completion') 6 7 --- @type table<string, lsp.Handler> 8 local M = {} 9 10 --- @deprecated 11 --- Client to server response handlers. 12 --- @type table<vim.lsp.protocol.Method.ClientToServer, lsp.Handler> 13 local RCS = {} 14 15 --- Server to client request handlers. 16 --- @type table<vim.lsp.protocol.Method.ServerToClient, lsp.Handler> 17 local RSC = {} 18 19 --- Server to client notification handlers. 20 --- @type table<vim.lsp.protocol.Method.ServerToClient, lsp.Handler> 21 local NSC = {} 22 23 --- Writes to error buffer. 24 ---@param ... string Will be concatenated before being written 25 local function err_message(...) 26 vim.notify(table.concat(vim.iter({ ... }):flatten():totable()), vim.log.levels.ERROR) 27 api.nvim_command('redraw') 28 end 29 30 --- @param params lsp.ShowMessageParams|lsp.ShowMessageRequestParams 31 --- @param ctx lsp.HandlerContext 32 local function show_message_notification(params, ctx) 33 local message_type = params.type 34 local message = params.message 35 local client_id = ctx.client_id 36 local client = vim.lsp.get_client_by_id(client_id) 37 local client_name = client and client.name or string.format('id=%d', client_id) 38 if not client then 39 err_message('LSP[', client_name, '] client has shut down after sending ', message) 40 end 41 message = ('LSP[%s] %s'):format(client_name, message) 42 vim.notify(message, log._from_lsp_level(message_type)) 43 return params 44 end 45 46 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand 47 RCS['workspace/executeCommand'] = function(_, _, _) 48 -- Error handling is done implicitly by wrapping all handlers; see end of this file 49 end 50 51 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress 52 ---@param params lsp.ProgressParams 53 ---@param ctx lsp.HandlerContext 54 ---@diagnostic disable-next-line:no-unknown 55 RSC['$/progress'] = function(_, params, ctx) 56 local client = vim.lsp.get_client_by_id(ctx.client_id) 57 if not client then 58 err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') 59 return vim.NIL 60 end 61 local kind = nil 62 local value = params.value 63 64 if type(value) == 'table' then 65 kind = value.kind --- @type string 66 -- Carry over title of `begin` messages to `report` and `end` messages 67 -- So that consumers always have it available, even if they consume a 68 -- subset of the full sequence 69 if kind == 'begin' then 70 client.progress.pending[params.token] = value.title 71 else 72 value.title = client.progress.pending[params.token] 73 if kind == 'end' then 74 client.progress.pending[params.token] = nil 75 end 76 end 77 end 78 79 client.progress:push(params) 80 81 api.nvim_exec_autocmds('LspProgress', { 82 pattern = kind, 83 modeline = false, 84 data = { client_id = ctx.client_id, params = params }, 85 }) 86 end 87 88 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create 89 ---@param params lsp.WorkDoneProgressCreateParams 90 ---@param ctx lsp.HandlerContext 91 RSC['window/workDoneProgress/create'] = function(_, params, ctx) 92 local client = vim.lsp.get_client_by_id(ctx.client_id) 93 if not client then 94 err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') 95 return vim.NIL 96 end 97 client.progress:push(params) 98 return vim.NIL 99 end 100 101 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest 102 ---@param params lsp.ShowMessageRequestParams 103 RSC['window/showMessageRequest'] = function(_, params, ctx) 104 if next(params.actions or {}) then 105 local co, is_main = coroutine.running() 106 if co and not is_main then 107 local opts = { 108 kind = 'lsp_message', 109 prompt = params.message .. ': ', 110 ---@param action lsp.MessageActionItem 111 ---@return string 112 format_item = function(action) 113 return (action.title:gsub('\r\n', '\\r\\n'):gsub('\n', '\\n')) 114 end, 115 } 116 vim.ui.select(params.actions, opts, function(choice) 117 -- schedule to ensure resume doesn't happen _before_ yield with 118 -- default synchronous vim.ui.select 119 vim.schedule(function() 120 coroutine.resume(co, choice or vim.NIL) 121 end) 122 end) 123 return coroutine.yield() 124 else 125 local option_strings = { params.message, '\nRequest Actions:' } 126 for i, action in ipairs(params.actions) do 127 local title = action.title:gsub('\r\n', '\\r\\n') 128 title = title:gsub('\n', '\\n') 129 table.insert(option_strings, string.format('%d. %s', i, title)) 130 end 131 local choice = vim.fn.inputlist(option_strings) 132 if choice < 1 or choice > #params.actions then 133 return vim.NIL 134 else 135 return params.actions[choice] 136 end 137 end 138 else 139 -- No actions, just show the message. 140 show_message_notification(params, ctx) 141 return vim.NIL 142 end 143 end 144 145 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability 146 --- @param params lsp.RegistrationParams 147 RSC['client/registerCapability'] = function(_, params, ctx) 148 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 149 client:_register(params.registrations) 150 for bufnr in pairs(client.attached_buffers) do 151 vim.lsp._set_defaults(client, bufnr) 152 end 153 for _, reg in ipairs(params.registrations) do 154 if reg.method == 'textDocument/documentColor' then 155 for bufnr in pairs(client.attached_buffers) do 156 if vim.lsp.document_color.is_enabled(bufnr) then 157 vim.lsp.document_color._buf_refresh(bufnr, client.id) 158 end 159 end 160 end 161 if reg.method == 'textDocument/diagnostic' then 162 for bufnr in pairs(client.attached_buffers) do 163 vim.lsp.diagnostic._refresh(bufnr, client.id) 164 end 165 end 166 end 167 return vim.NIL 168 end 169 170 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability 171 --- @param params lsp.UnregistrationParams 172 RSC['client/unregisterCapability'] = function(_, params, ctx) 173 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 174 client:_unregister(params.unregisterations) 175 return vim.NIL 176 end 177 178 -- TODO(lewis6991): Do we need to notify other servers? 179 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit 180 RSC['workspace/applyEdit'] = function(_, params, ctx) 181 assert( 182 params, 183 'workspace/applyEdit must be called with `ApplyWorkspaceEditParams`. Server is violating the specification' 184 ) 185 -- TODO(ashkan) Do something more with label? 186 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 187 if params.label then 188 print('Workspace edit', params.label) 189 end 190 local status, result = pcall(util.apply_workspace_edit, params.edit, client.offset_encoding) 191 return { 192 applied = status, 193 failureReason = result, 194 } 195 end 196 197 ---@param table table e.g., { foo = { bar = "z" } } 198 ---@param section string indicating the field of the table, e.g., "foo.bar" 199 ---@return any|nil setting value read from the table, or `nil` not found 200 local function lookup_section(table, section) 201 local keys = vim.split(section, '.', { plain = true }) --- @type string[] 202 return vim.tbl_get(table, unpack(keys)) 203 end 204 205 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration 206 --- @param params lsp.ConfigurationParams 207 RSC['workspace/configuration'] = function(_, params, ctx) 208 local client = vim.lsp.get_client_by_id(ctx.client_id) 209 if not client then 210 err_message( 211 'LSP[', 212 ctx.client_id, 213 '] client has shut down after sending a workspace/configuration request' 214 ) 215 return 216 end 217 if not params.items then 218 return {} 219 end 220 221 local response = {} 222 for _, item in ipairs(params.items) do 223 if item.section then 224 local value = lookup_section(client.settings, item.section) 225 -- For empty sections with no explicit '' key, return settings as is 226 if value == nil and item.section == '' then 227 value = client.settings 228 end 229 if value == nil then 230 value = vim.NIL 231 end 232 table.insert(response, value) 233 else 234 -- If no section is provided, return settings as is 235 table.insert(response, client.settings) 236 end 237 end 238 return response 239 end 240 241 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders 242 RSC['workspace/workspaceFolders'] = function(_, _, ctx) 243 local client = vim.lsp.get_client_by_id(ctx.client_id) 244 if not client then 245 err_message('LSP[id=', ctx.client_id, '] client has shut down after sending the message') 246 return 247 end 248 return client.workspace_folders or vim.NIL 249 end 250 251 NSC['textDocument/publishDiagnostics'] = function(...) 252 return vim.lsp.diagnostic.on_publish_diagnostics(...) 253 end 254 255 --- @private 256 RCS['textDocument/diagnostic'] = function(...) 257 return vim.lsp.diagnostic.on_diagnostic(...) 258 end 259 260 --- @private 261 RCS['textDocument/inlayHint'] = function(...) 262 return vim.lsp.inlay_hint.on_inlayhint(...) 263 end 264 265 --- Return a function that converts LSP responses to list items and opens the list 266 --- 267 --- The returned function has an optional {config} parameter that accepts |vim.lsp.ListOpts| 268 --- 269 ---@param map_result fun(resp: any, bufnr: integer, position_encoding: 'utf-8'|'utf-16'|'utf-32'): table to convert the response 270 ---@param entity string name of the resource used in a `not found` error message 271 ---@param title_fn fun(ctx: lsp.HandlerContext): string Function to call to generate list title 272 ---@return lsp.Handler 273 local function response_to_list(map_result, entity, title_fn) 274 --- @diagnostic disable-next-line:redundant-parameter 275 return function(_, result, ctx, config) 276 if not result or vim.tbl_isempty(result) then 277 vim.notify('No ' .. entity .. ' found') 278 return 279 end 280 config = config or {} 281 local title = title_fn(ctx) 282 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 283 local items = map_result(result, ctx.bufnr, client.offset_encoding) 284 285 local list = { title = title, items = items, context = ctx } 286 if config.on_list then 287 assert(vim.is_callable(config.on_list), 'on_list is not a function') 288 config.on_list(list) 289 elseif config.loclist then 290 vim.fn.setloclist(0, {}, ' ', list) 291 vim.cmd.lopen() 292 else 293 vim.fn.setqflist({}, ' ', list) 294 vim.cmd('botright copen') 295 end 296 end 297 end 298 299 --- @deprecated remove in 0.13 300 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol 301 RCS['textDocument/documentSymbol'] = response_to_list( 302 util.symbols_to_items, 303 'document symbols', 304 function(ctx) 305 local fname = vim.fn.fnamemodify(vim.uri_to_fname(ctx.params.textDocument.uri), ':.') 306 return string.format('Symbols in %s', fname) 307 end 308 ) 309 310 --- @deprecated remove in 0.13 311 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol 312 RCS['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols', function(ctx) 313 return string.format("Symbols matching '%s'", ctx.params.query) 314 end) 315 316 --- @deprecated remove in 0.13 317 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename 318 RCS['textDocument/rename'] = function(_, result, ctx) 319 if not result then 320 vim.notify("Language server couldn't provide rename result", vim.log.levels.INFO) 321 return 322 end 323 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 324 util.apply_workspace_edit(result, client.offset_encoding) 325 end 326 327 --- @deprecated remove in 0.13 328 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting 329 RCS['textDocument/rangeFormatting'] = function(_, result, ctx) 330 if not result then 331 return 332 end 333 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 334 util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) 335 end 336 337 --- @deprecated remove in 0.13 338 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting 339 RCS['textDocument/formatting'] = function(_, result, ctx) 340 if not result then 341 return 342 end 343 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 344 util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) 345 end 346 347 --- @deprecated remove in 0.13 348 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion 349 RCS['textDocument/completion'] = function(_, result, _) 350 if vim.tbl_isempty(result or {}) then 351 return 352 end 353 local cursor = api.nvim_win_get_cursor(0) 354 local row, col = cursor[1], cursor[2] 355 local line = assert(api.nvim_buf_get_lines(0, row - 1, row, false)[1]) 356 local line_to_cursor = line:sub(col + 1) 357 local textMatch = vim.fn.match(line_to_cursor, '\\k*$') 358 local prefix = line_to_cursor:sub(textMatch + 1) 359 360 local matches = completion._lsp_to_complete_items(result, prefix) 361 vim.fn.complete(textMatch + 1, matches) 362 end 363 364 --- @deprecated 365 --- |lsp-handler| for the method "textDocument/hover" 366 --- 367 --- ```lua 368 --- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with( 369 --- vim.lsp.handlers.hover, { 370 --- -- Use a sharp border with `FloatBorder` highlights 371 --- border = "single", 372 --- -- add the title in hover float window 373 --- title = "hover" 374 --- } 375 --- ) 376 --- ``` 377 --- 378 ---@param _ lsp.ResponseError? 379 ---@param result lsp.Hover 380 ---@param ctx lsp.HandlerContext 381 ---@param config table Configuration table. 382 --- - border: (default=nil) 383 --- - Add borders to the floating window 384 --- - See |vim.lsp.util.open_floating_preview()| for more options. 385 --- @diagnostic disable-next-line:redundant-parameter 386 function M.hover(_, result, ctx, config) 387 config = config or {} 388 config.focus_id = ctx.method 389 if api.nvim_get_current_buf() ~= ctx.bufnr then 390 -- Ignore result since buffer changed. This happens for slow language servers. 391 return 392 end 393 if not (result and result.contents) then 394 if config.silent ~= true then 395 vim.notify('No information available') 396 end 397 return 398 end 399 local format = 'markdown' 400 local contents ---@type string[] 401 if type(result.contents) == 'table' and result.contents.kind == 'plaintext' then 402 format = 'plaintext' 403 contents = vim.split(result.contents.value or '', '\n', { trimempty = true }) 404 else 405 contents = util.convert_input_to_markdown_lines(result.contents) 406 end 407 if vim.tbl_isempty(contents) then 408 if config.silent ~= true then 409 vim.notify('No information available') 410 end 411 return 412 end 413 return util.open_floating_preview(contents, format, config) 414 end 415 416 --- @deprecated remove in 0.13 417 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover 418 --- @diagnostic disable-next-line: deprecated 419 RCS['textDocument/hover'] = M.hover 420 421 local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help') 422 423 --- @deprecated remove in 0.13 424 --- |lsp-handler| for the method "textDocument/signatureHelp". 425 --- 426 --- The active parameter is highlighted with |hl-LspSignatureActiveParameter|. 427 --- 428 --- ```lua 429 --- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with( 430 --- vim.lsp.handlers.signature_help, { 431 --- -- Use a sharp border with `FloatBorder` highlights 432 --- border = "single" 433 --- } 434 --- ) 435 --- ``` 436 --- 437 ---@param _ lsp.ResponseError? 438 ---@param result lsp.SignatureHelp? Response from the language server 439 ---@param ctx lsp.HandlerContext Client context 440 ---@param config table Configuration table. 441 --- - border: (default=nil) 442 --- - Add borders to the floating window 443 --- - See |vim.lsp.util.open_floating_preview()| for more options 444 --- @diagnostic disable-next-line:redundant-parameter 445 function M.signature_help(_, result, ctx, config) 446 config = config or {} 447 config.focus_id = ctx.method 448 if api.nvim_get_current_buf() ~= ctx.bufnr then 449 -- Ignore result since buffer changed. This happens for slow language servers. 450 return 451 end 452 -- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler 453 -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore 454 if not (result and result.signatures and result.signatures[1]) then 455 if config.silent ~= true then 456 print('No signature help available') 457 end 458 return 459 end 460 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 461 local triggers = 462 vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters') 463 local ft = vim.bo[ctx.bufnr].filetype 464 local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) 465 if not lines or vim.tbl_isempty(lines) then 466 if config.silent ~= true then 467 print('No signature help available') 468 end 469 return 470 end 471 local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config) 472 -- Highlight the active parameter. 473 if hl then 474 vim.hl.range( 475 fbuf, 476 sig_help_ns, 477 'LspSignatureActiveParameter', 478 { hl[1], hl[2] }, 479 { hl[3], hl[4] } 480 ) 481 end 482 return fbuf, fwin 483 end 484 485 --- @deprecated remove in 0.13 486 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp 487 --- @diagnostic disable-next-line:deprecated 488 RCS['textDocument/signatureHelp'] = M.signature_help 489 490 --- @deprecated remove in 0.13 491 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight 492 RCS['textDocument/documentHighlight'] = function(_, result, ctx) 493 if not result then 494 return 495 end 496 local client_id = ctx.client_id 497 local client = vim.lsp.get_client_by_id(client_id) 498 if not client then 499 return 500 end 501 util.buf_highlight_references(ctx.bufnr, result, client.offset_encoding) 502 end 503 504 --- Displays call hierarchy in the quickfix window. 505 --- 506 --- @param direction 'from'|'to' `"from"` for incoming calls and `"to"` for outgoing calls 507 --- @overload fun(direction:'from'): fun(_, result: lsp.CallHierarchyIncomingCall[]?) 508 --- @overload fun(direction:'to'): fun(_, result: lsp.CallHierarchyOutgoingCall[]?) 509 local function make_call_hierarchy_handler(direction) 510 --- @param result lsp.CallHierarchyIncomingCall[]|lsp.CallHierarchyOutgoingCall[] 511 return function(_, result) 512 if not result then 513 return 514 end 515 local items = {} 516 for _, call_hierarchy_call in pairs(result) do 517 --- @type lsp.CallHierarchyItem 518 local call_hierarchy_item = call_hierarchy_call[direction] 519 for _, range in pairs(call_hierarchy_call.fromRanges) do 520 table.insert(items, { 521 filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)), 522 text = call_hierarchy_item.name, 523 lnum = range.start.line + 1, 524 col = range.start.character + 1, 525 }) 526 end 527 end 528 vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items }) 529 vim.cmd('botright copen') 530 end 531 end 532 533 --- @deprecated remove in 0.13 534 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_incomingCalls 535 RCS['callHierarchy/incomingCalls'] = make_call_hierarchy_handler('from') 536 537 --- @deprecated remove in 0.13 538 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_outgoingCalls 539 RCS['callHierarchy/outgoingCalls'] = make_call_hierarchy_handler('to') 540 541 --- Displays type hierarchy in the quickfix window. 542 local function make_type_hierarchy_handler() 543 --- @param result lsp.TypeHierarchyItem[] 544 return function(_, result, ctx, _) 545 if not result then 546 return 547 end 548 local function format_item(item) 549 if not item.detail or #item.detail == 0 then 550 return item.name 551 end 552 return string.format('%s %s', item.name, item.detail) 553 end 554 local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) 555 local items = {} 556 for _, type_hierarchy_item in pairs(result) do 557 local col = util._get_line_byte_from_position( 558 ctx.bufnr, 559 type_hierarchy_item.range.start, 560 client.offset_encoding 561 ) 562 table.insert(items, { 563 filename = assert(vim.uri_to_fname(type_hierarchy_item.uri)), 564 text = format_item(type_hierarchy_item), 565 lnum = type_hierarchy_item.range.start.line + 1, 566 col = col + 1, 567 }) 568 end 569 vim.fn.setqflist({}, ' ', { title = 'LSP type hierarchy', items = items }) 570 vim.cmd('botright copen') 571 end 572 end 573 574 --- @deprecated remove in 0.13 575 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_incomingCalls 576 RCS['typeHierarchy/subtypes'] = make_type_hierarchy_handler() 577 578 --- @deprecated remove in 0.13 579 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_outgoingCalls 580 RCS['typeHierarchy/supertypes'] = make_type_hierarchy_handler() 581 582 --- @see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage 583 --- @param params lsp.LogMessageParams 584 NSC['window/logMessage'] = function(_, params, ctx) 585 local message_type = params.type 586 local message = params.message 587 local client_id = ctx.client_id 588 local client = vim.lsp.get_client_by_id(client_id) 589 local client_name = client and client.name or string.format('id=%d', client_id) 590 if not client then 591 err_message('LSP[', client_name, '] client has shut down after sending ', message) 592 end 593 if message_type == protocol.MessageType.Error then 594 log.error(message) 595 elseif message_type == protocol.MessageType.Warning then 596 log.warn(message) 597 elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then 598 log.info(message) 599 else 600 log.debug(message) 601 end 602 return params 603 end 604 605 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessage 606 --- @param params lsp.ShowMessageParams 607 NSC['window/showMessage'] = function(_, params, ctx) 608 return show_message_notification(params, ctx) 609 end 610 611 --- @private 612 --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showDocument 613 --- @param params lsp.ShowDocumentParams 614 RSC['window/showDocument'] = function(_, params, ctx) 615 local uri = params.uri 616 617 if params.external then 618 -- TODO(lvimuser): ask the user for confirmation 619 local cmd, err = vim.ui.open(uri) 620 local ret = cmd and cmd:wait(2000) or nil 621 622 if ret == nil or ret.code ~= 0 then 623 return { 624 success = false, 625 error = { 626 code = protocol.ErrorCodes.UnknownErrorCode, 627 message = ret and ret.stderr or err, 628 }, 629 } 630 end 631 632 return { success = true } 633 end 634 635 local client_id = ctx.client_id 636 local client = vim.lsp.get_client_by_id(client_id) 637 local client_name = client and client.name or string.format('id=%d', client_id) 638 if not client then 639 err_message('LSP[', client_name, '] client has shut down after sending ', ctx.method) 640 return vim.NIL 641 end 642 643 local location = { 644 uri = uri, 645 range = params.selection, 646 } 647 648 local success = util.show_document(location, client.offset_encoding, { 649 reuse_win = true, 650 focus = params.takeFocus, 651 }) 652 return { success = success or false } 653 end 654 655 ---@see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeLens_refresh 656 RSC['workspace/codeLens/refresh'] = function(err, result, ctx) 657 return vim.lsp.codelens.on_refresh(err, result, ctx) 658 end 659 660 ---@see https://microsoft.github.io/language-server-protocol/specification/#diagnostic_refresh 661 RSC['workspace/diagnostic/refresh'] = function(err, result, ctx) 662 return vim.lsp.diagnostic.on_refresh(err, result, ctx) 663 end 664 665 ---@see https://microsoft.github.io/language-server-protocol/specification/#workspace_inlayHint_refresh 666 RSC['workspace/inlayHint/refresh'] = function(err, result, ctx) 667 return vim.lsp.inlay_hint.on_refresh(err, result, ctx) 668 end 669 670 ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#semanticTokens_refreshRequest 671 RSC['workspace/semanticTokens/refresh'] = function(err, result, ctx) 672 return vim.lsp.semantic_tokens._refresh(err, result, ctx) 673 end 674 675 --- @nodoc 676 --- @type table<string, lsp.Handler> 677 M = vim.tbl_extend('force', M, RSC, NSC, RCS) 678 679 -- Add boilerplate error validation and logging for all of these. 680 for k, fn in pairs(M) do 681 --- @diagnostic disable-next-line:redundant-parameter 682 M[k] = function(err, result, ctx, config) 683 if log.trace() then 684 log.trace('default_handler', ctx.method, { 685 err = err, 686 result = result, 687 ctx = vim.inspect(ctx), 688 }) 689 end 690 691 -- ServerCancelled errors should be propagated to the request handler 692 if err and err.code ~= protocol.ErrorCodes.ServerCancelled then 693 -- LSP spec: 694 -- interface ResponseError: 695 -- code: integer; 696 -- message: string; 697 -- data?: string | number | boolean | array | object | null; 698 699 -- Per LSP, don't show ContentModified error to the user. 700 if err.code ~= protocol.ErrorCodes.ContentModified then 701 local client = vim.lsp.get_client_by_id(ctx.client_id) 702 local client_name = client and client.name or string.format('client_id=%d', ctx.client_id) 703 704 err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message) 705 end 706 return 707 end 708 709 --- @diagnostic disable-next-line:redundant-parameter 710 return fn(err, result, ctx, config) 711 end 712 end 713 714 return M