mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07:00
feat(extmark): support proper multiline ranges
The removes the previous restriction that nvim_buf_set_extmark() could not be used to highlight arbitrary multi-line regions The problem can be summarized as follows: let's assume an extmark with a hl_group is placed covering the region (5,0) to (50,0) Now, consider what happens if nvim needs to redraw a window covering the lines 20-30. It needs to be able to ask the marktree what extmarks cover this region, even if they don't begin or end here. Therefore the marktree needs to be augmented with the information covers a point, not just what marks begin or end there. To do this, we augment each node with a field "intersect" which is a set the ids of the marks which overlap this node, but only if it is not part of the set of any parent. This ensures the number of nodes that need to be explicitly marked grows only logarithmically with the total number of explicitly nodes (and thus the number of of overlapping marks). Thus we can quickly iterate all marks which overlaps any query position by looking up what leaf node contains that position. Then we only need to consider all "start" marks within that leaf node, and the "intersect" set of that node and all its parents. Now, and the major source of complexity is that the tree restructuring operations (to ensure that each node has T-1 <= size <= 2*T-1) also need to update these sets. If a full inner node is split in two, one of the new parents might start to completely overlap some ranges and its ids will need to be moved from its children's sets to its own set. Similarly, if two undersized nodes gets joined into one, it might no longer completely overlap some ranges, and now the children which do needs to have the have the ids in its set instead. And then there are the pivots! Yes the pivot operations when a child gets moved from one parent to another.
This commit is contained in:
parent
6b5f44817e
commit
b04286a187
@ -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