refactor(decorations): break up Decoration struct into smaller pieces

Remove the monolithic Decoration struct. Before this change, each extmark
could either represent just a hl_id + priority value as a inline
decoration, or it would take a pointer to this monolitic 112 byte struct
which has to be allocated.

This change separates the decorations into two pieces: DecorSignHighlight
for signs, highlights and simple set-flag decorations (like spell,
ui-watched), and DecorVirtText for virtual text and lines.

The main separation here is whether they are expected to allocate more
memory. Currently this is not really true as sign text has to be an
allocated string, but the plan is to get rid of this eventually (it can
just be an array of two schar_T:s). Further refactors are expected to
improve the representation of each decoration kind individually. The
goal of this particular PR is to get things started by cutting the
Gordian knot which was the monolithic struct Decoration.

Now, each extmark can either contain chained indicies/pointers to
these kinds of objects, or it can fit a subset of DecorSignHighlight
inline.

The point of this change is not only to make decorations smaller in
memory. In fact, the main motivation is to later allow them to grow
_larger_, but on a dynamic, on demand fashion. As a simple example, it
would be possible to augment highlights to take a list of multiple
`hl_group`:s, which then would trivially map to a chain of multiple
DecorSignHighlight entries.

One small feature improvement included with this refactor itself, is
that the restriction that extmarks cannot be removed inside a decoration
provider has been lifted. These are instead safely lifetime extended
on a "to free" list until the current iteration of screen drawing is done.

NB: flags is a mess. but DecorLevel is useless, this slightly less so
This commit is contained in:
bfredl
2023-03-08 15:18:02 +01:00
parent 8c6b0a5f21
commit 0b38fe4dbb
18 changed files with 1089 additions and 616 deletions

View File

