diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index ce3ac90c3a..2196d256d7 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -4432,6 +4432,16 @@ lsp({buf}, {pos}, {position_encoding}) *vim.pos.lsp()* • {pos} (`lsp.Position`) • {position_encoding} (`lsp.PositionEncodingKind`) +offset({buf}, {offset}) *vim.pos.offset()* + Creates a new |vim.Pos| from buffer offset. + + Parameters: ~ + • {buf} (`integer`) + • {offset} (`integer`) + + Return: ~ + (`vim.Pos`) See |vim.Pos|. + to_cursor({pos}) *vim.pos.to_cursor()* Converts |vim.Pos| to cursor position (see |api-indexing|). @@ -4466,6 +4476,15 @@ to_lsp({pos}, {position_encoding}) *vim.pos.to_lsp()* • {pos} (`vim.Pos`) See |vim.Pos|. • {position_encoding} (`lsp.PositionEncodingKind`) +to_offset({pos}) *vim.pos.to_offset()* + Converts |vim.Pos| to buffer offset. + + Parameters: ~ + • {pos} (`vim.Pos`) See |vim.Pos|. + + Return: ~ + (`integer`) + ============================================================================== Lua module: vim.range *vim.range* diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index d86ad31816..bf67b3fcd0 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -180,6 +180,7 @@ LUA • |vim.nonnil()| returns the first argument which is not nil. • |vim.npcall()| calls the function `fn` in protected-mode like |pcall()|, but returns `nil` on error. +• |vim.pos| can now convert between positions and buffer offsets. OPTIONS diff --git a/runtime/lua/vim/pos.lua b/runtime/lua/vim/pos.lua index a4c0f2d536..1b6e8e6e7f 100644 --- a/runtime/lua/vim/pos.lua +++ b/runtime/lua/vim/pos.lua @@ -225,6 +225,33 @@ function M.extmark(buf, row, col) return M.new(buf, row, col) end +--- Converts |vim.Pos| to buffer offset. +---@param pos vim.Pos +---@return integer +function M.to_offset(pos) + return api.nvim_buf_get_offset(pos.buf, pos[1]) + pos[2] +end + +--- Creates a new |vim.Pos| from buffer offset. +---@param buf integer +---@param offset integer +---@return vim.Pos +function M.offset(buf, offset) + local lnum = vim.list.bisect( + setmetatable({}, { + __index = function(_, lnum) + return api.nvim_buf_get_offset(buf, lnum - 1) + end, + }), + offset, + { lo = 1, hi = api.nvim_buf_line_count(buf) + 2, bound = 'upper' } + ) - 1 + + local row = lnum - 1 + local col = offset - api.nvim_buf_get_offset(buf, row) + return M.new(buf, row, col) +end + -- Overload `Range.new` to allow calling this module as a function. setmetatable(M, { __call = function(_, ...) diff --git a/test/functional/lua/pos_spec.lua b/test/functional/lua/pos_spec.lua index c6d5b87feb..db5975d973 100644 --- a/test/functional/lua/pos_spec.lua +++ b/test/functional/lua/pos_spec.lua @@ -2,6 +2,7 @@ local t = require('test.testutil') local n = require('test.functional.testnvim')() local eq = t.eq +local dedent = t.dedent local clear = n.clear local exec_lua = n.exec_lua @@ -120,4 +121,40 @@ describe('vim.pos', function() end) eq({ 0, 9, buf }, pos2) end) + + it('converts between vim.Pos and buffer offset', function() + local buf = exec_lua(function() + return vim.api.nvim_get_current_buf() + end) + insert(dedent [[ + first + second + third + ]]) + + local offsets = exec_lua(function() + return { + vim.pos(buf, 0, 0):to_offset(), + vim.pos(buf, 0, 3):to_offset(), + vim.pos(buf, 1, 0):to_offset(), + vim.pos(buf, 3, 0):to_offset(), + } + end) + eq({ 0, 3, 6, 19 }, offsets) + + local positions = exec_lua(function() + return { + vim.pos.offset(buf, 0), + vim.pos.offset(buf, 3), + vim.pos.offset(buf, 6), + vim.pos.offset(buf, 19), + } + end) + eq({ + { 0, 0, buf }, + { 0, 3, buf }, + { 1, 0, buf }, + { 3, 0, buf }, + }, positions) + end) end)