mirror of
https://github.com/neovim/neovim.git
synced 2025-10-06 18:06:30 +00: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:
@@ -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.
|
||||
|
Reference in New Issue
Block a user