add Range:difference
All checks were successful
NeoVim tests / plenary-tests (push) Successful in 9s
All checks were successful
NeoVim tests / plenary-tests (push) Successful in 9s
This commit is contained in:
parent
dacb186324
commit
9f5fdb4f2b
@ -1,11 +1,12 @@
|
|||||||
local vim_repeat = require 'u.repeat'
|
local vim_repeat = require 'u.repeat'
|
||||||
local Pos = require 'u.pos'
|
|
||||||
local Range = require 'u.range'
|
local Range = require 'u.range'
|
||||||
local Buffer = require 'u.buffer'
|
local Buffer = require 'u.buffer'
|
||||||
local CodeWriter = require 'u.codewriter'
|
local CodeWriter = require 'u.codewriter'
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
local ESC = vim.api.nvim_replace_termcodes('<Esc>', true, false, true)
|
||||||
|
|
||||||
local surrounds = {
|
local surrounds = {
|
||||||
[')'] = { left = '(', right = ')' },
|
[')'] = { left = '(', right = ')' },
|
||||||
['('] = { left = '( ', right = ' )' },
|
['('] = { left = '( ', right = ' )' },
|
||||||
@ -126,7 +127,7 @@ function M.setup()
|
|||||||
|
|
||||||
do_surround(range, bounds)
|
do_surround(range, bounds)
|
||||||
-- this is a visual mapping: end in normal mode:
|
-- this is a visual mapping: end in normal mode:
|
||||||
vim.cmd { cmd = 'normal', args = { '' }, bang = true }
|
vim.cmd.normal(ESC)
|
||||||
end, { noremap = true, silent = true })
|
end, { noremap = true, silent = true })
|
||||||
|
|
||||||
-- Change
|
-- Change
|
||||||
|
@ -12,8 +12,17 @@ end
|
|||||||
--- @field col number 1-based column index
|
--- @field col number 1-based column index
|
||||||
--- @field off number
|
--- @field off number
|
||||||
local Pos = {}
|
local Pos = {}
|
||||||
|
Pos.__index = Pos
|
||||||
Pos.MAX_COL = MAX_COL
|
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 bufnr? number
|
||||||
--- @param lnum number 1-based
|
--- @param lnum number 1-based
|
||||||
--- @param col number 1-based
|
--- @param col number 1-based
|
||||||
@ -28,31 +37,13 @@ function Pos.new(bufnr, lnum, col, off)
|
|||||||
col = col,
|
col = col,
|
||||||
off = off,
|
off = off,
|
||||||
}
|
}
|
||||||
|
setmetatable(pos, Pos)
|
||||||
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,
|
|
||||||
})
|
|
||||||
return pos
|
return pos
|
||||||
end
|
end
|
||||||
|
|
||||||
function Pos.invalid() return Pos.new(0, 0, 0, 0) end
|
function Pos.invalid() return Pos.new(0, 0, 0, 0) end
|
||||||
|
|
||||||
function Pos.is(x)
|
function Pos.is(x) return getmetatable(x) == Pos end
|
||||||
if not type(x) == 'table' then return false end
|
|
||||||
local mt = getmetatable(x)
|
|
||||||
return mt and mt.__index == 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.__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
|
function Pos.__le(a, b) return a < b or a == b end
|
||||||
|
@ -12,18 +12,8 @@ end
|
|||||||
--- @field stop u.Pos|nil
|
--- @field stop u.Pos|nil
|
||||||
--- @field mode 'v'|'V'
|
--- @field mode 'v'|'V'
|
||||||
local Range = {}
|
local Range = {}
|
||||||
|
Range.__index = Range
|
||||||
--- @param start u.Pos
|
function Range.__tostring(self)
|
||||||
--- @param stop u.Pos|nil
|
|
||||||
--- @param mode? 'v'|'V'
|
|
||||||
--- @return u.Range
|
|
||||||
function Range.new(start, stop, mode)
|
|
||||||
if stop ~= nil and stop < start then
|
|
||||||
start, stop = stop, start
|
|
||||||
end
|
|
||||||
|
|
||||||
local r = { start = start, stop = stop, mode = mode or 'v' }
|
|
||||||
local function str()
|
|
||||||
--- @param p u.Pos
|
--- @param p u.Pos
|
||||||
local function posstr(p)
|
local function posstr(p)
|
||||||
if p == nil then
|
if p == nil then
|
||||||
@ -35,24 +25,33 @@ function Range.new(start, stop, mode)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local _1 = posstr(r.start)
|
local _1 = posstr(self.start)
|
||||||
local _2 = posstr(r.stop)
|
local _2 = posstr(self.stop)
|
||||||
return string.format(
|
return string.format(
|
||||||
'Range{bufnr=%d, mode=%s, start=%s, stop=%s}',
|
'Range{bufnr=%d, mode=%s, start=%s, stop=%s}',
|
||||||
r.start.bufnr,
|
self.start.bufnr,
|
||||||
r.mode,
|
self.mode,
|
||||||
_1,
|
_1,
|
||||||
_2
|
_2
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
setmetatable(r, { __index = Range, __tostring = str })
|
|
||||||
|
--- @param start u.Pos
|
||||||
|
--- @param stop u.Pos|nil
|
||||||
|
--- @param mode? 'v'|'V'
|
||||||
|
--- @return u.Range
|
||||||
|
function Range.new(start, stop, mode)
|
||||||
|
if stop ~= nil and stop < start then
|
||||||
|
start, stop = stop, start
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = { start = start, stop = stop, mode = mode or 'v' }
|
||||||
|
|
||||||
|
setmetatable(r, Range)
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
function Range.is(x)
|
function Range.is(x) return getmetatable(x) == Range end
|
||||||
local mt = getmetatable(x)
|
|
||||||
return mt and mt.__index == Range
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param lpos string
|
--- @param lpos string
|
||||||
--- @param rpos string
|
--- @param rpos string
|
||||||
@ -299,8 +298,44 @@ function Range:trim_stop()
|
|||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param p u.Pos
|
--- @param x u.Pos | u.Range
|
||||||
function Range:contains(p) return not self:is_empty() and p >= self.start and p <= self.stop end
|
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[]
|
--- @return string[]
|
||||||
function Range:lines()
|
function Range:lines()
|
||||||
|
@ -422,6 +422,30 @@ describe('Range', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('difference', function()
|
||||||
|
withbuf({ 'line one', 'and line two' }, function()
|
||||||
|
local range_outer = Range.new(Pos.new(nil, 2, 1), Pos.new(nil, 2, 12), 'v')
|
||||||
|
local range_inner = Range.new(Pos.new(nil, 2, 5), Pos.new(nil, 2, 8), 'v')
|
||||||
|
|
||||||
|
assert.are.same(range_outer:text(), 'and line two')
|
||||||
|
assert.are.same(range_inner:text(), 'line')
|
||||||
|
|
||||||
|
local left, right = range_outer:difference(range_inner)
|
||||||
|
assert.are.same(left:text(), 'and ')
|
||||||
|
assert.are.same(right:text(), ' two')
|
||||||
|
|
||||||
|
left, right = range_inner:difference(range_outer)
|
||||||
|
assert.are.same(left:text(), 'and ')
|
||||||
|
assert.are.same(right:text(), ' two')
|
||||||
|
|
||||||
|
left, right = range_outer:difference(range_outer)
|
||||||
|
assert.are.same(left:is_empty(), true)
|
||||||
|
assert.are.same(left:text(), '')
|
||||||
|
assert.are.same(right:is_empty(), true)
|
||||||
|
assert.are.same(right:text(), '')
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
it('shrink', function()
|
it('shrink', function()
|
||||||
withbuf({ 'line one', 'and line two' }, function()
|
withbuf({ 'line one', 'and line two' }, function()
|
||||||
local range = Range.new(Pos.new(nil, 2, 3), Pos.new(nil, 3, 5), 'v')
|
local range = Range.new(Pos.new(nil, 2, 3), Pos.new(nil, 3, 5), 'v')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user