1-based indexing rewrite
All checks were successful
NeoVim tests / plenary-tests (push) Successful in 9s

This commit is contained in:
2025-04-11 13:20:46 -06:00
parent d9bb01be8b
commit d9e6614aa4
23 changed files with 447 additions and 572 deletions

View File

@@ -4,12 +4,12 @@ local withbuf = loadfile './spec/withbuf.lua'()
describe('Pos', function()
it('get a char from a given position', function()
withbuf({ 'asdf', 'bleh', 'a', '', 'goo' }, function()
assert.are.same('a', Pos.new(nil, 0, 0):char())
assert.are.same('d', Pos.new(nil, 0, 2):char())
assert.are.same('f', Pos.new(nil, 0, 3):char())
assert.are.same('a', Pos.new(nil, 2, 0):char())
assert.are.same('', Pos.new(nil, 3, 0):char())
assert.are.same('o', Pos.new(nil, 4, 2):char())
assert.are.same('a', Pos.new(nil, 1, 1):char())
assert.are.same('d', Pos.new(nil, 1, 3):char())
assert.are.same('f', Pos.new(nil, 1, 4):char())
assert.are.same('a', Pos.new(nil, 3, 1):char())
assert.are.same('', Pos.new(nil, 4, 1):char())
assert.are.same('o', Pos.new(nil, 5, 3):char())
end)
end)
@@ -23,47 +23,47 @@ describe('Pos', function()
it('get the next position', function()
withbuf({ 'asdf', 'bleh', 'a', '', 'goo' }, function()
-- line 1: a => s
assert.are.same(Pos.new(nil, 0, 1), Pos.new(nil, 0, 0):next())
assert.are.same(Pos.new(nil, 1, 2), Pos.new(nil, 1, 1):next())
-- line 1: d => f
assert.are.same(Pos.new(nil, 0, 3), Pos.new(nil, 0, 2):next())
assert.are.same(Pos.new(nil, 1, 4), Pos.new(nil, 1, 3):next())
-- line 1 => 2
assert.are.same(Pos.new(nil, 1, 0), Pos.new(nil, 0, 3):next())
assert.are.same(Pos.new(nil, 2, 1), Pos.new(nil, 1, 4):next())
-- line 3 => 4
assert.are.same(Pos.new(nil, 3, 0), Pos.new(nil, 2, 0):next())
assert.are.same(Pos.new(nil, 4, 1), Pos.new(nil, 3, 1):next())
-- line 4 => 5
assert.are.same(Pos.new(nil, 4, 0), Pos.new(nil, 3, 0):next())
assert.are.same(Pos.new(nil, 5, 1), Pos.new(nil, 4, 1):next())
-- end returns nil
assert.are.same(nil, Pos.new(nil, 4, 2):next())
assert.are.same(nil, Pos.new(nil, 5, 3):next())
end)
end)
it('get the previous position', function()
withbuf({ 'asdf', 'bleh', 'a', '', 'goo' }, function()
-- line 1: s => a
assert.are.same(Pos.new(nil, 0, 0), Pos.new(nil, 0, 1):next(-1))
assert.are.same(Pos.new(nil, 1, 1), Pos.new(nil, 1, 2):next(-1))
-- line 1: f => d
assert.are.same(Pos.new(nil, 0, 2), Pos.new(nil, 0, 3):next(-1))
assert.are.same(Pos.new(nil, 1, 3), Pos.new(nil, 1, 4):next(-1))
-- line 2 => 1
assert.are.same(Pos.new(nil, 0, 3), Pos.new(nil, 1, 0):next(-1))
assert.are.same(Pos.new(nil, 1, 4), Pos.new(nil, 2, 1):next(-1))
-- line 4 => 3
assert.are.same(Pos.new(nil, 2, 0), Pos.new(nil, 3, 0):next(-1))
assert.are.same(Pos.new(nil, 3, 1), Pos.new(nil, 4, 1):next(-1))
-- line 5 => 4
assert.are.same(Pos.new(nil, 3, 0), Pos.new(nil, 4, 0):next(-1))
assert.are.same(Pos.new(nil, 4, 1), Pos.new(nil, 5, 1):next(-1))
-- beginning returns nil
assert.are.same(nil, Pos.new(nil, 0, 0):next(-1))
assert.are.same(nil, Pos.new(nil, 1, 1):next(-1))
end)
end)
it('find matching brackets', function()
withbuf({ 'asdf ({} def <[{}]>) ;lkj' }, function()
-- outer parens are matched:
assert.are.same(Pos.new(nil, 0, 19), Pos.new(nil, 0, 5):find_match())
assert.are.same(Pos.new(nil, 1, 20), Pos.new(nil, 1, 6):find_match())
-- outer parens are matched (backward):
assert.are.same(Pos.new(nil, 0, 5), Pos.new(nil, 0, 19):find_match())
assert.are.same(Pos.new(nil, 1, 6), Pos.new(nil, 1, 20):find_match())
-- no potential match returns nil
assert.are.same(nil, Pos.new(nil, 0, 0):find_match())
assert.are.same(nil, Pos.new(nil, 1, 1):find_match())
-- watchdog expires before an otherwise valid match is found:
assert.are.same(nil, Pos.new(nil, 0, 5):find_match(2))
assert.are.same(nil, Pos.new(nil, 1, 6):find_match(2))
end)
end)
end)

View File

@@ -28,7 +28,7 @@ describe('Range', function()
it('get from positions: v in single line', function()
withbuf({ 'line one', 'and line two' }, function()
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 0, 3), 'v')
local range = Range.new(Pos.new(nil, 1, 2), Pos.new(nil, 1, 4), 'v')
local lines = range:lines()
assert.are.same({ 'ine' }, lines)
@@ -39,7 +39,7 @@ describe('Range', function()
it('get from positions: v across multiple lines', function()
withbuf({ 'pre line', 'the quick brown fox', 'jumps over a lazy dog', 'post line' }, function()
local range = Range.new(Pos.new(nil, 1, 4), Pos.new(nil, 2, 4), 'v')
local range = Range.new(Pos.new(nil, 2, 5), Pos.new(nil, 3, 5), 'v')
local lines = range:lines()
assert.are.same({ 'quick brown fox', 'jumps' }, lines)
end)
@@ -47,7 +47,7 @@ describe('Range', function()
it('get from positions: V', function()
withbuf({ 'line one', 'and line two' }, function()
local range = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, Pos.MAX_COL), 'V')
local range = Range.new(Pos.new(nil, 1, 1), Pos.new(nil, 1, Pos.MAX_COL), 'V')
local lines = range:lines()
assert.are.same({ 'line one' }, lines)
@@ -58,7 +58,7 @@ describe('Range', function()
it('get from positions: V across multiple lines', function()
withbuf({ 'pre line', 'the quick brown fox', 'jumps over a lazy dog', 'post line' }, function()
local range = Range.new(Pos.new(nil, 1, 0), Pos.new(nil, 2, Pos.MAX_COL), 'V')
local range = Range.new(Pos.new(nil, 2, 1), Pos.new(nil, 3, Pos.MAX_COL), 'V')
local lines = range:lines()
assert.are.same({ 'the quick brown fox', 'jumps over a lazy dog' }, lines)
end)
@@ -88,7 +88,7 @@ describe('Range', function()
it('replace within line', function()
withbuf({ 'pre line', 'the quick brown fox', 'jumps over a lazy dog', 'post line' }, function()
local range = Range.new(Pos.new(nil, 1, 4), Pos.new(nil, 1, 8), 'v')
local range = Range.new(Pos.new(nil, 2, 5), Pos.new(nil, 2, 9), 'v')
range:replace 'quack'
local text = Range.from_line(nil, 1):text()
@@ -98,7 +98,7 @@ describe('Range', function()
it('delete within line', function()
withbuf({ 'pre line', 'the quick brown fox', 'jumps over a lazy dog', 'post line' }, function()
local range = Range.new(Pos.new(nil, 1, 4), Pos.new(nil, 1, 9), 'v')
local range = Range.new(Pos.new(nil, 2, 5), Pos.new(nil, 2, 10), 'v')
range:replace ''
local text = Range.from_line(nil, 1):text()
@@ -106,7 +106,7 @@ describe('Range', function()
end)
withbuf({ 'pre line', 'the quick brown fox', 'jumps over a lazy dog', 'post line' }, function()
local range = Range.new(Pos.new(nil, 1, 4), Pos.new(nil, 1, 9), 'v')
local range = Range.new(Pos.new(nil, 2, 5), Pos.new(nil, 2, 10), 'v')
range:replace(nil)
local text = Range.from_line(nil, 1):text()
@@ -116,7 +116,7 @@ describe('Range', function()
it('replace across multiple lines: v', function()
withbuf({ 'pre line', 'the quick brown fox', 'jumps over a lazy dog', 'post line' }, function()
local range = Range.new(Pos.new(nil, 1, 4), Pos.new(nil, 2, 4), 'v')
local range = Range.new(Pos.new(nil, 2, 5), Pos.new(nil, 3, 5), 'v')
range:replace 'plane flew'
local lines = Range.from_buf_text():lines()
@@ -258,22 +258,18 @@ describe('Range', function()
end)
end)
it('line0', function()
it('line', function()
withbuf({
'this is a {',
'block',
'} here',
}, function()
local range = Range.new(Pos.new(0, 0, 5), Pos.new(0, 1, 4), 'v')
local lfirst = range:line0(0)
assert.are.same(5, lfirst.idx0.start)
assert.are.same(10, lfirst.idx0.stop)
assert.are.same(0, lfirst.lnum)
assert.are.same('is a {', lfirst.text())
assert.are.same('is a {', lfirst.range():text())
assert.are.same(Pos.new(0, 0, 5), lfirst.range().start)
assert.are.same(Pos.new(0, 0, 10), lfirst.range().stop)
assert.are.same('block', range:line0(1).text())
local range = Range.new(Pos.new(0, 1, 6), Pos.new(0, 2, 5), 'v')
local lfirst = assert(range:line(1), 'lfirst null')
assert.are.same('is a {', lfirst:text())
assert.are.same(Pos.new(0, 1, 6), lfirst.start)
assert.are.same(Pos.new(0, 1, 11), lfirst.stop)
assert.are.same('block', range:line(2):text())
end)
end)
@@ -297,16 +293,16 @@ describe('Range', function()
vim.cmd.normal 'v' -- enter visual mode
vim.cmd.normal 'l' -- select one character to the right
local range = Range.from_vtext()
assert.are.same(range.start, Pos.new(nil, 0, 2))
assert.are.same(range.stop, Pos.new(nil, 0, 3))
assert.are.same(range.start, Pos.new(nil, 1, 3))
assert.are.same(range.stop, Pos.new(nil, 1, 4))
assert.are.same(range.mode, 'v')
end)
end)
it('from_op_func', function()
withbuf({ 'line one', 'and line two' }, function()
local a = Pos.new(nil, 0, 0)
local b = Pos.new(nil, 1, 1)
local a = Pos.new(nil, 1, 1)
local b = Pos.new(nil, 2, 2)
a:save_to_pos "'["
b:save_to_pos "']"
@@ -317,7 +313,7 @@ describe('Range', function()
range = Range.from_op_func 'line'
assert.are.same(range.start, a)
assert.are.same(range.stop, Pos.new(nil, 1, Pos.MAX_COL))
assert.are.same(range.stop, Pos.new(nil, 2, Pos.MAX_COL))
assert.are.same(range.mode, 'V')
end)
end)
@@ -325,8 +321,8 @@ describe('Range', function()
it('from_cmd_args', function()
local args = { range = 1 }
withbuf({ 'line one', 'and line two' }, function()
local a = Pos.new(nil, 0, 0)
local b = Pos.new(nil, 1, 1)
local a = Pos.new(nil, 1, 1)
local b = Pos.new(nil, 2, 2)
a:save_to_pos "'<"
b:save_to_pos "'>"
@@ -341,25 +337,25 @@ describe('Range', function()
withbuf({ [[the "quick" brown fox]] }, function()
vim.fn.setpos('.', { 0, 1, 5, 0 })
local range = Range.find_nearest_quotes()
assert.are.same(range.start, Pos.new(nil, 0, 4))
assert.are.same(range.stop, Pos.new(nil, 0, 10))
assert.are.same(range.start, Pos.new(nil, 1, 5))
assert.are.same(range.stop, Pos.new(nil, 1, 11))
end)
withbuf({ [[the 'quick' brown fox]] }, function()
vim.fn.setpos('.', { 0, 1, 5, 0 })
local range = Range.find_nearest_quotes()
assert.are.same(range.start, Pos.new(nil, 0, 4))
assert.are.same(range.stop, Pos.new(nil, 0, 10))
assert.are.same(range.start, Pos.new(nil, 1, 5))
assert.are.same(range.stop, Pos.new(nil, 1, 11))
end)
end)
it('smallest', function()
local r1 = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 0, 3), 'v')
local r2 = Range.new(Pos.new(nil, 0, 2), Pos.new(nil, 0, 4), 'v')
local r3 = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 5), 'v')
local r1 = Range.new(Pos.new(nil, 1, 2), Pos.new(nil, 1, 4), 'v')
local r2 = Range.new(Pos.new(nil, 1, 3), Pos.new(nil, 1, 5), 'v')
local r3 = Range.new(Pos.new(nil, 1, 1), Pos.new(nil, 1, 6), 'v')
local smallest = Range.smallest { r1, r2, r3 }
assert.are.same(smallest.start, Pos.new(nil, 0, 1))
assert.are.same(smallest.stop, Pos.new(nil, 0, 3))
assert.are.same(smallest.start, Pos.new(nil, 1, 2))
assert.are.same(smallest.stop, Pos.new(nil, 1, 4))
end)
it('clone', function()
@@ -381,9 +377,9 @@ describe('Range', function()
it('to_linewise()', function()
withbuf({ 'line one', 'and line two' }, function()
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 1, 3), 'v')
local range = Range.new(Pos.new(nil, 1, 2), Pos.new(nil, 2, 4), 'v')
local linewise_range = range:to_linewise()
assert.are.same(linewise_range.start.col, 0)
assert.are.same(linewise_range.start.col, 1)
assert.are.same(linewise_range.stop.col, Pos.MAX_COL)
assert.are.same(linewise_range.mode, 'V')
end)
@@ -391,56 +387,56 @@ describe('Range', function()
it('is_empty', function()
withbuf({ 'line one', 'and line two' }, function()
local range = Range.new(Pos.new(nil, 0, 0), nil, 'v')
local range = Range.new(Pos.new(nil, 1, 1), nil, 'v')
assert.is_true(range:is_empty())
local range2 = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 1), 'v')
local range2 = Range.new(Pos.new(nil, 1, 1), Pos.new(nil, 1, 2), 'v')
assert.is_false(range2:is_empty())
end)
end)
it('trim_start', function()
withbuf({ ' line one', 'line two' }, function()
local range = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 9), 'v')
local range = Range.new(Pos.new(nil, 1, 1), Pos.new(nil, 1, 10), 'v')
local trimmed = range:trim_start()
assert.are.same(trimmed.start, Pos.new(nil, 0, 3)) -- should be after the spaces
assert.are.same(trimmed.start, Pos.new(nil, 1, 4)) -- should be after the spaces
end)
end)
it('trim_stop', function()
withbuf({ 'line one ', 'line two' }, function()
local range = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 9), 'v')
local range = Range.new(Pos.new(nil, 1, 1), Pos.new(nil, 1, 10), 'v')
local trimmed = range:trim_stop()
assert.are.same(trimmed.stop, Pos.new(nil, 0, 7)) -- should be before the spaces
assert.are.same(trimmed.stop, Pos.new(nil, 1, 8)) -- should be before the spaces
end)
end)
it('contains', function()
withbuf({ 'line one', 'and line two' }, function()
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 0, 3), 'v')
local pos = Pos.new(nil, 0, 2)
local range = Range.new(Pos.new(nil, 1, 2), Pos.new(nil, 1, 4), 'v')
local pos = Pos.new(nil, 1, 3)
assert.is_true(range:contains(pos))
pos = Pos.new(nil, 0, 4) -- outside of range
pos = Pos.new(nil, 1, 5) -- outside of range
assert.is_false(range:contains(pos))
end)
end)
it('shrink', function()
withbuf({ 'line one', 'and line two' }, function()
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 1, 3), 'v')
local range = Range.new(Pos.new(nil, 2, 3), Pos.new(nil, 3, 5), 'v')
local shrunk = range:shrink(1)
assert.are.same(shrunk.start, Pos.new(nil, 0, 2))
assert.are.same(shrunk.stop, Pos.new(nil, 1, 2))
assert.are.same(shrunk.start, Pos.new(nil, 2, 4))
assert.are.same(shrunk.stop, Pos.new(nil, 3, 4))
end)
end)
it('must_shrink', function()
withbuf({ 'line one', 'and line two' }, function()
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 1, 3), 'v')
local range = Range.new(Pos.new(nil, 2, 3), Pos.new(nil, 3, 5), 'v')
local shrunk = range:must_shrink(1)
assert.are.same(shrunk.start, Pos.new(nil, 0, 2))
assert.are.same(shrunk.stop, Pos.new(nil, 1, 2))
assert.are.same(shrunk.start, Pos.new(nil, 2, 4))
assert.are.same(shrunk.stop, Pos.new(nil, 3, 4))
assert.has.error(function() range:must_shrink(100) end, 'error in Range:must_shrink: Range:shrink() returned nil')
end)
@@ -451,22 +447,24 @@ describe('Range', function()
local range = Range.from_lines(nil, 0, 1)
range:set_visual_selection()
assert.are.same(Pos.from_pos 'v', Pos.new(nil, 0, 0))
assert.are.same(Pos.from_pos '.', Pos.new(nil, 1, 11))
assert.are.same(Pos.from_pos 'v', Pos.new(nil, 1, 1))
-- Since the selection is 'V' (instead of 'v'), the end
-- selects one character past the end:
assert.are.same(Pos.from_pos '.', Pos.new(nil, 2, 13))
end)
end)
it('selections set to past the EOL should not error', function()
withbuf({ 'Rg SET NAMES' }, function()
local b = vim.api.nvim_get_current_buf()
local r = Range.new(Pos.new(b, 0, 3), Pos.new(b, 0, 12), 'v')
local r = Range.new(Pos.new(b, 1, 4), Pos.new(b, 1, 13), 'v')
r:replace 'bleh'
assert.are.same({ 'Rg bleh' }, vim.api.nvim_buf_get_lines(b, 0, -1, false))
end)
withbuf({ 'Rg SET NAMES' }, function()
local b = vim.api.nvim_get_current_buf()
local r = Range.new(Pos.new(b, 0, 3), Pos.new(b, 0, 11), 'v')
local r = Range.new(Pos.new(b, 1, 4), Pos.new(b, 1, 12), 'v')
r:replace 'bleh'
assert.are.same({ 'Rg bleh' }, vim.api.nvim_buf_get_lines(b, 0, -1, false))
end)
@@ -475,7 +473,7 @@ describe('Range', function()
it('replace updates Range.stop: same line', function()
withbuf({ 'The quick brown fox jumps over the lazy dog' }, function()
local b = vim.api.nvim_get_current_buf()
local r = Range.new(Pos.new(b, 0, 4), Pos.new(b, 0, 8), 'v')
local r = Range.new(Pos.new(b, 1, 5), Pos.new(b, 1, 9), 'v')
r:replace 'bleh1'
assert.are.same({ 'The bleh1 brown fox jumps over the lazy dog' }, vim.api.nvim_buf_get_lines(b, 0, -1, false))
@@ -491,11 +489,12 @@ describe('Range', function()
'over the lazy dog',
}, function()
local b = vim.api.nvim_get_current_buf()
local r = Range.new(Pos.new(b, 0, 20), Pos.new(b, 1, 3), 'v')
local r = Range.new(Pos.new(b, 1, 21), Pos.new(b, 2, 4), 'v')
assert.are.same({ 'jumps', 'over' }, r:lines())
r:replace 'bleh1'
assert.are.same({ 'The quick brown fox bleh1 the lazy dog' }, vim.api.nvim_buf_get_lines(b, 0, -1, false))
assert.are.same({ 'bleh1' }, r:lines())
r:replace 'blehGoo2'
assert.are.same({ 'The quick brown fox blehGoo2 the lazy dog' }, vim.api.nvim_buf_get_lines(b, 0, -1, false))
@@ -511,7 +510,7 @@ describe('Range', function()
'the lazy dog',
}, function()
local b = vim.api.nvim_get_current_buf()
local r = Range.new(Pos.new(b, 1, 0), Pos.new(b, 3, Pos.MAX_COL), 'V')
local r = Range.new(Pos.new(b, 2, 1), Pos.new(b, 4, Pos.MAX_COL), 'V')
assert.are.same({ 'fox', 'jumps', 'over' }, r:lines())
r:replace { 'bleh1', 'bleh2' }
@@ -540,7 +539,7 @@ describe('Range', function()
'the lazy dog',
}, function()
local b = vim.api.nvim_get_current_buf()
local r = Range.new(Pos.new(b, 1, 0), Pos.new(b, 3, Pos.MAX_COL), 'V')
local r = Range.new(Pos.new(b, 2, 1), Pos.new(b, 4, Pos.MAX_COL), 'V')
assert.are.same({ 'fox', 'jumps', 'over' }, r:lines())
r:replace(nil)