@@ -7,6 +7,7 @@
#include "klib/kvec.h"
#include "nvim/assert.h"
#include "nvim/decoration_defs.h"
#include "nvim/garray.h"
#include "nvim/map.h"
#include "nvim/pos.h"
@@ -47,6 +48,8 @@ typedef struct {
} MarkTreeIter;
#define marktree_itr_valid(itr) ((itr)->x != NULL)
// accces raw key: flags in MT_FLAG_EXTERNAL_MASK and decor_data are safe to modify.
#define mt_itr_rawkey(itr) ((itr)->x->key[(itr)->i])
// Internal storage
//
@@ -56,10 +59,8 @@ typedef struct {
MTPos pos;
uint32_t ns;
uint32_t id;
int32_t hl_id;
uint16_t flags;
uint16_t priority;
Decoration *decor_full;
DecorInlineData decor_data; // "ext" tag in flags
} MTKey;
typedef struct {
@@ -68,28 +69,40 @@ typedef struct {
bool end_right_gravity;
} MTPair;
#define MT_INVALID_KEY (MTKey) { { -1, -1 }, 0, 0, 0, 0, 0, NULL }
#define MT_INVALID_KEY (MTKey) { { -1, -1 }, 0, 0, 0, { .hl = DECOR_HIGHLIGHT_INLINE_INIT } }
#define MT_FLAG_REAL (((uint16_t)1) << 0)
#define MT_FLAG_END (((uint16_t)1) << 1)
#define MT_FLAG_PAIRED (((uint16_t)1) << 2)
// 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 MT_FLAG_NO_UNDO (((uint16_t)1) << 5)
#define MT_FLAG_INVALIDATE (((uint16_t)1) << 6)
#define MT_FLAG_INVALID (((uint16_t)1) << 7)
#define MT_FLAG_NO_UNDO (((uint16_t)1) << 4)
#define MT_FLAG_INVALIDATE (((uint16_t)1) << 5)
#define MT_FLAG_INVALID (((uint16_t)1) << 6)
// discriminant for union
#define MT_FLAG_DECOR_EXT (((uint16_t)1) << 7)
#define DECOR_LEVELS 4
#define MT_FLAG_DECOR_OFFSET 8
#define MT_FLAG_DECOR_MASK (((uint16_t)(DECOR_LEVELS - 1)) << MT_FLAG_DECOR_OFFSET)
// TODO(bfredl): flags for decorations. These cover the cases where we quickly needs
// to skip over irrelevant marks internally. When we refactor this more, also make all info
// for ExtmarkType included here
#define MT_FLAG_DECOR_HL (((uint16_t)1) << 8)
#define MT_FLAG_DECOR_SIGNTEXT (((uint16_t)1) << 9)
// TODO(bfredl): for now this means specifically number_hl, line_hl, cursorline_hl
// needs to clean up the name.
#define MT_FLAG_DECOR_SIGNHL (((uint16_t)1) << 10)
#define MT_FLAG_DECOR_VIRT_LINES (((uint16_t)1) << 11)
#define MT_FLAG_DECOR_VIRT_TEXT_INLINE (((uint16_t)1) << 12)
// These _must_ be last to preserve ordering of marks
#define MT_FLAG_RIGHT_GRAVITY (((uint16_t)1) << 14)
#define MT_FLAG_LAST (((uint16_t)1) << 15)
#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_RIGHT_GRAVITY | MT_FLAG_HL_EOL \
| MT_FLAG_NO_UNDO | MT_FLAG_INVALIDATE | MT_FLAG_INVALID)
#define MT_FLAG_DECOR_MASK (MT_FLAG_DECOR_EXT| MT_FLAG_DECOR_HL | MT_FLAG_DECOR_SIGNTEXT \
| MT_FLAG_DECOR_SIGNHL | MT_FLAG_DECOR_VIRT_LINES \
| MT_FLAG_DECOR_VIRT_TEXT_INLINE)
#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_NO_UNDO \
| MT_FLAG_INVALIDATE | MT_FLAG_INVALID)
// this is defined so that start and end of the same range have adjacent ids
#define MARKTREE_END_FLAG ((uint64_t)1)
@@ -143,20 +156,22 @@ static inline bool mt_invalid(MTKey key)
return key.flags & MT_FLAG_INVALID;
}
static inline uint8_t marktree_decor_level(MTKey key)
static inline bool mt_decor_any(MTKey key)
{
return (uint8_t)((key.flags&MT_FLAG_DECOR_MASK) >> MT_FLAG_DECOR_OFFSET);
return key.flags & MT_FLAG_DECOR_MASK;
}
static inline uint16_t mt_flags(bool right_gravity, bool hl_eol, bool no_undo, bool invalidate,
uint8_t decor_level)
static inline bool mt_decor_sign(MTKey key)
{
return key.flags & (MT_FLAG_DECOR_SIGNTEXT | MT_FLAG_DECOR_SIGNHL);
}
static inline uint16_t mt_flags(bool right_gravity, bool no_undo, bool invalidate, bool decor_ext)
{
assert(decor_level < DECOR_LEVELS);
return (uint16_t)((right_gravity ? MT_FLAG_RIGHT_GRAVITY : 0)
| (hl_eol ? MT_FLAG_HL_EOL : 0)
| (no_undo ? MT_FLAG_NO_UNDO : 0)
| (invalidate ? MT_FLAG_INVALIDATE : 0)
| (decor_level << MT_FLAG_DECOR_OFFSET));
| (decor_ext ? MT_FLAG_DECOR_EXT : 0));
}
static inline MTPair mtpair_from(MTKey start, MTKey end)
@@ -164,6 +179,11 @@ static inline MTPair mtpair_from(MTKey start, MTKey end)
return (MTPair){ .start = start, .end_pos = end.pos, .end_right_gravity = mt_right(end) };
}
static inline DecorInline mt_decor(MTKey key)
{
return (DecorInline){ .ext = key.flags & MT_FLAG_DECOR_EXT, .data = key.decor_data };
}
typedef kvec_withinit_t(uint64_t, 4) Intersection;
struct mtnode_s {
@@ -186,8 +206,6 @@ static inline uint64_t mt_dbg_id(uint64_t id)
typedef struct {
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;
PMap(uint64_t) id2node[1];
} MarkTree;