add Range:difference
All checks were successful
NeoVim tests / plenary-tests (push) Successful in 9s

This commit is contained in:
2025-04-13 14:44:28 -06:00
parent dacb186324
commit 9f5fdb4f2b
4 changed files with 101 additions and 50 deletions

View File

@@ -12,8 +12,17 @@ end
--- @field col number 1-based column index
--- @field off number
local Pos = {}
Pos.__index = Pos
Pos.MAX_COL = MAX_COL
function Pos.__tostring(self)
if self.off ~= 0 then
return string.format('Pos(%d:%d){bufnr=%d, off=%d}', self.lnum, self.col, self.bufnr, self.off)
else
return string.format('Pos(%d:%d){bufnr=%d}', self.lnum, self.col, self.bufnr)
end
end
--- @param bufnr? number
--- @param lnum number 1-based
--- @param col number 1-based
@@ -28,31 +37,13 @@ function Pos.new(bufnr, lnum, col, off)
col = col,
off = off,
}
local function str()
if pos.off ~= 0 then
return string.format('Pos(%d:%d){bufnr=%d, off=%d}', pos.lnum, pos.col, pos.bufnr, pos.off)
else
return string.format('Pos(%d:%d){bufnr=%d}', pos.lnum, pos.col, pos.bufnr)
end
end
setmetatable(pos, {
__index = Pos,
__tostring = str,
__lt = Pos.__lt,
__le = Pos.__le,
__eq = Pos.__eq,
})
setmetatable(pos, Pos)
return pos
end
function Pos.invalid() return Pos.new(0, 0, 0, 0) end
function Pos.is(x)
if not type(x) == 'table' then return false end
local mt = getmetatable(x)
return mt and mt.__index == Pos
end
function Pos.is(x) return getmetatable(x) == Pos end
function Pos.__lt(a, b) return a.lnum < b.lnum or (a.lnum == b.lnum and a.col < b.col) end
function Pos.__le(a, b) return a < b or a == b end

View File

@@ -12,6 +12,29 @@ end
--- @field stop u.Pos|nil
--- @field mode 'v'|'V'
local Range = {}
Range.__index = Range
function Range.__tostring(self)
--- @param p u.Pos
local function posstr(p)
if p == nil then
return 'nil'
elseif p.off ~= 0 then
return string.format('Pos(%d:%d){off=%d}', p.lnum, p.col, p.off)
else
return string.format('Pos(%d:%d)', p.lnum, p.col)
end
end
local _1 = posstr(self.start)
local _2 = posstr(self.stop)
return string.format(
'Range{bufnr=%d, mode=%s, start=%s, stop=%s}',
self.start.bufnr,
self.mode,
_1,
_2
)
end
--- @param start u.Pos
--- @param stop u.Pos|nil
@@ -23,36 +46,12 @@ function Range.new(start, stop, mode)
end
local r = { start = start, stop = stop, mode = mode or 'v' }
local function str()
--- @param p u.Pos
local function posstr(p)
if p == nil then
return 'nil'
elseif p.off ~= 0 then
return string.format('Pos(%d:%d){off=%d}', p.lnum, p.col, p.off)
else
return string.format('Pos(%d:%d)', p.lnum, p.col)
end
end
local _1 = posstr(r.start)
local _2 = posstr(r.stop)
return string.format(
'Range{bufnr=%d, mode=%s, start=%s, stop=%s}',
r.start.bufnr,
r.mode,
_1,
_2
)
end
setmetatable(r, { __index = Range, __tostring = str })
setmetatable(r, Range)
return r
end
function Range.is(x)
local mt = getmetatable(x)
return mt and mt.__index == Range
end
function Range.is(x) return getmetatable(x) == Range end
--- @param lpos string
--- @param rpos string
@@ -299,8 +298,44 @@ function Range:trim_stop()
return r
end
--- @param p u.Pos
function Range:contains(p) return not self:is_empty() and p >= self.start and p <= self.stop end
--- @param x u.Pos | u.Range
function Range:contains(x)
if getmetatable(x) == Pos then
return not self:is_empty() and x >= self.start and x <= self.stop
elseif getmetatable(x) == Range then
return self:contains(x.start) and self:contains(x.stop)
end
return false
end
--- TODO: test
--- @param other u.Range
--- @return u.Range|nil, u.Range|nil
function Range:difference(other)
local outer, inner = self, other
if not outer:contains(inner) then
outer, inner = inner, outer
end
if not outer:contains(inner) then return nil, nil end
local left
if outer.start ~= inner.start then
local stop = inner.start:clone() - 1
left = Range.new(outer.start, stop)
else
left = Range.new(outer.start) -- empty range
end
local right
if inner.stop ~= outer.stop then
local start = inner.stop:clone() + 1
right = Range.new(start, outer.stop)
else
right = Range.new(inner.stop) -- empty range
end
return left, right
end
--- @return string[]
function Range:lines()