From f2b031136ff607866a9ab7933408aa86eb8b99a7 Mon Sep 17 00:00:00 2001 From: Yi Ming Date: Mon, 18 May 2026 17:08:19 +0800 Subject: 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(). --- runtime/lua/vim/range.lua | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) (limited to 'runtime/lua/vim/range.lua') diff --git a/runtime/lua/vim/range.lua b/runtime/lua/vim/range.lua index f50aff60b4..dd22d1151e 100644 --- a/runtime/lua/vim/range.lua +++ b/runtime/lua/vim/range.lua @@ -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: -- cgit