From 2bd13177b83a158f2e8a00e7aeac5ffe793e0923 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Tue, 2 Jun 2026 20:06:08 +0800 Subject: [PATCH 1/2] feat(pos): create a cursor position by using the current of a window Problem: `vim.pos.cursor(vim.api.nvim_get_current_buf(win), vim.api.nvim_win_get_cursor(win))` is too verbose to create a cursor position of a window, but it is a common use case. Solution: Overload `vim.pos.cursor()`, so that it accepts `win` as an argument when `pos` is omitted. --- runtime/doc/lua.txt | 11 ++++++++++- runtime/lua/vim/lsp/buf.lua | 4 ++-- runtime/lua/vim/lsp/codelens.lua | 2 +- runtime/lua/vim/lsp/util.lua | 5 ++--- runtime/lua/vim/pos.lua | 28 +++++++++++++++++++++++----- test/functional/lua/pos_spec.lua | 9 +++++++++ 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 6effa6e7c3..c983c5798a 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -4503,15 +4503,24 @@ by |vim.Pos| objects. cursor({buf}, {pos}) *vim.pos.cursor()* Creates a new |vim.Pos| from cursor position (see |api-indexing|). + If {pos} is omitted, the first argument is treated as {win} instead of + {buf}, and the current cursor position of {win} is used. + Example: >lua + local buf = vim.api.nvim_win_get_buf(0) local cursor_pos = vim.api.nvim_win_get_cursor(0) - local pos = vim.pos.cursor(0, cursor_pos) + local pos = vim.pos.cursor(buf, cursor_pos) + -- This is equivalent: + local pos = vim.pos.cursor(0) < Parameters: ~ • {buf} (`integer`) • {pos} (`[integer, integer]`) (lnum, col) tuple + Overloads: ~ + • `fun(win: integer): vim.Pos` + Return: ~ (`vim.Pos`) See |vim.Pos|. diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 2eef37458e..c2dccdcb00 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -51,7 +51,7 @@ local function ctx_is_valid(ctx) return false end - local cur_pos = vim.pos.cursor(bufnr, api.nvim_win_get_cursor(0)) + local cur_pos = vim.pos.cursor(0) local pos = vim.pos.lsp(bufnr, p, enc) return cur_pos == pos end @@ -222,7 +222,7 @@ local function get_locations(method, context, opts) ) end - local pos = opts.pos or vim.pos.cursor(0, api.nvim_win_get_cursor(0)) + local pos = opts.pos or vim.pos.cursor(0) local buf = pos.buf local clients = lsp.get_clients({ method = method, bufnr = buf }) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 1846d0f752..b2560fa69b 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -490,7 +490,7 @@ function M.run(opts) local winid = api.nvim_get_current_win() local bufnr = api.nvim_win_get_buf(winid) - local pos = vim.pos.cursor(bufnr, api.nvim_win_get_cursor(winid)) + local pos = vim.pos.cursor(winid) local params = { textDocument = vim.lsp.util.make_text_document_params(bufnr), } diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 0b5e978323..4b05228b98 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1810,10 +1810,9 @@ end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(win, position_encoding) win = win or 0 - local buf = api.nvim_win_get_buf(win) return { - textDocument = M.make_text_document_params(buf), - position = vim.pos.cursor(buf, api.nvim_win_get_cursor(win)):to_lsp(position_encoding), + textDocument = M.make_text_document_params(api.nvim_win_get_buf(win)), + position = vim.pos.cursor(win):to_lsp(position_encoding), } end diff --git a/runtime/lua/vim/pos.lua b/runtime/lua/vim/pos.lua index 599bd3ab33..0cf5cc9e7c 100644 --- a/runtime/lua/vim/pos.lua +++ b/runtime/lua/vim/pos.lua @@ -165,20 +165,38 @@ end --- Creates a new |vim.Pos| from cursor position (see |api-indexing|). --- +--- If {pos} is omitted, the first argument is treated as {win} instead of {buf}, +--- and the current cursor position of {win} is used. +--- --- Example: --- ```lua +--- local buf = vim.api.nvim_win_get_buf(0) --- local cursor_pos = vim.api.nvim_win_get_cursor(0) ---- local pos = vim.pos.cursor(0, cursor_pos) +--- local pos = vim.pos.cursor(buf, cursor_pos) +--- -- This is equivalent: +--- local pos = vim.pos.cursor(0) --- ``` ---@param buf integer ---@param pos [integer, integer] (lnum, col) tuple ---@return vim.Pos +---@overload fun(win: integer): vim.Pos function M.cursor(buf, pos) - validate('buf', buf, 'number') - validate('pos', pos, 'table') + validate('pos', pos, 'table', true) - if buf == 0 then - buf = api.nvim_get_current_buf() + if pos then + validate('buf', buf, 'number') + if buf == 0 then + buf = api.nvim_get_current_buf() + end + else + local win = buf + validate('win', win, 'number') + if win == 0 then + win = api.nvim_get_current_win() + end + + buf = api.nvim_win_get_buf(win) + pos = api.nvim_win_get_cursor(win) end return M.new(buf, util.from_mark(pos[1], pos[2])) diff --git a/test/functional/lua/pos_spec.lua b/test/functional/lua/pos_spec.lua index 6805b0f308..1cd33de60e 100644 --- a/test/functional/lua/pos_spec.lua +++ b/test/functional/lua/pos_spec.lua @@ -30,6 +30,15 @@ describe('vim.pos', function() eq(buf, pos[3]) end) + it('creates a position from the window cursor', function() + local pos, buf = exec_lua(function() + vim.api.nvim_buf_set_lines(0, 0, -1, true, { 'first', 'second' }) + vim.api.nvim_win_set_cursor(0, { 2, 3 }) + return vim.pos.cursor(0), vim.api.nvim_get_current_buf() + end) + eq({ 1, 3, buf }, pos) + end) + it('comparisons by overloaded operators', function() local buf = exec_lua(function() return vim.api.nvim_create_buf(false, true) From f79999270ed30c85a13e1f06c845a82939918a28 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Tue, 2 Jun 2026 19:13:26 +0800 Subject: [PATCH 2/2] feat(pos)!: match return value of `to_cursor()` with parameters of `cursor()` Problem: - `pos.cursor()` receives a table contains `row` and `col` as arguments, but `pos:to_cursor` returns `row` and `col` directly without wrapping them in a table. - `pos.mark()`/`pos:to_mark` can already handle unpacked `row` `col`. Solution: Make `pos:to_cursor()` return a `row` and `col` in a table. --- runtime/doc/lua.txt | 7 +++---- runtime/doc/news.txt | 2 ++ runtime/lua/vim/pos.lua | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index c983c5798a..0bb1480387 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -4603,16 +4603,15 @@ to_cursor({pos}) *vim.pos.to_cursor()* local pos = vim.pos(0, 3, 5) -- Convert to cursor position, you can call it in a method style. - local cursor_pos = { pos:to_cursor() } + local cursor_pos = pos:to_cursor() vim.api.nvim_win_set_cursor(0, cursor_pos) < Parameters: ~ • {pos} (`vim.Pos`) See |vim.Pos|. - Return (multiple): ~ - (`integer`) lnum - (`integer`) col + Return: ~ + (`[integer, integer]`) (lnum, col) tuple to_extmark({pos}) *vim.pos.to_extmark()* Converts |vim.Pos| to extmark position (see |api-indexing|). diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 7bea2f2cad..d1bcbf3fca 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -35,6 +35,8 @@ LUA • vim.pos, vim.range always require the `buf` parameter. • range.cursor() and range.to_cursor() are removed. Use range.mark() and range.to_mark() instead. +• pos.to_cursor() returns a (`row,` `col)` tuple + instead of returning them as separate values. DIAGNOSTICS diff --git a/runtime/lua/vim/pos.lua b/runtime/lua/vim/pos.lua index 0cf5cc9e7c..9f1f61b446 100644 --- a/runtime/lua/vim/pos.lua +++ b/runtime/lua/vim/pos.lua @@ -153,14 +153,14 @@ end --- local pos = vim.pos(0, 3, 5) --- --- -- Convert to cursor position, you can call it in a method style. ---- local cursor_pos = { pos:to_cursor() } +--- local cursor_pos = pos:to_cursor() --- vim.api.nvim_win_set_cursor(0, cursor_pos) --- ``` ---@param pos vim.Pos ----@return integer lnum, integer col +---@return [integer, integer] (lnum, col) tuple function M.to_cursor(pos) validate('pos', pos, 'table') - return util.to_mark(pos[1], pos[2]) + return { util.to_mark(pos[1], pos[2]) } end --- Creates a new |vim.Pos| from cursor position (see |api-indexing|).