From c2d7dd781a8aa5760d98d5cec70b1e3703f0efc6 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Mon, 11 May 2026 01:49:36 +0800 Subject: [PATCH] feat(lsp): pass arbitrary positions to vim.lsp.buf #39446 Problem: Currently, it only supports sending requests using the current cursor position as a parameter. Solution: Support sending requests using arbitrary positions. --- runtime/doc/lsp.txt | 2 ++ runtime/doc/news.txt | 13 +++++++++---- runtime/lua/vim/lsp/buf.lua | 30 ++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 4387629641..40be5881ab 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1447,6 +1447,8 @@ the current buffer. See |setqflist-what| for the structure of the `what` parameter. + • {pos} (`vim.Pos`, default: cursor position) Position on a buffer + to request. *vim.lsp.buf.hover.Opts* Extends: |vim.lsp.util.open_floating_preview.Opts| diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 0490313736..4d463f8a76 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -156,8 +156,12 @@ LSP "preselect". https://microsoft.github.io/language-server-protocol/specification/#completionClientCapabilities • `textDocument/foldingRange` |vim.lsp.foldtext()| highlights collapsed text. https://microsoft.github.io/language-server-protocol/specification/#textDocument_foldingRange -• |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, |vim.lsp.buf.definition()|, - and |vim.lsp.buf.implementation()| now follows 'switchbuf'. +• |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, + |vim.lsp.buf.type_definition()|, and |vim.lsp.buf.implementation()| + now follows 'switchbuf'. +• |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, + |vim.lsp.buf.type_definition()|, and |vim.lsp.buf.implementation()| + now supports passing arbitrary positions. • Support for nested snippets. LUA @@ -262,8 +266,9 @@ REMOVED FEATURES *news-removed* These deprecated features were removed. -• |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, |vim.lsp.buf.definition()|, - and |vim.lsp.buf.implementation()| no longer accept the `reuse_win` option. +• |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, + |vim.lsp.buf.type_definition()|, and |vim.lsp.buf.implementation()| + no longer accept the `reuse_win` option. ============================================================================== DEPRECATIONS *news-deprecations* diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index d69546b700..cc71f13071 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -225,23 +225,29 @@ local function get_locations(method, context, opts) ) end - local bufnr = api.nvim_get_current_buf() - local win = api.nvim_get_current_win() + local pos = opts.pos or vim.pos.cursor(0, api.nvim_win_get_cursor(0)) + local buf = pos.buf - local clients = lsp.get_clients({ method = method, bufnr = bufnr }) + local clients = lsp.get_clients({ method = method, bufnr = buf }) if not next(clients) then vim.notify(lsp._unsupported_method(method), vim.log.levels.WARN) return end local from = vim.fn.getpos('.') - from[1] = bufnr + from[1] = buf local tagname = vim.fn.expand('') - lsp.buf_request_all(bufnr, method, function(client) - local params = util.make_position_params(win, client.offset_encoding) - ---@diagnostic disable-next-line: inject-field - params.context = context or { includeDeclaration = true } + lsp.buf_request_all(buf, method, function(client) + ---@type lsp.TextDocumentPositionParams + local params = { + textDocument = util.make_text_document_params(buf), + position = pos:to_lsp(client.offset_encoding), + } + if method == 'textDocument/references' then + ---@cast params lsp.ReferenceParams + params.context = context or { includeDeclaration = true } + end return params end, function(results) ---@type vim.quickfix.entry[] @@ -267,7 +273,7 @@ local function get_locations(method, context, opts) local what = { title = name:gsub('^%l', string.upper), items = all_items, - context = { bufnr = bufnr, method = method }, + context = { bufnr = buf, method = method }, } if opts.on_list then validate('opts.on_list', opts.on_list, 'function') @@ -285,7 +291,7 @@ local function get_locations(method, context, opts) and method ~= 'textDocument/references' then local tagstack = { { tagname = tagname, from = from } } - vim.fn.settagstack(vim.fn.win_getid(win), { items = tagstack }, 't') + vim.fn.settagstack(vim.fn.bufwinid(buf), { items = tagstack }, 't') if opts.loclist then vim.cmd('lfirst') else @@ -338,6 +344,10 @@ end --- vim.lsp.buf.references(nil, { loclist = false }) --- ``` --- @field loclist? boolean +--- +--- Position on a buffer to request. +--- (default: cursor position) +--- @field pos vim.Pos --- Jumps to the declaration of the symbol under the cursor. --- @note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead.