feat(pos,range): pos:to_mark(), pos.mark(), range:to_mark(), range.mark()

Problem:
Ranges represented by marks are usually end-inclusive,
but the range utilities we provided are end-exclusive.

Solution:
Add pos:to_mark(), pos.mark(), range:to_mark(), and range.mark().
This commit is contained in:
Yi Ming
2026-05-18 17:08:19 +08:00
parent d7164eced6
commit f2b031136f
4 changed files with 153 additions and 0 deletions

View File

@@ -4539,6 +4539,14 @@ lsp({buf}, {pos}, {position_encoding}) *vim.pos.lsp()*
• {pos} (`lsp.Position`)
• {position_encoding} (`lsp.PositionEncodingKind`)
mark({buf}, {row}, {col}) *vim.pos.mark()*
Creates a new |vim.Pos| from mark position (see |api-indexing|).
Parameters: ~
• {buf} (`integer`)
• {row} (`integer`)
• {col} (`integer`)
offset({buf}, {offset}) *vim.pos.offset()*
Creates a new |vim.Pos| from buffer offset.
@@ -4583,6 +4591,16 @@ to_lsp({pos}, {position_encoding}) *vim.pos.to_lsp()*
• {pos} (`vim.Pos`) See |vim.Pos|.
• {position_encoding} (`lsp.PositionEncodingKind`)
to_mark({pos}) *vim.pos.to_mark()*
Converts |vim.Pos| to mark position (see |api-indexing|).
Parameters: ~
• {pos} (`vim.Pos`) See |vim.Pos|.
Return (multiple): ~
(`integer`)
(`integer`)
to_offset({pos}) *vim.pos.to_offset()*
Converts |vim.Pos| to buffer offset.
@@ -4719,6 +4737,26 @@ lsp({buf}, {range}, {position_encoding}) *vim.range.lsp()*
• {range} (`lsp.Range`)
• {position_encoding} (`lsp.PositionEncodingKind`)
*vim.range.mark()*
mark({buf}, {start_row}, {start_col}, {end_row}, {end_col})
Creates a new |vim.Range| from "mark-indexed" range (see |api-indexing|).
Example: >lua
-- A range represented by marks may be end-inclusive (decided by 'selection' option).
local start_row, start_col = unpack(api.nvim_buf_get_mark(bufnr, '<'))
local end_row, end_col = unpack(api.nvim_buf_get_mark(bufnr, '>'))
-- Create an end-exclusive range.
local range = vim.range.mark(0, start_row, start_col, end_row, end_col)
<
Parameters: ~
• {buf} (`integer`)
• {start_row} (`integer`)
• {start_col} (`integer`)
• {end_row} (`integer`)
• {end_col} (`integer`)
to_cursor({range}) *vim.range.to_cursor()*
Converts |vim.Range| to mark-like range (see |api-indexing|).
@@ -4762,6 +4800,25 @@ to_lsp({range}, {position_encoding}) *vim.range.to_lsp()*
Return: ~
(`lsp.Range`)
to_mark({range}) *vim.range.to_mark()*
Converts |vim.Range| to extmark range (see |api-indexing|).
Example: >lua
local range = vim.range(0, 3, 5, 4, 0)
-- Convert to mark range, you can call it in a method style.
local start_row, start_col, end_row, end_col = range:to_mark()
<
Parameters: ~
• {range} (`vim.Range`) See |vim.Range|.
Return (multiple): ~
(`integer`)
(`integer`)
(`integer`)
(`integer`)
==============================================================================
Lua module: vim.re *vim.re*

View File

@@ -203,6 +203,7 @@ LUA
• |vim.log| for easily creating loggers.
• |vim.pack.get()| output includes revision of a pending update.
• |vim.pack.get()| can fetch new updates before computing the output.
• |vim.pos| and |vim.range| can now convert between mark positions.
OPTIONS

View File

@@ -291,6 +291,25 @@ function M.cursor(buf, pos)
return M.new(buf, pos[1] - 1, pos[2])
end
--- Converts |vim.Pos| to mark position (see |api-indexing|).
---@param pos vim.Pos
---@return integer, integer
function M.to_mark(pos)
return pos[1] + 1, pos[2]
end
--- Creates a new |vim.Pos| from mark position (see |api-indexing|).
---@param buf integer
---@param row integer
---@param col integer
function M.mark(buf, row, col)
if buf == 0 then
buf = api.nvim_get_current_buf()
end
return M.new(buf, row - 1, col)
end
--- Converts |vim.Pos| to extmark position (see |api-indexing|).
---@param pos vim.Pos
---@return integer, integer

View File

@@ -160,6 +160,21 @@ local function to_inclusive_pos(buf, row, col)
return row, col
end
---@param row integer
---@param col integer
---@param buf integer
---@return integer, integer
local function to_exclusive_pos(buf, row, col)
if col >= #get_line(buf, row) then
row = row + 1
col = 0
else
col = col + 1
end
return row, col
end
---@private
---@param r1 vim.Range
---@param r2 vim.Range
@@ -314,6 +329,67 @@ function M.lsp(buf, range, position_encoding)
return M.new(start, end_)
end
--- Converts |vim.Range| to extmark range (see |api-indexing|).
---
--- Example:
--- ```lua
--- local range = vim.range(0, 3, 5, 4, 0)
---
--- -- Convert to mark range, you can call it in a method style.
--- local start_row, start_col, end_row, end_col = range:to_mark()
--- ```
---@param range vim.Range
---@return integer, integer, integer, integer
function M.to_mark(range)
validate('range', range, 'table')
local buf = range.buf
local start_row, start_col, end_row, end_col = range[1], range[2], range[3], range[4]
if vim.o.selection ~= 'exclusive' then
end_row, end_col = to_inclusive_pos(buf, end_row, end_col)
end
start_row, start_col = vim.pos(buf, start_row, start_col):to_mark()
end_row, end_col = vim.pos(buf, end_row, end_col):to_mark()
return start_row, start_col, end_row, end_col
end
--- Creates a new |vim.Range| from "mark-indexed" range (see |api-indexing|).
---
--- Example:
--- ```lua
--- -- A range represented by marks may be end-inclusive (decided by 'selection' option).
--- local start_row, start_col = unpack(api.nvim_buf_get_mark(bufnr, '<'))
--- local end_row, end_col = unpack(api.nvim_buf_get_mark(bufnr, '>'))
---
--- -- Create an end-exclusive range.
--- local range = vim.range.mark(0, start_row, start_col, end_row, end_col)
--- ```
---@param buf integer
---@param start_row integer
---@param start_col integer
---@param end_row integer
---@param end_col integer
function M.mark(buf, start_row, start_col, end_row, end_col)
validate('buf', buf, 'number')
validate('start_row', start_row, 'number')
validate('start_col', start_col, 'number')
validate('end_row', end_row, 'number')
validate('end_col', end_col, 'number')
if buf == 0 then
buf = api.nvim_get_current_buf()
end
local start = vim.pos.mark(buf, start_row, start_col)
local end_ = vim.pos.mark(buf, end_row, end_col)
if vim.o.selection ~= 'exclusive' then
end_[1], end_[2] = to_exclusive_pos(buf, end_[1], end_[2])
end
return M.new(start, end_)
end
--- Converts |vim.Range| to extmark range (see |api-indexing|).
---
--- Example: