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

@@ -241,8 +241,11 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
/// ```
///
/// If `end` is less than `start`, traversal works backwards. (Useful
/// with `limit`, to get the first marks prior to a given position.)
/// If `end` is less than `start`, marks are returned in reverse order.
/// (Useful with `limit`, to get the first marks prior to a given position.)
///
/// Note: For a reverse range, `limit` does not actually affect the traversed
/// range, just how many marks are returned
///
/// Note: when using extmark ranges (marks with a end_row/end_col position)
/// the `overlap` option might be useful. Otherwise only the start position
@@ -327,8 +330,6 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
limit = INT64_MAX;
}
bool reverse = false;
int l_row;
colnr_T l_col;
if (!extmark_get_index_from_obj(buf, ns_id, start, &l_row, &l_col, err)) {
@@ -341,17 +342,31 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
return rv;
}
if (l_row > u_row || (l_row == u_row && l_col > u_col)) {
reverse = true;
size_t rv_limit = (size_t)limit;
bool reverse = l_row > u_row || (l_row == u_row && l_col > u_col);
if (reverse) {
limit = INT64_MAX; // limit the return value instead
int row = l_row;
l_row = u_row;
u_row = row;
colnr_T col = l_col;
l_col = u_col;
u_col = col;
}
// note: ns_id=-1 allowed, represented as UINT32_MAX
ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col, u_row,
u_col, (int64_t)limit, reverse, type, opts->overlap);
u_col, (int64_t)limit, type, opts->overlap);
rv = arena_array(arena, kv_size(marks));
for (size_t i = 0; i < kv_size(marks); i++) {
ADD_C(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena)));
rv = arena_array(arena, MIN(kv_size(marks), rv_limit));
if (reverse) {
for (int i = (int)kv_size(marks) - 1; i >= 0 && kv_size(rv) < rv_limit; i--) {
ADD_C(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena)));
}
} else {
for (size_t i = 0; i < kv_size(marks); i++) {
ADD_C(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena)));
}
}
kv_destroy(marks);