These are not needed after #35129 but making uncrustify still play nice
with them was a bit tricky.
Unfortunately `uncrustify --update-config-with-doc` breaks strings
with backslashes. This issue has been reported upstream,
and in the meanwhile auto-update on every single run has been disabled.
Problem: When iterating in reverse with {start} > {end} in
`nvim_buf_get_extmarks()`, marks that overlap {start} and are
greater than {end} are included in the return value twice.
Marks that overlap {end} and do not overlap {start} are not
not included in the return value at all. Marks are not
actually returned in a meaningful "traversal order".
Solution: Rather than actually iterating in reverse, (also possible but
requires convoluted conditions and would require fetching
overlapping marks for both the {start} and {end} position,
while still ending up with non-traversal ordered marks),
iterate normally and reverse the return value.
Problem: Computed previous buffer line count may be beyond end of
buffer. This results in signs being removed from `b_signcols`
that were never included in it, tripping an assertion.
Solution: Store the previous line count as it was before appending or
deleting lines. Use it to clamp the edited region when
clearing signs before a splice, after which it is reset.
Problem: Paired mark whose end is in front of its start should not have
its decor removed (as fixed by 72f630f9), but may still need to
have its range redrawn.
Solution: Still call `buf_decor_remove()` but ensure it is not called with
an inverse range when `extmark_del()` is called on an end mark.
Problem: Marks that go beyond the end of the buffer, and paired marks
whose end is in front of its start mark are added to and
removed from the decor. This results in incorrect tracking
of the signcolumn.
Solution: Ensure such marks are not added to and removed from the decor.
If a "on_lines" callback changes the structure of the marktree, the
iterator (which is used for an entire window viewport) might now
point to invalid memory. Restore the iterator to the beginning of the
line in this case.
fixes#29484
Problem: Marks that are properly restored by the splice associated with
an undo edit, are unnecessarily pushed to the undo header. This
results in incorrect mark tracking in the "copy_only"
save/restore completion path.
Solution: Avoid pushing left gravity marks at the beginning of the range,
and right gravity marks at the end of the range to the undo
header.
Problem: Marktree meta count still includes invalidated marks, making
guards that check the meta total ineffective.
Solution: Revise marktree metadata when in/revalidating a mark.
Problem: Marks whose position did not change with the action that
invalidated them (right_gravity = false) are not revalidated
upon undo.
Solution: Remove early return when restoring a marks saved position so
that it is still revalidated. Add "move" guards instead.
Problem: Invalid marks appear to be revalidated multiple times, and
decor is added at the old position for unpaired marks.
Solution: Avoid revalidating already valid marks, and don't use old
position to add to decor for unpaired marks.
This avoids redraw when adding/removing an empty namespace for a window.
This also avoids marktree traversal when clearing a namespace that has
already been cleared, which is added as a benchmark.
Problem: Marks moved by undo may be lost to "b_signcols.count".
Solution: Count signs for each undo object separately instead of
once for the entire undo.
This expands on the global "don't pay for what you don't use" rules for
these special extmark decorations:
- inline virtual text, which needs to be processed in plines.c when we
calculate the size of text on screen
- virtual lines, which are needed when calculating "filler" lines
- signs, with text and/or highlights, both of which needs to be
processed for the entire line already at the beginning of a line.
This adds a count to each node of the marktree, for how many special
marks of each kind can be found in the subtree for this node. This makes
it possible to quickly skip over these extra checks, when working in
regions of the buffer not containing these kind of marks, instead of
before where this could just be skipped if the entire _buffer_
didn't contain such marks.
Problem: Some edge cases to the old (pre-#26406) and current "b_signcols"
structure result in an incorrectly sized "auto" 'signcolumn'.
Solution: * Implement a simpler 'signcolumn' validation strategy by immediately
counting the number of signs in a range upon sign insertion and
deletion. Decrease in performance here but there is a clear path
forward to decreasing this performance hit by moving signs to a
dedicated marktree, or by adding meta-data to the existing
marktree which may be queried more efficiently?
* Also replace "max_count" and keep track of the number of lines with
a certain number of signs. This makes it so that it is no longer
necessary to scan the entire buffer when the maximum number of signs
decreases. This likely makes the commit a net increase in performance.
* To ensure correctness we also have re-initialize the count for an
edited region that spans multiple lines. Such an edit may move the
signs within it. Thus we count and decrement before splicing the
marktree and count and increment after.
Remove `export` pramgas from defs headers as it causes IWYU to believe
that the definitions from the defs headers comes from main header, which
is not what we really want.
Problem: Unpaired marks are invalidated if its column is deleted,
which may just be a "placeholder" column, e.g. for signs.
Solution: Only remove unpaired marks if its entire row is deleted.
Problem: The entire marktree needs to be traversed each time a sign is
removed from the sentinel line.
Solution: Remove sentinel line and instead keep track of the number of
lines that hold up the 'signcolumn' in "max_count". Adjust this
number for added/removed signs, and set it to 0 when the
maximum number of signs on a line changes. Only when
"max_count" is decremented to 0 due to sign removal do we need
to check the entire buffer.
Also replace "invalid_top" and "invalid_bot" with a map of
invalid ranges, further reducing the number of lines to be
checked.
Also improve tree traversal when counting the number of signs.
Instead of looping over the to be checked range and counting
the overlap for each row, keep track of the overlap in an
array and add this to the count.
Problem: Signcolumn width does not increase when ranged sign does not
start at sentinel line.
Solution: Handle paired range of added sign when checking signcols.
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
Problem: The legacy signlist data structures and associated functions are
redundant since the introduction of extmark signs.
Solution: Store signs defined through the legacy commands in a hashmap, placed
signs in the extmark tree. Replace signlist associated functions.
Usage of the legacy sign commands should yield no change in behavior with the
exception of:
- "orphaned signs" are now always removed when the line it is placed on is
deleted. This used to depend on the value of 'signcolumn'.
- It is no longer possible to place multiple signs with the same identifier
in a single group on multiple lines. This will now move the sign instead.
Moreover, both signs placed through the legacy sign commands and through
|nvim_buf_set_extmark()|:
- Will show up in both |sign-place| and |nvim_buf_get_extmarks()|.
- Are displayed by increasing sign identifier, left to right.
Extmark signs used to be ordered decreasingly as opposed to legacy signs.
We already have an extensive suite of static analysis tools we use,
which causes a fair bit of redundancy as we get duplicate warnings. PVS
is also prone to give false warnings which creates a lot of work to
identify and disable.
Problem: No way to have extmarks automatically removed when the range it
is attached to is deleted.
Solution: Add new 'invalidate' property that will hide a mark when the
entirety of its range is deleted. When "undo_restore" is set
to false, delete the mark from the buffer instead.
It is a design goal of extmarks that they allow precise tracking
of changes across undo/redo, including restore the exact positions
after a do/undo or undo/redo cycle. However this behavior is not useful
for all usecases. Many plugins won't keep marks around for long after
text changes, but uses them more like a cache until some external source
(like LSP semantic highlights) has fully updated to changed text and
then will explicitly readjust/replace extmarks as needed.
Add a "undo_restore" flag which is true by default (matches existing
behavior) but can be set to false to opt-out of this behavior.
Delete dead u_extmark_set() code.
long is 32 bits on windows, while it is 64 bits on other architectures.
This makes the type suboptimal for a codebase meant to be
cross-platform. Replace it with more appropriate integer types.