fix(api): wrong return value with reverse range + overlap #32956

Problem:  When iterating in reverse with {start} > {end} in
          `nvim_buf_get_extmarks()`, marks that overlap {start} and are
          greater than {end} are included in the return value twice.
          Marks that overlap {end} and do not overlap {start} are not
          not included in the return value at all. Marks are not
          actually returned in a meaningful "traversal order".

Solution: Rather than actually iterating in reverse, (also possible but
          requires convoluted conditions and would require fetching
          overlapping marks for both the {start} and {end} position,
          while still ending up with non-traversal ordered marks),
          iterate normally and reverse the return value.
(cherry picked from commit 65170e8dad)
This commit is contained in:
luukvbaal
2025-04-22 01:18:03 +02:00
committed by github-actions[bot]
parent 3b0c88a537
commit fb71d631a5
5 changed files with 62 additions and 43 deletions

View File

@@ -331,30 +331,30 @@ describe('API/extmarks', function()
rv = get_extmarks(ns, lower, upper)
eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
-- prev with mark id
-- reverse with mark id
rv = get_extmarks(ns, marks[3], { 0, 0 }, { limit = 1 })
eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
rv = get_extmarks(ns, marks[2], { 0, 0 }, { limit = 1 })
eq({ { marks[2], positions[2][1], positions[2][2] } }, rv)
-- prev with positional when mark exists at position
-- reverse with positional when mark exists at position
rv = get_extmarks(ns, positions[3], { 0, 0 }, { limit = 1 })
eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
-- prev with positional index (no mark at position)
-- reverse with positional index (no mark at position)
rv = get_extmarks(ns, { positions[1][1], positions[1][2] + 1 }, { 0, 0 }, { limit = 1 })
eq({ { marks[1], positions[1][1], positions[1][2] } }, rv)
-- prev with Extremity index
-- reverse with Extremity index
rv = get_extmarks(ns, { -1, -1 }, { 0, 0 }, { limit = 1 })
eq({ { marks[3], positions[3][1], positions[3][2] } }, rv)
-- prevrange with mark id
-- reverse with mark id
rv = get_extmarks(ns, marks[3], marks[1])
eq({ marks[3], positions[3][1], positions[3][2] }, rv[1])
eq({ marks[2], positions[2][1], positions[2][2] }, rv[2])
eq({ marks[1], positions[1][1], positions[1][2] }, rv[3])
-- prevrange with limit
-- reverse with limit
rv = get_extmarks(ns, marks[3], marks[1], { limit = 2 })
eq(2, #rv)
-- prevrange with positional when mark exists at position
-- reverse with positional when mark exists at position
rv = get_extmarks(ns, positions[3], positions[1])
eq({
{ marks[3], positions[3][1], positions[3][2] },
@@ -363,7 +363,7 @@ describe('API/extmarks', function()
}, rv)
rv = get_extmarks(ns, positions[2], positions[1])
eq(2, #rv)
-- prevrange with positional index (no mark at position)
-- reverse with positional index (no mark at position)
lower = { positions[2][1], positions[2][2] + 1 }
upper = { positions[3][1], positions[3][2] + 1 }
rv = get_extmarks(ns, upper, lower)
@@ -372,7 +372,7 @@ describe('API/extmarks', function()
upper = { positions[3][1], positions[3][2] + 2 }
rv = get_extmarks(ns, upper, lower)
eq({}, rv)
-- prevrange with extremity index
-- reverse with extremity index
lower = { 0, 0 }
upper = { positions[2][1], positions[2][2] - 1 }
rv = get_extmarks(ns, upper, lower)
@@ -428,6 +428,14 @@ describe('API/extmarks', function()
set_extmark(ns, marks[3], 0, 2) -- check is inclusive
local rv = get_extmarks(ns, { 2, 3 }, { 0, 2 })
eq({ { marks[1], 2, 1 }, { marks[2], 1, 4 }, { marks[3], 0, 2 } }, rv)
-- doesn't include paired marks whose start pos > lower bound twice
-- and returns mark overlapping start pos but not end pos
local m1 = set_extmark(ns, nil, 0, 0, { end_row = 1, end_col = 4 })
local m2 = set_extmark(ns, nil, 0, 0, { end_row = 1, end_col = 2 })
local m3 = set_extmark(ns, nil, 1, 0, { end_row = 1, end_col = 4 })
local m4 = set_extmark(ns, nil, 1, 2, { end_row = 1, end_col = 4 })
rv = get_extmarks(ns, { 1, 3 }, { 1, 2 }, { overlap = true })
eq({ { m4, 1, 2 }, { m3, 1, 0 }, { m2, 0, 0 }, { m1, 0, 0 } }, rv)
end)
it('get_marks limit=0 returns nothing', function()
@@ -973,7 +981,7 @@ describe('API/extmarks', function()
eq(1, #rv)
rv = get_extmarks(ns2, { 0, 0 }, positions[2], { limit = 1 })
eq(1, #rv)
-- get_prev (limit set)
-- reverse (limit set)
rv = get_extmarks(ns, positions[1], { 0, 0 }, { limit = 1 })
eq(1, #rv)
rv = get_extmarks(ns2, positions[1], { 0, 0 }, { limit = 1 })
@@ -984,7 +992,7 @@ describe('API/extmarks', function()
eq(2, #rv)
rv = get_extmarks(ns2, positions[1], positions[2])
eq(2, #rv)
-- get_prev (no limit)
-- reverse (no limit)
rv = get_extmarks(ns, positions[2], positions[1])
eq(2, #rv)
rv = get_extmarks(ns2, positions[2], positions[1])