mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #13243 from bfredl/intersection
feat(extmark): support highlighting and querying multiline ranges
This commit is contained in:
commit
1c4a93b591
@ -41,3 +41,4 @@ Checks: >
|
||||
-readability-redundant-declaration,
|
||||
-readability-redundant-function-ptr-dereference,
|
||||
-readability-suspicious-call-argument,
|
||||
-readability-non-const-parameter,
|
||||
|
@ -2546,7 +2546,7 @@ nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {opts})
|
||||
0-indexed (row, col) tuple or empty list () if extmark id was absent
|
||||
|
||||
*nvim_buf_get_extmarks()*
|
||||
nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
|
||||
nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {*opts})
|
||||
Gets |extmarks| in "traversal order" from a |charwise| region defined by
|
||||
buffer positions (inclusive, 0-indexed |api-indexing|).
|
||||
|
||||
@ -2560,6 +2560,10 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
|
||||
If `end` is less than `start`, traversal works backwards. (Useful with
|
||||
`limit`, to get the first marks prior to a given position.)
|
||||
|
||||
Note: when using extmark ranges (marks with a end_row/end_col position)
|
||||
the `overlap` option might be useful. Otherwise only the start position of
|
||||
an extmark will be considered.
|
||||
|
||||
Example: >lua
|
||||
local api = vim.api
|
||||
local pos = api.nvim_win_get_cursor(0)
|
||||
@ -2589,6 +2593,8 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
|
||||
• details: Whether to include the details dict
|
||||
• hl_name: Whether to include highlight group name instead
|
||||
of id, true if omitted
|
||||
• overlap: Also include marks which overlap the range, even
|
||||
if their start position is less than `start`
|
||||
• type: Filter marks by type: "highlight", "sign",
|
||||
"virt_text" and "virt_lines"
|
||||
|
||||
@ -2608,6 +2614,11 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
|
||||
Using the optional arguments, it is possible to use this to highlight a
|
||||
range of text, and also to associate virtual text to the mark.
|
||||
|
||||
If present, the position defined by `end_col` and `end_row` should be
|
||||
after the start position in order for the extmark to cover a range. An
|
||||
earlier end position is not an error, but then it behaves like an empty
|
||||
range (no highlighting).
|
||||
|
||||
Parameters: ~
|
||||
• {buffer} Buffer handle, or 0 for current buffer
|
||||
• {ns_id} Namespace id from |nvim_create_namespace()|
|
||||
|
@ -221,6 +221,15 @@ The following changes to existing APIs or features add new behavior.
|
||||
"virtual_text" table, which gives users more control over how diagnostic
|
||||
virtual text is displayed.
|
||||
|
||||
• Extmarks now fully support multi-line ranges, and a single extmark can be
|
||||
used to highlight a range of arbitrary length. The |nvim_buf_set_extmark()|
|
||||
API function already allowed you to define such ranges, but highlight regions
|
||||
were not rendered consistently for a range that covers more than one line break.
|
||||
This has now been fixed. Signs defined as part of a multi-line extmark also
|
||||
apply to every line in the range, not just the first.
|
||||
In addition, |nvim_buf_get_extmarks()| has gained an "overlap" option to
|
||||
return such ranges even if they started before the specified position.
|
||||
|
||||
==============================================================================
|
||||
REMOVED FEATURES *news-removed*
|
||||
|
||||
|
18
runtime/lua/vim/_meta/api.lua
generated
18
runtime/lua/vim/_meta/api.lua
generated
@ -5,6 +5,13 @@ error('Cannot require a meta file')
|
||||
|
||||
vim.api = {}
|
||||
|
||||
--- @private
|
||||
--- @param buffer integer
|
||||
--- @param keys boolean
|
||||
--- @param dot boolean
|
||||
--- @return string
|
||||
function vim.api.nvim__buf_debug_extmarks(buffer, keys, dot) end
|
||||
|
||||
--- @private
|
||||
--- @param buffer integer
|
||||
--- @param first integer
|
||||
@ -313,6 +320,9 @@ function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
|
||||
--- ```
|
||||
--- If `end` is less than `start`, traversal works backwards. (Useful with
|
||||
--- `limit`, to get the first marks prior to a given position.)
|
||||
--- Note: when using extmark ranges (marks with a end_row/end_col position)
|
||||
--- the `overlap` option might be useful. Otherwise only the start position of
|
||||
--- an extmark will be considered.
|
||||
--- Example:
|
||||
--- ```lua
|
||||
--- local api = vim.api
|
||||
@ -337,11 +347,13 @@ function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
|
||||
--- @param end_ any End of range (inclusive): a 0-indexed (row, col) or valid
|
||||
--- extmark id (whose position defines the bound).
|
||||
--- `api-indexing`
|
||||
--- @param opts table<string,any> Optional parameters. Keys:
|
||||
--- @param opts vim.api.keyset.get_extmarks Optional parameters. Keys:
|
||||
--- • limit: Maximum number of marks to return
|
||||
--- • details: Whether to include the details dict
|
||||
--- • hl_name: Whether to include highlight group name instead
|
||||
--- of id, true if omitted
|
||||
--- • overlap: Also include marks which overlap the range, even
|
||||
--- if their start position is less than `start`
|
||||
--- • type: Filter marks by type: "highlight", "sign",
|
||||
--- "virt_text" and "virt_lines"
|
||||
--- @return any[]
|
||||
@ -457,6 +469,10 @@ function vim.api.nvim_buf_line_count(buffer) end
|
||||
--- waiting for the return value.)
|
||||
--- Using the optional arguments, it is possible to use this to highlight a
|
||||
--- range of text, and also to associate virtual text to the mark.
|
||||
--- If present, the position defined by `end_col` and `end_row` should be
|
||||
--- after the start position in order for the extmark to cover a range. An
|
||||
--- earlier end position is not an error, but then it behaves like an empty
|
||||
--- range (no highlighting).
|
||||
---
|
||||
--- @param buffer integer Buffer handle, or 0 for current buffer
|
||||
--- @param ns_id integer Namespace id from `nvim_create_namespace()`
|
||||
|
7
runtime/lua/vim/_meta/api_keysets.lua
generated
7
runtime/lua/vim/_meta/api_keysets.lua
generated
@ -122,6 +122,13 @@ error('Cannot require a meta file')
|
||||
--- @class vim.api.keyset.get_commands
|
||||
--- @field builtin? boolean
|
||||
|
||||
--- @class vim.api.keyset.get_extmarks
|
||||
--- @field limit? integer
|
||||
--- @field details? boolean
|
||||
--- @field hl_name? boolean
|
||||
--- @field overlap? boolean
|
||||
--- @field type? string
|
||||
|
||||
--- @class vim.api.keyset.get_highlight
|
||||
--- @field id? integer
|
||||
--- @field name? string
|
||||
|
@ -55,6 +55,9 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
|
||||
local inclusive = opts.inclusive or false
|
||||
local priority = opts.priority or M.priorities.user
|
||||
|
||||
-- TODO: in case of 'v', 'V' (not block), this should calculate equivalent
|
||||
-- bounds (row, col, end_row, end_col) as multiline regions are natively
|
||||
-- supported now
|
||||
local region = vim.region(bufnr, start, finish, regtype, inclusive)
|
||||
for linenr, cols in pairs(region) do
|
||||
local end_row
|
||||
|
@ -207,6 +207,16 @@ static inline void *_memcpy_free(void *const restrict dest, void *const restrict
|
||||
/* 2^x initial array size. */ \
|
||||
kvi_resize(v, (v).capacity << 1)
|
||||
|
||||
/// fit at least "len" more items
|
||||
#define kvi_ensure_more_space(v, len) \
|
||||
do { \
|
||||
if ((v).capacity < (v).size + len) { \
|
||||
(v).capacity = (v).size + len; \
|
||||
kv_roundup32((v).capacity); \
|
||||
kvi_resize((v), (v).capacity); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/// Get location where to store new element to a vector with preallocated array
|
||||
///
|
||||
/// @param[in,out] v Vector to push to.
|
||||
@ -223,6 +233,19 @@ static inline void *_memcpy_free(void *const restrict dest, void *const restrict
|
||||
#define kvi_push(v, x) \
|
||||
(*kvi_pushp(v) = (x))
|
||||
|
||||
/// Copy a vector to a preallocated vector
|
||||
///
|
||||
/// @param[out] v1 destination
|
||||
/// @param[in] v0 source (can be either vector or preallocated vector)
|
||||
#define kvi_copy(v1, v0) \
|
||||
do { \
|
||||
if ((v1).capacity < (v0).size) { \
|
||||
kvi_resize(v1, (v0).size); \
|
||||
} \
|
||||
(v1).size = (v0).size; \
|
||||
memcpy((v1).items, (v0).items, sizeof((v1).items[0]) * (v0).size); \
|
||||
} while (0)
|
||||
|
||||
/// Free array of elements of a vector with preallocated array if needed
|
||||
///
|
||||
/// @param[out] v Vector to free.
|
||||
|
@ -308,6 +308,10 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
|
||||
/// If `end` is less than `start`, traversal works backwards. (Useful
|
||||
/// with `limit`, to get the first marks prior to a given position.)
|
||||
///
|
||||
/// Note: when using extmark ranges (marks with a end_row/end_col position)
|
||||
/// the `overlap` option might be useful. Otherwise only the start position
|
||||
/// of an extmark will be considered.
|
||||
///
|
||||
/// Example:
|
||||
/// <pre>lua
|
||||
/// local api = vim.api
|
||||
@ -334,11 +338,13 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
|
||||
/// - limit: Maximum number of marks to return
|
||||
/// - details: Whether to include the details dict
|
||||
/// - hl_name: Whether to include highlight group name instead of id, true if omitted
|
||||
/// - overlap: Also include marks which overlap the range, even if
|
||||
/// their start position is less than `start`
|
||||
/// - type: Filter marks by type: "highlight", "sign", "virt_text" and "virt_lines"
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return List of [extmark_id, row, col] tuples in "traversal order".
|
||||
Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end, Dictionary opts,
|
||||
Error *err)
|
||||
Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end,
|
||||
Dict(get_extmarks) *opts, Error *err)
|
||||
FUNC_API_SINCE(7)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
@ -348,63 +354,32 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool all_ns;
|
||||
if (ns_id == -1) {
|
||||
all_ns = true;
|
||||
} else {
|
||||
VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
|
||||
return rv;
|
||||
});
|
||||
all_ns = false;
|
||||
}
|
||||
VALIDATE_INT(ns_id == -1 || ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
|
||||
return rv;
|
||||
});
|
||||
|
||||
bool details = opts->details;
|
||||
bool hl_name = GET_BOOL_OR_TRUE(opts, get_extmarks, hl_name);
|
||||
|
||||
Integer limit = -1;
|
||||
bool details = false;
|
||||
bool hl_name = true;
|
||||
ExtmarkType type = kExtmarkNone;
|
||||
|
||||
for (size_t i = 0; i < opts.size; i++) {
|
||||
String k = opts.items[i].key;
|
||||
Object *v = &opts.items[i].value;
|
||||
if (strequal("limit", k.data)) {
|
||||
VALIDATE_T("limit", kObjectTypeInteger, v->type, {
|
||||
return rv;
|
||||
});
|
||||
limit = v->data.integer;
|
||||
} else if (strequal("details", k.data)) {
|
||||
details = api_object_to_bool(*v, "details", false, err);
|
||||
if (ERROR_SET(err)) {
|
||||
return rv;
|
||||
}
|
||||
} else if (strequal("hl_name", k.data)) {
|
||||
hl_name = api_object_to_bool(*v, "hl_name", false, err);
|
||||
if (ERROR_SET(err)) {
|
||||
return rv;
|
||||
}
|
||||
} else if (strequal("type", k.data)) {
|
||||
VALIDATE_EXP(v->type == kObjectTypeString, "type", "String", api_typename(v->type), {
|
||||
return rv;
|
||||
});
|
||||
if (strequal(v->data.string.data, "sign")) {
|
||||
type = kExtmarkSign;
|
||||
} else if (strequal(v->data.string.data, "virt_text")) {
|
||||
type = kExtmarkVirtText;
|
||||
} else if (strequal(v->data.string.data, "virt_lines")) {
|
||||
type = kExtmarkVirtLines;
|
||||
} else if (strequal(v->data.string.data, "highlight")) {
|
||||
type = kExtmarkHighlight;
|
||||
} else {
|
||||
VALIDATE_EXP(false, "type", "sign, virt_text, virt_lines or highlight", v->data.string.data, {
|
||||
return rv;
|
||||
});
|
||||
}
|
||||
if (HAS_KEY(opts, get_extmarks, type)) {
|
||||
if (strequal(opts->type.data, "sign")) {
|
||||
type = kExtmarkSign;
|
||||
} else if (strequal(opts->type.data, "virt_text")) {
|
||||
type = kExtmarkVirtText;
|
||||
} else if (strequal(opts->type.data, "virt_lines")) {
|
||||
type = kExtmarkVirtLines;
|
||||
} else if (strequal(opts->type.data, "highlight")) {
|
||||
type = kExtmarkHighlight;
|
||||
} else {
|
||||
VALIDATE_S(false, "'opts' key", k.data, {
|
||||
VALIDATE_EXP(false, "type", "sign, virt_text, virt_lines or highlight", opts->type.data, {
|
||||
return rv;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Integer limit = HAS_KEY(opts, get_extmarks, limit) ? opts->limit : -1;
|
||||
|
||||
if (limit == 0) {
|
||||
return rv;
|
||||
} else if (limit < 0) {
|
||||
@ -429,11 +404,12 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
||||
reverse = true;
|
||||
}
|
||||
|
||||
// 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, all_ns, type);
|
||||
u_col, (int64_t)limit, reverse, type, opts->overlap);
|
||||
|
||||
for (size_t i = 0; i < kv_size(marks); i++) {
|
||||
ADD(rv, ARRAY_OBJ(extmark_to_array(&kv_A(marks, i), true, (bool)details, hl_name)));
|
||||
ADD(rv, ARRAY_OBJ(extmark_to_array(&kv_A(marks, i), true, details, hl_name)));
|
||||
}
|
||||
|
||||
kv_destroy(marks);
|
||||
@ -451,6 +427,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
||||
/// Using the optional arguments, it is possible to use this to highlight
|
||||
/// a range of text, and also to associate virtual text to the mark.
|
||||
///
|
||||
/// If present, the position defined by `end_col` and `end_row` should be after
|
||||
/// the start position in order for the extmark to cover a range.
|
||||
/// An earlier end position is not an error, but then it behaves like an empty
|
||||
/// range (no highlighting).
|
||||
///
|
||||
/// @param buffer Buffer handle, or 0 for current buffer
|
||||
/// @param ns_id Namespace id from |nvim_create_namespace()|
|
||||
/// @param line Line where to place the mark, 0-based. |api-indexing|
|
||||
@ -1230,3 +1211,14 @@ free_exit:
|
||||
clear_virttext(&virt_text);
|
||||
return virt_text;
|
||||
}
|
||||
|
||||
String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error *err)
|
||||
FUNC_API_SINCE(7)
|
||||
{
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
if (!buf) {
|
||||
return NULL_STRING;
|
||||
}
|
||||
|
||||
return mt_inspect(buf->b_marktree, keys, dot);
|
||||
}
|
||||
|
@ -50,6 +50,15 @@ typedef struct {
|
||||
Boolean ui_watched;
|
||||
} Dict(set_extmark);
|
||||
|
||||
typedef struct {
|
||||
OptionalKeys is_set__get_extmarks_;
|
||||
Integer limit;
|
||||
Boolean details;
|
||||
Boolean hl_name;
|
||||
Boolean overlap;
|
||||
String type;
|
||||
} Dict(get_extmarks);
|
||||
|
||||
typedef struct {
|
||||
OptionalKeys is_set__keymap_;
|
||||
Boolean noremap;
|
||||
|
@ -747,6 +747,7 @@ void buf_clear_file(buf_T *buf)
|
||||
void buf_clear(void)
|
||||
{
|
||||
linenr_T line_count = curbuf->b_ml.ml_line_count;
|
||||
extmark_free_all(curbuf); // delete any extmarks
|
||||
while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) {
|
||||
ml_delete((linenr_T)1, false);
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, row, 0, itr);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(itr);
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0 || mark.pos.row > row) {
|
||||
break;
|
||||
} else if (marktree_decor_level(mark) < kDecorLevelVisible) {
|
||||
@ -189,7 +189,7 @@ bool decor_redraw_reset(win_T *wp, DecorState *state)
|
||||
return wp->w_buffer->b_marktree->n_keys;
|
||||
}
|
||||
|
||||
Decoration get_decor(mtkey_t mark)
|
||||
Decoration get_decor(MTKey mark)
|
||||
{
|
||||
if (mark.decor_full) {
|
||||
return *mark.decor_full;
|
||||
@ -211,50 +211,20 @@ bool decor_redraw_start(win_T *wp, int top_row, DecorState *state)
|
||||
{
|
||||
buf_T *buf = wp->w_buffer;
|
||||
state->top_row = top_row;
|
||||
marktree_itr_get(buf->b_marktree, top_row, 0, state->itr);
|
||||
if (!state->itr->node) {
|
||||
if (!marktree_itr_get_overlap(buf->b_marktree, top_row, 0, state->itr)) {
|
||||
return false;
|
||||
}
|
||||
marktree_itr_rewind(buf->b_marktree, state->itr);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(state->itr);
|
||||
if (mark.pos.row < 0) { // || mark.row > end_row
|
||||
break;
|
||||
}
|
||||
if ((mark.pos.row < top_row && mt_end(mark))
|
||||
|| marktree_decor_level(mark) < kDecorLevelVisible) {
|
||||
goto next_mark;
|
||||
MTPair pair;
|
||||
|
||||
while (marktree_itr_step_overlap(buf->b_marktree, state->itr, &pair)) {
|
||||
if (marktree_decor_level(pair.start) < kDecorLevelVisible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Decoration decor = get_decor(mark);
|
||||
Decoration decor = get_decor(pair.start);
|
||||
|
||||
mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
||||
|
||||
// Exclude start marks if the end mark position is above the top row
|
||||
// Exclude end marks if we have already added the start mark
|
||||
if ((mt_start(mark) && altpos.row < top_row && !decor_virt_pos(&decor))
|
||||
|| (mt_end(mark) && altpos.row >= top_row)) {
|
||||
goto next_mark;
|
||||
}
|
||||
|
||||
if (mt_end(mark)) {
|
||||
decor_add(state, altpos.row, altpos.col, mark.pos.row, mark.pos.col,
|
||||
&decor, false, mark.ns, mark.id);
|
||||
} else {
|
||||
if (altpos.row == -1) {
|
||||
altpos.row = mark.pos.row;
|
||||
altpos.col = mark.pos.col;
|
||||
}
|
||||
decor_add(state, mark.pos.row, mark.pos.col, altpos.row, altpos.col,
|
||||
&decor, false, mark.ns, mark.id);
|
||||
}
|
||||
|
||||
next_mark:
|
||||
if (marktree_itr_node_done(state->itr)) {
|
||||
marktree_itr_next(buf->b_marktree, state->itr);
|
||||
break;
|
||||
}
|
||||
marktree_itr_next(buf->b_marktree, state->itr);
|
||||
decor_add(state, pair.start.pos.row, pair.start.pos.col, pair.end_pos.row, pair.end_pos.col,
|
||||
&decor, false, pair.start.ns, pair.start.id);
|
||||
}
|
||||
|
||||
return true; // TODO(bfredl): check if available in the region
|
||||
@ -268,7 +238,13 @@ bool decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
state->row = row;
|
||||
state->col_until = -1;
|
||||
state->eol_col = -1;
|
||||
return true; // TODO(bfredl): be more precise
|
||||
|
||||
if (kv_size(state->active)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MTKey k = marktree_itr_current(state->itr);
|
||||
return (k.pos.row >= 0 && k.pos.row <= row);
|
||||
}
|
||||
|
||||
static void decor_add(DecorState *state, int start_row, int start_col, int end_row, int end_col,
|
||||
@ -302,7 +278,7 @@ int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *s
|
||||
while (true) {
|
||||
// TODO(bfredl): check duplicate entry in "intersection"
|
||||
// branch
|
||||
mtkey_t mark = marktree_itr_current(state->itr);
|
||||
MTKey mark = marktree_itr_current(state->itr);
|
||||
if (mark.pos.row < 0 || mark.pos.row > state->row) {
|
||||
break;
|
||||
} else if (mark.pos.row == state->row && mark.pos.col > col) {
|
||||
@ -317,8 +293,7 @@ int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *s
|
||||
|
||||
Decoration decor = get_decor(mark);
|
||||
|
||||
mtpos_t endpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
||||
|
||||
MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
||||
if (endpos.row == -1) {
|
||||
endpos = mark.pos;
|
||||
}
|
||||
@ -412,8 +387,28 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattr
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, row, 0, itr);
|
||||
|
||||
// TODO(bfredl): integrate with main decor loop.
|
||||
if (!marktree_itr_get_overlap(buf->b_marktree, row, 0, itr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MTPair pair;
|
||||
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
|
||||
if (marktree_decor_level(pair.start) < kDecorLevelVisible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Decoration *decor = pair.start.decor_full;
|
||||
|
||||
if (!decor || !decor_has_sign(decor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
decor_to_sign(decor, num_signs, sattrs, num_id, line_id, cul_id);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(itr);
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0 || mark.pos.row > row) {
|
||||
break;
|
||||
}
|
||||
@ -428,46 +423,52 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattr
|
||||
goto next_mark;
|
||||
}
|
||||
|
||||
if (decor->sign_text) {
|
||||
int j;
|
||||
for (j = (*num_signs); j > 0; j--) {
|
||||
if (sattrs[j - 1].priority >= decor->priority) {
|
||||
break;
|
||||
}
|
||||
if (j < SIGN_SHOW_MAX) {
|
||||
sattrs[j] = sattrs[j - 1];
|
||||
}
|
||||
}
|
||||
if (j < SIGN_SHOW_MAX) {
|
||||
sattrs[j] = (SignTextAttrs) {
|
||||
.text = decor->sign_text,
|
||||
.hl_id = decor->sign_hl_id,
|
||||
.priority = decor->priority
|
||||
};
|
||||
(*num_signs)++;
|
||||
}
|
||||
}
|
||||
|
||||
struct { HlPriId *dest; int hl; } cattrs[] = {
|
||||
{ line_id, decor->line_hl_id },
|
||||
{ num_id, decor->number_hl_id },
|
||||
{ cul_id, decor->cursorline_hl_id },
|
||||
{ NULL, -1 },
|
||||
};
|
||||
for (int i = 0; cattrs[i].dest; i++) {
|
||||
if (cattrs[i].hl != 0 && decor->priority >= cattrs[i].dest->priority) {
|
||||
*cattrs[i].dest = (HlPriId) {
|
||||
.hl_id = cattrs[i].hl,
|
||||
.priority = decor->priority
|
||||
};
|
||||
}
|
||||
}
|
||||
decor_to_sign(decor, num_signs, sattrs, num_id, line_id, cul_id);
|
||||
|
||||
next_mark:
|
||||
marktree_itr_next(buf->b_marktree, itr);
|
||||
}
|
||||
}
|
||||
|
||||
static void decor_to_sign(Decoration *decor, int *num_signs, SignTextAttrs sattrs[],
|
||||
HlPriId *num_id, HlPriId *line_id, HlPriId *cul_id)
|
||||
{
|
||||
if (decor->sign_text) {
|
||||
int j;
|
||||
for (j = (*num_signs); j > 0; j--) {
|
||||
if (sattrs[j - 1].priority >= decor->priority) {
|
||||
break;
|
||||
}
|
||||
if (j < SIGN_SHOW_MAX) {
|
||||
sattrs[j] = sattrs[j - 1];
|
||||
}
|
||||
}
|
||||
if (j < SIGN_SHOW_MAX) {
|
||||
sattrs[j] = (SignTextAttrs) {
|
||||
.text = decor->sign_text,
|
||||
.hl_id = decor->sign_hl_id,
|
||||
.priority = decor->priority
|
||||
};
|
||||
(*num_signs)++;
|
||||
}
|
||||
}
|
||||
|
||||
struct { HlPriId *dest; int hl; } cattrs[] = {
|
||||
{ line_id, decor->line_hl_id },
|
||||
{ num_id, decor->number_hl_id },
|
||||
{ cul_id, decor->cursorline_hl_id },
|
||||
{ NULL, -1 },
|
||||
};
|
||||
for (int i = 0; cattrs[i].dest; i++) {
|
||||
if (cattrs[i].hl != 0 && decor->priority >= cattrs[i].dest->priority) {
|
||||
*cattrs[i].dest = (HlPriId) {
|
||||
.hl_id = cattrs[i].hl,
|
||||
.priority = decor->priority
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the maximum required amount of sign columns needed between row and
|
||||
// end_row.
|
||||
int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max)
|
||||
@ -488,7 +489,7 @@ int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max)
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, 0, -1, itr);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(itr);
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0 || mark.pos.row > end_row) {
|
||||
break;
|
||||
}
|
||||
@ -525,7 +526,7 @@ int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max)
|
||||
goto next_mark;
|
||||
}
|
||||
|
||||
mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
||||
MTPos altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
|
||||
|
||||
if (mt_end(mark)) {
|
||||
if (mark.pos.row >= row && altpos.row <= end_row) {
|
||||
@ -610,7 +611,7 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fo
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, start_row, 0, itr);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(itr);
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0 || mark.pos.row >= end_row) {
|
||||
break;
|
||||
} else if (mt_end(mark)
|
||||
|
@ -2889,15 +2889,21 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
|
||||
&& !wp->w_p_wrap
|
||||
&& wlv.filler_todo <= 0
|
||||
&& (wp->w_p_rl ? wlv.col == 0 : wlv.col == grid->cols - 1)
|
||||
&& !has_fold
|
||||
&& (*ptr != NUL
|
||||
|| lcs_eol_one > 0
|
||||
|| (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL))
|
||||
|| wlv.more_virt_inline_chunks)) {
|
||||
c = wp->w_p_lcs_chars.ext;
|
||||
wlv.char_attr = win_hl_attr(wp, HLF_AT);
|
||||
mb_c = c;
|
||||
mb_utf8 = check_mb_utf8(&c, u8cc);
|
||||
&& !has_fold) {
|
||||
if (*ptr == NUL && lcs_eol_one == 0 && has_decor) {
|
||||
// Tricky: there might be a virtual text just _after_ the last char
|
||||
decor_redraw_col(wp, (colnr_T)v, wlv.off, false, &decor_state);
|
||||
handle_inline_virtual_text(wp, &wlv, v);
|
||||
}
|
||||
if (*ptr != NUL
|
||||
|| lcs_eol_one > 0
|
||||
|| (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL))
|
||||
|| wlv.more_virt_inline_chunks) {
|
||||
c = wp->w_p_lcs_chars.ext;
|
||||
wlv.char_attr = win_hl_attr(wp, HLF_AT);
|
||||
mb_c = c;
|
||||
mb_utf8 = check_mb_utf8(&c, u8cc);
|
||||
}
|
||||
}
|
||||
|
||||
// advance to the next 'colorcolumn'
|
||||
@ -3079,6 +3085,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
|
||||
wlv.char_attr = saved_attr2;
|
||||
}
|
||||
|
||||
if ((wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols)) && has_decor) {
|
||||
// At the end of screen line: might need to peek for decorations just after
|
||||
// this position. Without wrapping, we might need to display win_pos overlays
|
||||
// from the entire text line.
|
||||
colnr_T nextpos = wp->w_p_wrap ? (colnr_T)(ptr - line) : (colnr_T)strlen(line);
|
||||
decor_redraw_col(wp, nextpos, wlv.off, true, &decor_state);
|
||||
handle_inline_virtual_text(wp, &wlv, v);
|
||||
}
|
||||
|
||||
// At end of screen line and there is more to come: Display the line
|
||||
// so far. If there is no more to display it is caught above.
|
||||
if ((wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols))
|
||||
|
@ -2410,6 +2410,9 @@ static void cmdpreview_restore_state(CpInfo *cpinfo)
|
||||
|
||||
buf->b_changed = cp_bufinfo.save_b_changed;
|
||||
|
||||
// Clear preview highlights.
|
||||
extmark_clear(buf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
|
||||
|
||||
if (buf->b_u_seq_cur != cp_bufinfo.undo_info.save_b_u_seq_cur) {
|
||||
int count = 0;
|
||||
|
||||
@ -2439,9 +2442,6 @@ static void cmdpreview_restore_state(CpInfo *cpinfo)
|
||||
}
|
||||
|
||||
buf->b_p_ul = cp_bufinfo.save_b_p_ul; // Restore 'undolevels'
|
||||
|
||||
// Clear preview highlights.
|
||||
extmark_clear(buf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cpinfo->win_info.size; i++) {
|
||||
|
@ -82,7 +82,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
|
||||
id = ++*ns;
|
||||
} else {
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
mtkey_t old_mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr);
|
||||
MTKey old_mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr);
|
||||
if (old_mark.id) {
|
||||
if (decor_state.running_on_lines) {
|
||||
if (err) {
|
||||
@ -124,8 +124,8 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
|
||||
}
|
||||
}
|
||||
|
||||
mtkey_t mark = { { row, col }, ns_id, id, 0,
|
||||
mt_flags(right_gravity, decor_level), 0, NULL };
|
||||
MTKey mark = { { row, col }, ns_id, id, 0,
|
||||
mt_flags(right_gravity, decor_level), 0, NULL };
|
||||
if (decor_full) {
|
||||
mark.decor_full = decor;
|
||||
} else if (decor) {
|
||||
@ -180,7 +180,7 @@ error:
|
||||
static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col)
|
||||
{
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
mtkey_t key = marktree_lookup(buf->b_marktree, mark, itr);
|
||||
MTKey key = marktree_lookup(buf->b_marktree, mark, itr);
|
||||
if (key.pos.row == -1) {
|
||||
return false;
|
||||
}
|
||||
@ -199,14 +199,14 @@ static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col)
|
||||
bool extmark_del(buf_T *buf, uint32_t ns_id, uint32_t id)
|
||||
{
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
mtkey_t key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr);
|
||||
MTKey key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr);
|
||||
if (!key.id) {
|
||||
return false;
|
||||
}
|
||||
assert(key.pos.row >= 0);
|
||||
uint64_t other = marktree_del_itr(buf->b_marktree, itr, false);
|
||||
|
||||
mtkey_t key2 = key;
|
||||
MTKey key2 = key;
|
||||
|
||||
if (other) {
|
||||
key2 = marktree_lookup(buf->b_marktree, other, itr);
|
||||
@ -250,7 +250,7 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, l_row, l_col, itr);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(itr);
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0
|
||||
|| mark.pos.row > u_row
|
||||
|| (mark.pos.row == u_row && mark.pos.col > u_col)) {
|
||||
@ -292,7 +292,7 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r
|
||||
uint64_t id;
|
||||
ssize_t decor_id;
|
||||
map_foreach(&delete_set, id, decor_id, {
|
||||
mtkey_t mark = marktree_lookup(buf->b_marktree, id, itr);
|
||||
MTKey mark = marktree_lookup(buf->b_marktree, id, itr);
|
||||
assert(marktree_itr_valid(itr));
|
||||
marktree_del_itr(buf->b_marktree, itr, false);
|
||||
if (decor_id >= 0) {
|
||||
@ -313,17 +313,31 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r
|
||||
/// dir can be set to control the order of the array
|
||||
/// amount = amount of marks to find or -1 for all
|
||||
ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row,
|
||||
colnr_T u_col, int64_t amount, bool reverse, bool all_ns,
|
||||
ExtmarkType type_filter)
|
||||
colnr_T u_col, int64_t amount, bool reverse, ExtmarkType type_filter,
|
||||
bool overlap)
|
||||
{
|
||||
ExtmarkInfoArray array = KV_INITIAL_VALUE;
|
||||
MarkTreeIter itr[1];
|
||||
// Find all the marks
|
||||
marktree_itr_get_ext(buf->b_marktree, mtpos_t(l_row, l_col),
|
||||
itr, reverse, false, NULL);
|
||||
|
||||
if (overlap) {
|
||||
// Find all the marks overlapping the start position
|
||||
if (!marktree_itr_get_overlap(buf->b_marktree, l_row, l_col, itr)) {
|
||||
return array;
|
||||
}
|
||||
|
||||
MTPair pair;
|
||||
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
|
||||
push_mark(&array, ns_id, type_filter, pair.start, pair.end_pos, pair.end_right_gravity);
|
||||
}
|
||||
} else {
|
||||
// Find all the marks beginning with the start position
|
||||
marktree_itr_get_ext(buf->b_marktree, MTPos(l_row, l_col),
|
||||
itr, reverse, false, NULL);
|
||||
}
|
||||
|
||||
int order = reverse ? -1 : 1;
|
||||
while ((int64_t)kv_size(array) < amount) {
|
||||
mtkey_t mark = marktree_itr_current(itr);
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0
|
||||
|| (mark.pos.row - u_row) * order > 0
|
||||
|| (mark.pos.row == u_row && (mark.pos.col - u_col) * order > 0)) {
|
||||
@ -333,35 +347,8 @@ ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_co
|
||||
goto next_mark;
|
||||
}
|
||||
|
||||
uint16_t type_flags = kExtmarkNone;
|
||||
if (type_filter != kExtmarkNone) {
|
||||
Decoration *decor = mark.decor_full;
|
||||
if (decor && (decor->sign_text || decor->number_hl_id)) {
|
||||
type_flags |= kExtmarkSign;
|
||||
}
|
||||
if (decor && decor->virt_text.size) {
|
||||
type_flags |= kExtmarkVirtText;
|
||||
}
|
||||
if (decor && decor->virt_lines.size) {
|
||||
type_flags |= kExtmarkVirtLines;
|
||||
}
|
||||
if ((decor && (decor->line_hl_id || decor->cursorline_hl_id))
|
||||
|| mark.hl_id) {
|
||||
type_flags |= kExtmarkHighlight;
|
||||
}
|
||||
}
|
||||
|
||||
if ((all_ns || mark.ns == ns_id) && type_flags & type_filter) {
|
||||
mtkey_t end = marktree_get_alt(buf->b_marktree, mark, NULL);
|
||||
kv_push(array, ((ExtmarkInfo) { .ns_id = mark.ns,
|
||||
.mark_id = mark.id,
|
||||
.row = mark.pos.row, .col = mark.pos.col,
|
||||
.end_row = end.pos.row,
|
||||
.end_col = end.pos.col,
|
||||
.right_gravity = mt_right(mark),
|
||||
.end_right_gravity = mt_right(end),
|
||||
.decor = get_decor(mark) }));
|
||||
}
|
||||
MTKey end = marktree_get_alt(buf->b_marktree, mark, NULL);
|
||||
push_mark(&array, ns_id, type_filter, mark, end.pos, mt_right(end));
|
||||
next_mark:
|
||||
if (reverse) {
|
||||
marktree_itr_prev(buf->b_marktree, itr);
|
||||
@ -372,16 +359,54 @@ next_mark:
|
||||
return array;
|
||||
}
|
||||
|
||||
static void push_mark(ExtmarkInfoArray *array, uint32_t ns_id, ExtmarkType type_filter, MTKey mark,
|
||||
MTPos end_pos, bool end_right)
|
||||
{
|
||||
if (!(ns_id == UINT32_MAX || mark.ns == ns_id)) {
|
||||
return;
|
||||
}
|
||||
uint16_t type_flags = kExtmarkNone;
|
||||
if (type_filter != kExtmarkNone) {
|
||||
Decoration *decor = mark.decor_full;
|
||||
if (decor && (decor->sign_text || decor->number_hl_id)) {
|
||||
type_flags |= kExtmarkSign;
|
||||
}
|
||||
if (decor && decor->virt_text.size) {
|
||||
type_flags |= kExtmarkVirtText;
|
||||
}
|
||||
if (decor && decor->virt_lines.size) {
|
||||
type_flags |= kExtmarkVirtLines;
|
||||
}
|
||||
if ((decor && (decor->line_hl_id || decor->cursorline_hl_id))
|
||||
|| mark.hl_id) {
|
||||
type_flags |= kExtmarkHighlight;
|
||||
}
|
||||
|
||||
if (!(type_flags & type_filter)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
kv_push(*array, ((ExtmarkInfo) { .ns_id = mark.ns,
|
||||
.mark_id = mark.id,
|
||||
.row = mark.pos.row, .col = mark.pos.col,
|
||||
.end_row = end_pos.row,
|
||||
.end_col = end_pos.col,
|
||||
.right_gravity = mt_right(mark),
|
||||
.end_right_gravity = end_right,
|
||||
.decor = get_decor(mark) }));
|
||||
}
|
||||
|
||||
/// Lookup an extmark by id
|
||||
ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id)
|
||||
{
|
||||
ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, false, false, DECORATION_INIT };
|
||||
mtkey_t mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, NULL);
|
||||
MTKey mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, NULL);
|
||||
if (!mark.id) {
|
||||
return ret;
|
||||
}
|
||||
assert(mark.pos.row >= 0);
|
||||
mtkey_t end = marktree_get_alt(buf->b_marktree, mark, NULL);
|
||||
MTKey end = marktree_get_alt(buf->b_marktree, mark, NULL);
|
||||
|
||||
ret.ns_id = ns_id;
|
||||
ret.mark_id = id;
|
||||
@ -406,7 +431,7 @@ void extmark_free_all(buf_T *buf)
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, 0, 0, itr);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(itr);
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0) {
|
||||
break;
|
||||
}
|
||||
@ -462,7 +487,7 @@ void u_extmark_copy(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_c
|
||||
MarkTreeIter itr[1] = { 0 };
|
||||
marktree_itr_get(buf->b_marktree, (int32_t)l_row, l_col, itr);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(itr);
|
||||
MTKey mark = marktree_itr_current(itr);
|
||||
if (mark.pos.row < 0
|
||||
|| mark.pos.row > u_row
|
||||
|| (mark.pos.row == u_row && mark.pos.col > u_col)) {
|
||||
|
1600
src/nvim/marktree.c
1600
src/nvim/marktree.c
File diff suppressed because it is too large
Load Diff
@ -6,29 +6,37 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "klib/kvec.h"
|
||||
#include "nvim/assert.h"
|
||||
#include "nvim/garray.h"
|
||||
#include "nvim/map.h"
|
||||
#include "nvim/pos.h"
|
||||
#include "nvim/types.h"
|
||||
|
||||
// only for debug functions:
|
||||
#include "api/private/defs.h"
|
||||
|
||||
struct mtnode_s;
|
||||
|
||||
#define MT_MAX_DEPTH 20
|
||||
#define MT_BRANCH_FACTOR 10
|
||||
// note max branch is actually 2*MT_BRANCH_FACTOR
|
||||
// and strictly this is ceil(log2(2*MT_BRANCH_FACTOR + 1))
|
||||
// as we need a pseudo-index for "right before this node"
|
||||
#define MT_LOG2_BRANCH 5
|
||||
|
||||
typedef struct {
|
||||
int32_t row;
|
||||
int32_t col;
|
||||
} mtpos_t;
|
||||
#define mtpos_t(r, c) ((mtpos_t){ .row = (r), .col = (c) })
|
||||
} MTPos;
|
||||
#define MTPos(r, c) ((MTPos){ .row = (r), .col = (c) })
|
||||
|
||||
typedef struct mtnode_s mtnode_t;
|
||||
typedef struct mtnode_s MTNode;
|
||||
|
||||
typedef struct {
|
||||
mtpos_t pos;
|
||||
MTPos pos;
|
||||
int lvl;
|
||||
mtnode_t *node;
|
||||
MTNode *x;
|
||||
int i;
|
||||
struct {
|
||||
int oldcol;
|
||||
@ -36,33 +44,43 @@ typedef struct {
|
||||
} s[MT_MAX_DEPTH];
|
||||
|
||||
size_t intersect_idx;
|
||||
mtpos_t intersect_pos;
|
||||
MTPos intersect_pos;
|
||||
MTPos intersect_pos_x;
|
||||
} MarkTreeIter;
|
||||
|
||||
#define marktree_itr_valid(itr) ((itr)->node != NULL)
|
||||
#define marktree_itr_valid(itr) ((itr)->x != NULL)
|
||||
|
||||
// Internal storage
|
||||
//
|
||||
// NB: actual marks have flags > 0, so we can use (row,col,0) pseudo-key for
|
||||
// "space before (row,col)"
|
||||
typedef struct {
|
||||
mtpos_t pos;
|
||||
MTPos pos;
|
||||
uint32_t ns;
|
||||
uint32_t id;
|
||||
int32_t hl_id;
|
||||
uint16_t flags;
|
||||
uint16_t priority;
|
||||
Decoration *decor_full;
|
||||
} mtkey_t;
|
||||
#define MT_INVALID_KEY (mtkey_t) { { -1, -1 }, 0, 0, 0, 0, 0, NULL }
|
||||
} MTKey;
|
||||
|
||||
typedef struct {
|
||||
MTKey start;
|
||||
MTPos end_pos;
|
||||
bool end_right_gravity;
|
||||
} MTPair;
|
||||
|
||||
#define MT_INVALID_KEY (MTKey) { { -1, -1 }, 0, 0, 0, 0, 0, NULL }
|
||||
|
||||
#define MT_FLAG_REAL (((uint16_t)1) << 0)
|
||||
#define MT_FLAG_END (((uint16_t)1) << 1)
|
||||
#define MT_FLAG_PAIRED (((uint16_t)1) << 2)
|
||||
#define MT_FLAG_HL_EOL (((uint16_t)1) << 3)
|
||||
// orphaned: the other side of this paired mark was deleted. this mark must be deleted very soon!
|
||||
#define MT_FLAG_ORPHANED (((uint16_t)1) << 3)
|
||||
#define MT_FLAG_HL_EOL (((uint16_t)1) << 4)
|
||||
|
||||
#define DECOR_LEVELS 4
|
||||
#define MT_FLAG_DECOR_OFFSET 4
|
||||
#define MT_FLAG_DECOR_OFFSET 5
|
||||
#define MT_FLAG_DECOR_MASK (((uint16_t)(DECOR_LEVELS - 1)) << MT_FLAG_DECOR_OFFSET)
|
||||
|
||||
// next flag is (((uint16_t)1) << 6)
|
||||
@ -73,39 +91,44 @@ typedef struct {
|
||||
|
||||
#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_RIGHT_GRAVITY | MT_FLAG_HL_EOL)
|
||||
|
||||
#define MARKTREE_END_FLAG (((uint64_t)1) << 63)
|
||||
// this is defined so that start and end of the same range have adjacent ids
|
||||
#define MARKTREE_END_FLAG ((uint64_t)1)
|
||||
static inline uint64_t mt_lookup_id(uint32_t ns, uint32_t id, bool enda)
|
||||
{
|
||||
return (uint64_t)ns << 32 | id | (enda ? MARKTREE_END_FLAG : 0);
|
||||
return (uint64_t)ns << 33 | (id <<1) | (enda ? MARKTREE_END_FLAG : 0);
|
||||
}
|
||||
#undef MARKTREE_END_FLAG
|
||||
|
||||
static inline uint64_t mt_lookup_key(mtkey_t key)
|
||||
static inline uint64_t mt_lookup_key_side(MTKey key, bool end)
|
||||
{
|
||||
return mt_lookup_id(key.ns, key.id, end);
|
||||
}
|
||||
|
||||
static inline uint64_t mt_lookup_key(MTKey key)
|
||||
{
|
||||
return mt_lookup_id(key.ns, key.id, key.flags & MT_FLAG_END);
|
||||
}
|
||||
|
||||
static inline bool mt_paired(mtkey_t key)
|
||||
static inline bool mt_paired(MTKey key)
|
||||
{
|
||||
return key.flags & MT_FLAG_PAIRED;
|
||||
}
|
||||
|
||||
static inline bool mt_end(mtkey_t key)
|
||||
static inline bool mt_end(MTKey key)
|
||||
{
|
||||
return key.flags & MT_FLAG_END;
|
||||
}
|
||||
|
||||
static inline bool mt_start(mtkey_t key)
|
||||
static inline bool mt_start(MTKey key)
|
||||
{
|
||||
return mt_paired(key) && !mt_end(key);
|
||||
}
|
||||
|
||||
static inline bool mt_right(mtkey_t key)
|
||||
static inline bool mt_right(MTKey key)
|
||||
{
|
||||
return key.flags & MT_FLAG_RIGHT_GRAVITY;
|
||||
}
|
||||
|
||||
static inline uint8_t marktree_decor_level(mtkey_t key)
|
||||
static inline uint8_t marktree_decor_level(MTKey key)
|
||||
{
|
||||
return (uint8_t)((key.flags&MT_FLAG_DECOR_MASK) >> MT_FLAG_DECOR_OFFSET);
|
||||
}
|
||||
@ -117,18 +140,27 @@ static inline uint16_t mt_flags(bool right_gravity, uint8_t decor_level)
|
||||
| (decor_level << MT_FLAG_DECOR_OFFSET));
|
||||
}
|
||||
|
||||
typedef kvec_withinit_t(uint64_t, 4) Intersection;
|
||||
|
||||
struct mtnode_s {
|
||||
int32_t n;
|
||||
int32_t level;
|
||||
int16_t level;
|
||||
int16_t p_idx; // index in parent
|
||||
Intersection intersect;
|
||||
// TODO(bfredl): we could consider having a only-sometimes-valid
|
||||
// index into parent for faster "cached" lookup.
|
||||
mtnode_t *parent;
|
||||
mtkey_t key[2 * MT_BRANCH_FACTOR - 1];
|
||||
mtnode_t *ptr[];
|
||||
MTNode *parent;
|
||||
MTKey key[2 * MT_BRANCH_FACTOR - 1];
|
||||
MTNode *ptr[];
|
||||
};
|
||||
|
||||
static inline uint64_t mt_dbg_id(uint64_t id)
|
||||
{
|
||||
return (id>>1)&0xffffffff;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
mtnode_t *root;
|
||||
MTNode *root;
|
||||
size_t n_keys, n_nodes;
|
||||
// TODO(bfredl): the pointer to node could be part of the larger
|
||||
// Map(uint64_t, ExtmarkItem) essentially;
|
||||
|
@ -630,6 +630,7 @@ EXTERN unsigned rdb_flags;
|
||||
#define RDB_NODELTA 0x008
|
||||
#define RDB_LINE 0x010
|
||||
#define RDB_FLUSH 0x020
|
||||
#define RDB_INTERSECT 0x040
|
||||
|
||||
EXTERN long p_rdt; // 'redrawtime'
|
||||
EXTERN long p_re; // 'regexpengine'
|
||||
|
@ -133,7 +133,7 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T
|
||||
|
||||
if (cts->cts_row >= 0 && wp->w_buffer->b_virt_text_inline > 0) {
|
||||
marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter);
|
||||
mtkey_t mark = marktree_itr_current(cts->cts_iter);
|
||||
MTKey mark = marktree_itr_current(cts->cts_iter);
|
||||
if (mark.pos.row == cts->cts_row) {
|
||||
cts->cts_has_virt_text = true;
|
||||
}
|
||||
@ -222,7 +222,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
|
||||
int tab_size = size;
|
||||
int col = (int)(s - line);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(cts->cts_iter);
|
||||
MTKey mark = marktree_itr_current(cts->cts_iter);
|
||||
if (mark.pos.row != cts->cts_row || mark.pos.col > col) {
|
||||
break;
|
||||
} else if (mark.pos.col == col) {
|
||||
|
@ -7,6 +7,9 @@
|
||||
// defined in version.c
|
||||
extern char *Version;
|
||||
extern char *longVersion;
|
||||
#ifndef NDEBUG
|
||||
extern char *version_cflags;
|
||||
#endif
|
||||
|
||||
//
|
||||
// Vim version number, name, etc. Patchlevel is defined in version.c.
|
||||
|
@ -753,7 +753,14 @@ describe('API/extmarks', function()
|
||||
})
|
||||
end)
|
||||
|
||||
-- TODO(bfredl): add more tests!
|
||||
it('can get overlapping extmarks', function()
|
||||
set_extmark(ns, 1, 0, 0, {end_row = 5, end_col=0})
|
||||
set_extmark(ns, 2, 2, 5, {end_row = 2, end_col=30})
|
||||
set_extmark(ns, 3, 0, 5, {end_row = 2, end_col=10})
|
||||
set_extmark(ns, 4, 0, 0, {end_row = 1, end_col=0})
|
||||
eq({{ 2, 2, 5 }}, get_extmarks(ns, {2, 0}, {2, -1}, { overlap=false }))
|
||||
eq({{ 1, 0, 0 }, { 3, 0, 5}, {2, 2, 5}}, get_extmarks(ns, {2, 0}, {2, -1}, { overlap=true }))
|
||||
end)
|
||||
end)
|
||||
|
||||
it('replace works', function()
|
||||
|
@ -857,6 +857,11 @@ function module.testprg(name)
|
||||
return ('%s/%s%s'):format(module.nvim_dir, name, ext)
|
||||
end
|
||||
|
||||
function module.is_asan()
|
||||
local version = module.eval('execute("verbose version")')
|
||||
return version:match('-fsanitize=[a-z,]*address')
|
||||
end
|
||||
|
||||
-- Returns a valid, platform-independent Nvim listen address.
|
||||
-- Useful for communicating with child instances.
|
||||
function module.new_pipename()
|
||||
|
@ -11,14 +11,10 @@ local load_adjust = helpers.load_adjust
|
||||
local write_file = helpers.write_file
|
||||
local is_os = helpers.is_os
|
||||
local is_ci = helpers.is_ci
|
||||
|
||||
local function isasan()
|
||||
local version = eval('execute("verbose version")')
|
||||
return version:match('-fsanitize=[a-z,]*address')
|
||||
end
|
||||
local is_asan = helpers.is_asan
|
||||
|
||||
clear()
|
||||
if isasan() then
|
||||
if is_asan() then
|
||||
pending('ASAN build is difficult to estimate memory usage', function() end)
|
||||
return
|
||||
elseif is_os('win') then
|
||||
|
@ -691,6 +691,7 @@ describe('extmark decorations', function()
|
||||
[33] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray};
|
||||
[34] = {background = Screen.colors.Yellow};
|
||||
[35] = {background = Screen.colors.Yellow, bold = true, foreground = Screen.colors.Blue};
|
||||
[36] = {foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.Red};
|
||||
}
|
||||
|
||||
ns = meths.create_namespace 'test'
|
||||
@ -1652,6 +1653,70 @@ describe('extmark decorations', function()
|
||||
{24:-- VISUAL BLOCK --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('supports multiline highlights', function()
|
||||
insert(example_text)
|
||||
feed 'gg'
|
||||
for _,i in ipairs {1,2,3,5,6,7} do
|
||||
for _,j in ipairs {2,5,10,15} do
|
||||
meths.buf_set_extmark(0, ns, i, j, { end_col=j+2, hl_group = 'NonText'})
|
||||
end
|
||||
end
|
||||
screen:expect{grid=[[
|
||||
^for _,item in ipairs(items) do |
|
||||
{1: }l{1:oc}al {1:te}xt,{1: h}l_id_cell, count = unpack(item) |
|
||||
{1: }i{1:f }hl_{1:id}_ce{1:ll} ~= nil then |
|
||||
{1: } {1: } hl{1:_i}d ={1: h}l_id_cell |
|
||||
end |
|
||||
{1: }f{1:or} _ {1:= }1, {1:(c}ount or 1) do |
|
||||
{1: } {1: } lo{1:ca}l c{1:el}l = line[colpos] |
|
||||
{1: } {1: } ce{1:ll}.te{1:xt} = text |
|
||||
cell.hl_id = hl_id |
|
||||
colpos = colpos+1 |
|
||||
end |
|
||||
end |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]]}
|
||||
feed'5<c-e>'
|
||||
screen:expect{grid=[[
|
||||
^ {1: }f{1:or} _ {1:= }1, {1:(c}ount or 1) do |
|
||||
{1: } {1: } lo{1:ca}l c{1:el}l = line[colpos] |
|
||||
{1: } {1: } ce{1:ll}.te{1:xt} = text |
|
||||
cell.hl_id = hl_id |
|
||||
colpos = colpos+1 |
|
||||
end |
|
||||
end |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]]}
|
||||
|
||||
meths.buf_set_extmark(0, ns, 1, 0, { end_line=8, end_col=10, hl_group = 'ErrorMsg'})
|
||||
screen:expect{grid=[[
|
||||
{4:^ }{36: }{4:f}{36:or}{4: _ }{36:= }{4:1, }{36:(c}{4:ount or 1) do} |
|
||||
{4: }{36: }{4: }{36: }{4: lo}{36:ca}{4:l c}{36:el}{4:l = line[colpos]} |
|
||||
{4: }{36: }{4: }{36: }{4: ce}{36:ll}{4:.te}{36:xt}{4: = text} |
|
||||
{4: ce}ll.hl_id = hl_id |
|
||||
colpos = colpos+1 |
|
||||
end |
|
||||
end |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]]}
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('decorations: inline virtual text', function()
|
||||
@ -4136,7 +4201,6 @@ l5
|
||||
end)
|
||||
|
||||
it('can add multiple signs (single extmark)', function()
|
||||
pending('TODO(lewis6991): Support ranged signs')
|
||||
insert(example_test3)
|
||||
feed 'gg'
|
||||
|
||||
@ -4158,7 +4222,6 @@ l5
|
||||
end)
|
||||
|
||||
it('can add multiple signs (multiple extmarks)', function()
|
||||
pending('TODO(lewis6991): Support ranged signs')
|
||||
insert(example_test3)
|
||||
feed'gg'
|
||||
|
||||
@ -4219,7 +4282,6 @@ l5
|
||||
end)
|
||||
|
||||
it('can add multiple signs (multiple extmarks) 3', function()
|
||||
pending('TODO(lewis6991): Support ranged signs')
|
||||
|
||||
insert(example_test3)
|
||||
feed 'gg'
|
||||
@ -4289,7 +4351,6 @@ l5
|
||||
end)
|
||||
|
||||
it('works with old signs (with range)', function()
|
||||
pending('TODO(lewis6991): Support ranged signs')
|
||||
insert(example_test3)
|
||||
feed 'gg'
|
||||
|
||||
@ -4304,7 +4365,7 @@ l5
|
||||
|
||||
screen:expect{grid=[[
|
||||
S3S4S1^l1 |
|
||||
S2S3x l2 |
|
||||
x S2S3l2 |
|
||||
S5S3{1: }l3 |
|
||||
S3{1: }l4 |
|
||||
S3{1: }l5 |
|
||||
@ -4317,8 +4378,6 @@ l5
|
||||
end)
|
||||
|
||||
it('can add a ranged sign (with start out of view)', function()
|
||||
pending('TODO(lewis6991): Support ranged signs')
|
||||
|
||||
insert(example_test3)
|
||||
command 'set signcolumn=yes:2'
|
||||
feed 'gg'
|
||||
|
@ -849,6 +849,16 @@ local function ptr2key(ptr)
|
||||
return ffi.string(s)
|
||||
end
|
||||
|
||||
local function is_asan()
|
||||
cimport('./src/nvim/version.h')
|
||||
local status, res = pcall(function() return lib.version_cflags end)
|
||||
if status then
|
||||
return ffi.string(res):match('-fsanitize=[a-z,]*address')
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local module = {
|
||||
cimport = cimport,
|
||||
cppimport = cppimport,
|
||||
@ -876,6 +886,7 @@ local module = {
|
||||
ptr2addr = ptr2addr,
|
||||
ptr2key = ptr2key,
|
||||
debug_log = debug_log,
|
||||
is_asan = is_asan,
|
||||
}
|
||||
module = global_helpers.tbl_extend('error', module, global_helpers)
|
||||
return function()
|
||||
|
@ -87,13 +87,18 @@ local function dosplice(tree, shadow, start, old_extent, new_extent)
|
||||
shadowsplice(shadow, start, old_extent, new_extent)
|
||||
end
|
||||
|
||||
local ns = 10
|
||||
local last_id = nil
|
||||
|
||||
local function put(tree, row, col, gravitate)
|
||||
local function put(tree, row, col, gravitate, end_row, end_col, end_gravitate)
|
||||
last_id = last_id + 1
|
||||
local my_id = last_id
|
||||
|
||||
lib.marktree_put_test(tree, my_id, row, col, gravitate);
|
||||
end_row = end_row or -1
|
||||
end_col = end_col or -1
|
||||
end_gravitate = end_gravitate or false
|
||||
|
||||
lib.marktree_put_test(tree, ns, my_id, row, col, gravitate, end_row, end_col, end_gravitate);
|
||||
return my_id
|
||||
end
|
||||
|
||||
@ -102,7 +107,7 @@ describe('marktree', function()
|
||||
last_id = 0
|
||||
end)
|
||||
|
||||
itp('works', function()
|
||||
itp('works', function()
|
||||
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
|
||||
local shadow = {}
|
||||
local iter = ffi.new("MarkTreeIter[1]")
|
||||
@ -129,7 +134,7 @@ describe('marktree', function()
|
||||
eq({}, id2pos)
|
||||
|
||||
for i,ipos in pairs(shadow) do
|
||||
local p = lib.marktree_lookup_ns(tree, -1, i, false, iter)
|
||||
local p = lib.marktree_lookup_ns(tree, ns, i, false, iter)
|
||||
eq(ipos[1], p.pos.row)
|
||||
eq(ipos[2], p.pos.col)
|
||||
local k = lib.marktree_itr_current(iter)
|
||||
@ -210,10 +215,224 @@ describe('marktree', function()
|
||||
|
||||
lib.marktree_itr_get(tree, 10, 10, iter)
|
||||
lib.marktree_del_itr(tree, iter, false)
|
||||
eq(11, iter[0].node.key[iter[0].i].pos.col)
|
||||
eq(11, iter[0].x.key[iter[0].i].pos.col)
|
||||
|
||||
lib.marktree_itr_get(tree, 11, 11, iter)
|
||||
lib.marktree_del_itr(tree, iter, false)
|
||||
eq(12, iter[0].node.key[iter[0].i].pos.col)
|
||||
end)
|
||||
eq(12, iter[0].x.key[iter[0].i].pos.col)
|
||||
end)
|
||||
|
||||
itp("'intersect_mov' function works correctly", function()
|
||||
local function mov(x, y, w)
|
||||
local xa = ffi.new("uint64_t[?]", #x)
|
||||
for i, xi in ipairs(x) do xa[i-1] = xi end
|
||||
local ya = ffi.new("uint64_t[?]", #y)
|
||||
for i, yi in ipairs(y) do ya[i-1] = yi end
|
||||
local wa = ffi.new("uint64_t[?]", #w)
|
||||
for i, wi in ipairs(w) do wa[i-1] = wi end
|
||||
|
||||
local dummy_size = #x + #y + #w
|
||||
local wouta = ffi.new("uint64_t[?]", dummy_size)
|
||||
local douta = ffi.new("uint64_t[?]", dummy_size)
|
||||
local wsize = ffi.new("size_t[1]")
|
||||
wsize[0] = dummy_size
|
||||
local dsize = ffi.new("size_t[1]")
|
||||
dsize[0] = dummy_size
|
||||
|
||||
local status = lib.intersect_mov_test(xa, #x, ya, #y, wa, #w, wouta, wsize, douta, dsize)
|
||||
if status == 0 then error'wowza' end
|
||||
|
||||
local wout, dout = {}, {}
|
||||
for i = 0,tonumber(wsize[0])-1 do table.insert(wout, tonumber(wouta[i])) end
|
||||
for i = 0,tonumber(dsize[0])-1 do table.insert(dout, tonumber(douta[i])) end
|
||||
return {wout, dout}
|
||||
end
|
||||
|
||||
eq({{}, {}}, mov({}, {2, 3}, {2, 3}))
|
||||
eq({{2, 3}, {}}, mov({}, {}, {2, 3}))
|
||||
eq({{2, 3}, {}}, mov({2, 3}, {}, {}))
|
||||
eq({{}, {2,3}}, mov({}, {2,3}, {}))
|
||||
|
||||
eq({{1, 5}, {}}, mov({1,2,5}, {2, 3}, {3}))
|
||||
eq({{1, 2}, {}}, mov({1,2,5}, {5, 10}, {10}))
|
||||
eq({{1, 2}, {5}}, mov({1,2}, {5, 10}, {10}))
|
||||
eq({{1,3,5,7,9}, {2,4,6,8,10}}, mov({1,3,5,7,9}, {2,4,6,8,10}, {}))
|
||||
eq({{1,3,5,7,9}, {2,6,10}}, mov({1,3,5,7,9}, {2,4,6,8,10}, {4, 8}))
|
||||
eq({{1,4,7}, {2,5,8}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {}))
|
||||
eq({{1,4,7}, {}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {2,5,8}))
|
||||
eq({{0,1,4,7,10}, {}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {0,2,5,8,10}))
|
||||
end)
|
||||
|
||||
|
||||
local function check_intersections(tree)
|
||||
lib.marktree_check(tree)
|
||||
-- to debug stuff disable this branch
|
||||
if true == true then
|
||||
ok(lib.marktree_check_intersections(tree))
|
||||
return
|
||||
end
|
||||
|
||||
local str1 = lib.mt_inspect(tree, true, true)
|
||||
local dot1 = ffi.string(str1.data, str1.size)
|
||||
|
||||
local val = lib.marktree_check_intersections(tree)
|
||||
if not val then
|
||||
local str2 = lib.mt_inspect(tree, true, true)
|
||||
local dot2 = ffi.string(str2.data, str2.size)
|
||||
print("actual:\n\n".."Xafile.dot".."\n\nexpected:\n\n".."Xefile.dot".."\n")
|
||||
print("nivå", tree[0].root.level);
|
||||
io.stdout:flush()
|
||||
local afil = io.open("Xafile.dot", "wb")
|
||||
afil:write(dot1)
|
||||
afil:close()
|
||||
local efil = io.open("Xefile.dot", "wb")
|
||||
efil:write(dot2)
|
||||
efil:close()
|
||||
ok(false)
|
||||
else
|
||||
ffi.C.xfree(str1.data)
|
||||
end
|
||||
end
|
||||
|
||||
itp('works with intersections', function()
|
||||
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
|
||||
|
||||
local ids = {}
|
||||
|
||||
for i = 1,80 do
|
||||
table.insert(ids, put(tree, 1, i, false, 2, 100-i, false))
|
||||
check_intersections(tree)
|
||||
end
|
||||
for i = 1,80 do
|
||||
lib.marktree_del_pair_test(tree, ns, ids[i])
|
||||
check_intersections(tree)
|
||||
end
|
||||
ids = {}
|
||||
|
||||
for i = 1,80 do
|
||||
table.insert(ids, put(tree, 1, i, false, 2, 100-i, false))
|
||||
check_intersections(tree)
|
||||
end
|
||||
|
||||
for i = 1,10 do
|
||||
for j = 1,8 do
|
||||
local ival = (j-1)*10+i
|
||||
lib.marktree_del_pair_test(tree, ns, ids[ival])
|
||||
check_intersections(tree)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
itp('works with intersections with a big tree', function()
|
||||
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
|
||||
|
||||
local ids = {}
|
||||
|
||||
for i = 1,1000 do
|
||||
table.insert(ids, put(tree, 1, i, false, 2, 1000-i, false))
|
||||
if i % 10 == 1 then
|
||||
check_intersections(tree)
|
||||
end
|
||||
end
|
||||
|
||||
check_intersections(tree)
|
||||
eq(2000, tree[0].n_keys)
|
||||
ok(tree[0].root.level >= 2)
|
||||
|
||||
local iter = ffi.new("MarkTreeIter[1]")
|
||||
|
||||
local k = 0
|
||||
for i = 1,20 do
|
||||
for j = 1,50 do
|
||||
k = k + 1
|
||||
local ival = (j-1)*20+i
|
||||
if false == true then -- if there actually is a failure, this branch will fail out at the actual spot of the error
|
||||
lib.marktree_lookup_ns(tree, ns, ids[ival], false, iter)
|
||||
lib.marktree_del_itr(tree, iter, false)
|
||||
check_intersections(tree)
|
||||
|
||||
lib.marktree_lookup_ns(tree, ns, ids[ival], true, iter)
|
||||
lib.marktree_del_itr(tree, iter, false)
|
||||
check_intersections(tree)
|
||||
else
|
||||
lib.marktree_del_pair_test(tree, ns, ids[ival])
|
||||
if k % 5 == 1 then
|
||||
check_intersections(tree)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
eq(0, tree[0].n_keys)
|
||||
end)
|
||||
|
||||
itp('works with intersections with a even bigger tree', function()
|
||||
local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
|
||||
|
||||
local ids = {}
|
||||
|
||||
-- too much overhead on ASAN
|
||||
local size_factor = helpers.is_asan() and 3 or 10
|
||||
|
||||
local at_row = {}
|
||||
for i = 1, 10 do
|
||||
at_row[i] = {}
|
||||
end
|
||||
|
||||
local size = 1000*size_factor
|
||||
local k = 1
|
||||
while k <= size do
|
||||
for row1 = 1,9 do
|
||||
for row2 = row1,10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2
|
||||
if k > size then
|
||||
break
|
||||
end
|
||||
local id = put(tree, row1, k, false, row2, size-k, false)
|
||||
table.insert(ids, id)
|
||||
for i = row1+1, row2 do
|
||||
table.insert(at_row[i], id)
|
||||
end
|
||||
--if tree[0].root.level == 4 then error("kk"..k) end
|
||||
if k % 100*size_factor == 1 or (k < 2000 and k%100 == 1) then
|
||||
check_intersections(tree)
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
eq(2*size, tree[0].n_keys)
|
||||
ok(tree[0].root.level >= 3)
|
||||
check_intersections(tree)
|
||||
|
||||
local iter = ffi.new("MarkTreeIter[1]")
|
||||
local pair = ffi.new("MTPair[1]")
|
||||
for i = 1,10 do
|
||||
-- use array as set and not {[id]=true} map, to detect duplicates
|
||||
local set = {}
|
||||
eq(true, ffi.C.marktree_itr_get_overlap(tree, i, 0, iter))
|
||||
while ffi.C.marktree_itr_step_overlap(tree, iter, pair) do
|
||||
local id = tonumber(pair[0].start.id)
|
||||
table.insert(set, id)
|
||||
end
|
||||
table.sort(set)
|
||||
eq(at_row[i], set)
|
||||
end
|
||||
|
||||
k = 0
|
||||
for i = 1,100 do
|
||||
for j = 1,(10*size_factor) do
|
||||
k = k + 1
|
||||
local ival = (j-1)*100+i
|
||||
lib.marktree_del_pair_test(tree, ns, ids[ival])
|
||||
-- just a few stickprov, if there is trouble we need to check
|
||||
-- everyone using the code in the "big tree" case above
|
||||
if k % 100*size_factor == 0 or (k > 3000 and k % 200 == 0) then
|
||||
check_intersections(tree)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
eq(0, tree[0].n_keys)
|
||||
end)
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user