commit 05bd4398c50706945577ea9d620d8611471b847c
parent 49c19a1fe3cb0f525f457170e6988f60cc5ec73f
Author: Maria Solano <majosolano99@gmail.com>
Date: Mon, 16 Feb 2026 11:05:33 -0800
feat(lsp): support `textDocument/documentLink` (#37644)
Diffstat:
5 files changed, 37 insertions(+), 2 deletions(-)
diff --git 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
@@ -137,7 +137,7 @@ do
{ silent = true, expr = true, desc = ':help v_@-default' }
)
- --- Map |gx| to call |vim.ui.open| on the <cfile> at cursor.
+ --- Map |gx| to call |vim.ui.open| on the `textDocument/documentLink` or <cfile> 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
@@ -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
@@ -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
@@ -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)