From 05bd4398c50706945577ea9d620d8611471b847c Mon Sep 17 00:00:00 2001 From: Maria Solano Date: Mon, 16 Feb 2026 11:05:33 -0800 Subject: [PATCH] feat(lsp): support `textDocument/documentLink` (#37644) --- runtime/doc/news.txt | 1 + runtime/lua/vim/_core/defaults.lua | 2 +- runtime/lua/vim/lsp/protocol.lua | 4 ++++ runtime/lua/vim/ui.lua | 27 ++++++++++++++++++++++++ test/functional/ui/statuscolumn_spec.lua | 5 ++++- 5 files changed, 37 insertions(+), 2 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 03e852779d..0213d4c48b 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -307,6 +307,7 @@ LSP • Code lenses now display as virtual lines • Support for `workspace/codeLens/refresh`: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeLens_refresh +• `gx` will use `textDocument/documentLink` if available. LUA diff --git a/runtime/lua/vim/_core/defaults.lua b/runtime/lua/vim/_core/defaults.lua index a971dca16a..61cd412890 100644 --- a/runtime/lua/vim/_core/defaults.lua +++ b/runtime/lua/vim/_core/defaults.lua @@ -137,7 +137,7 @@ do { silent = true, expr = true, desc = ':help v_@-default' } ) - --- Map |gx| to call |vim.ui.open| on the at cursor. + --- Map |gx| to call |vim.ui.open| on the `textDocument/documentLink` or at cursor. do local function do_open(uri) local cmd, err = vim.ui.open(uri) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 979acb5db8..b9a77c6689 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -508,6 +508,10 @@ function protocol.make_client_capabilities() linkSupport = true, dynamicRegistration = true, }, + documentLink = { + dynamicRegistration = false, + tooltipSupport = false, + }, implementation = { linkSupport = true, }, diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index 5a2cb662ef..5ebff4beb0 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -213,6 +213,33 @@ function M._get_urls() local cursor = vim.api.nvim_win_get_cursor(0) local row = cursor[1] - 1 local col = cursor[2] + + -- Find LSP document links under the cursor. + local params = { textDocument = vim.lsp.util.make_text_document_params(bufnr) } + local results = vim.lsp.buf_request_sync(bufnr, 'textDocument/documentLink', params) + + for client_id, result in pairs(results or {}) do + if result.error then + vim.lsp.log.error(result.error) + else + local client = assert(vim.lsp.get_client_by_id(client_id)) + local lsp_position = vim.lsp.util.make_position_params(0, client.offset_encoding).position + local position = vim.pos.lsp(bufnr, lsp_position, client.offset_encoding) + + local document_links = result.result or {} ---@type lsp.DocumentLink[] + for _, document_link in ipairs(document_links) do + local range = vim.range.lsp(bufnr, document_link.range, client.offset_encoding) + if document_link.target and range:has(position) then + local target = document_link.target ---@type string + if vim.startswith(target, 'file://') then + target = vim.uri_to_fname(target) + end + table.insert(urls, target) + end + end + end + end + local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, -1, { row, col }, { row, col }, { details = true, type = 'highlight', diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index 2004606b63..33d04e2e56 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -731,7 +731,10 @@ describe('statuscolumn', function() api.nvim_input_mouse('left', 'press', '', 0, 5, 8) eq('', eval('g:testvar')) api.nvim_input_mouse('right', 'press', '', 0, 6, 4) - eq('0 1 r 10', eval('g:testvar')) + -- Wait for the synchronous call of `textDocument/documentLink` + t.retry(nil, 1500, function() + eq('0 1 r 10', eval('g:testvar')) + end) api.nvim_input_mouse('left', 'press', '', 0, 7, 7) eq('0 1 l 11', eval('g:testvar')) end)