aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/extmark.c
Commit message (Collapse)AuthorAge
* refactor(multicursor)!: drop nvim_mcursor #42156Justin M. Keyes2026-09-30
| | | | | | | | | | | | | | Problem: `nvim_mcursor` is not fully baked, and not strongly motivated, currently. The main programmatic interface for cursors is the "nvim.multicursor" extmarks namespace. Solution: Drop `nvim_mcursor`. Add an internal hook so that multicursor can react when extmarks are added to the "nvim.multicursor" namespace. TODO: If extmarks/namespaces are clumsy to work with, we should _fix that_ rather than adding wrappers.
* refactor(multicursor): view/extmark/region logic #42030Justin M. Keyes2026-09-21
| | | | | | | | | | Problem: Redundant logic. Solution: Deduplicate. Also: `winrestview()` no longer forces a status-line redraw and resending `win_pos` (`win_new_width()` on unchanged width).
* feat(multicursor): MC HAMMER #41587Justin M. Keyes2026-09-01
| | | | | | | | | | | | | | | | | | | | | Other (squashed) commits: fix(tui): emit ui_send output atomically with the frame Problem: tui_ui_send() writes directly to the TTY, bypassing the output buffer. Sequences sent via nvim_ui_send() (e.g. kitty multiple-cursors, or visual-dot-repeat) always arrive in a separate TTY write from the frame they were computed for. This manifests as "tearing", or e.g. in the case of multicursor the terminal renders text with stale cursor overlays. Solution: - tui_ui_send(): while a frame is being assembled (pending invalid regions or buffered output), buffer instead of writing directly. - Out-of-frame sends (tty queries, clear-on-disable) still write immediately. - mcursor.lua: emit the terminal-cursor update at the end of the redraw cycle (`on_end`, when screen positions are final) instead of vim.schedule().
* fix(extmarks): redo of a mark created during an edit #41324Justin M. Keyes2026-08-15
| | | | | | | | | | | | | | | | | Problem: A mark created by `nvim_buf_set_extmark()` while an undo block is open never comes back on redo. Analysis: Undo deletes the text it covers and collapses the range; redo replays the splices, which re-insert the text but cannot re-expand the mark. `extmark_set()` records a position only for a mark it moves, not for one it creates. Solution: Record the created position for redo; undo leaves the mark to the splice replay, since it did not exist before the edit. Each side of a paired mark gets its own entry. Redo also revives a mark that undo invalidated, else its position returns but its highlight does not.
* fix(extmarks): undo-redo of a mark explicitly moved during an edit #41252Justin M. Keyes2026-08-10
| | | | | | | | | | | | | | Problem: A mark moved by nvim_buf_set_extmark() during an edit is misplaced by undo and redo. Only splices ("edits") are recorded, and replaying them reproduces the shifts they caused, never the explicit set: the mark ends up wherever the text pushed it. Solution: When an open undo block moves an existing mark, record both positions. Undo restores the pre-set position, redo re-applies the set. Partially reverts 18334a4a0cd9 ; ExtmarkSavePos.row/col were unused because nothing recorded an explicit move, but now `extmark_set()` does.
* fix(extmark): adjust invalidate range by one for deleted lines #37897luukvbaal2026-02-16
| | | | | | | | | | | | | | | | | | | | | | | Problem: A multi-line extmark that ends exactly at a deleted range end is not invalidated. Revalidated sign mark is added with incorrect range. Solution: Remove questionable invalidation range condition which was originally added to avoid deleting a mark that ends below a deleted line. Since splicing for a deleted line, and a replaced range that explicitly ends at column 0 beyond a deleted line is identical, we can't try to distinguish these two cases. I.e. :1delete 1 and nvim_buf_set_text(0, 0, 0, 1, 0, {}) yield the same splice operation. This means that a multi-line sign_text mark should now span at least one column beyond its end_row, as seen in the adjusted test. This is still somewhat unexpected/inconvenient to me which is what prompted me to try to avoid it with the original condition. Add revalidated sign mark back to decor with correct range; third sign mark added to test exposed a crash.
* fix(api): buffer overflow in nvim_buf_get_extmarks overlap #37184Francisco Giordano2026-01-02
| | | | | With `overlap=true`, more extmarks than the requested limit may be collected in `extmark_get`. This then leads to an out of bounds write of `rv` in `nvim_buf_get_extmarks`.
* docs: misc #36580Justin M. Keyes2025-11-16
| | | | | | | | Co-authored-by: nguyenkd27 <nguyenkd27@gmail.com> Co-authored-by: dundargoc <gocdundar@gmail.com> Co-authored-by: Yochem van Rosmalen <git@yochem.nl> Co-authored-by: Tuure Piitulainen <tuure.piitulainen@gmail.com> Co-authored-by: Maria Solano <majosolano99@gmail.com> Co-authored-by: tao <2471314@gmail.com>
* refactor(build): remove INCLUDE_GENERATED_DECLARATIONS guardsbfredl2025-08-14
| | | | | | | | | 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.
* fix(api): wrong return value with reverse range + overlap #32956luukvbaal2025-04-21
| | | | | | | | | | | | | | 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.
* fix(column): don't count signs on lines beyond eob #33410luukvbaal2025-04-11
| | | | | | | | | 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.
* fix(marks): ensure decor is removed with proper range (#32973)luukvbaal2025-03-19
| | | | | | | 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.
* fix(marks): issues with invalid marks and marks beyond eob (#32862)luukvbaal2025-03-16
| | | | | | | 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.
* fix(marks): revise metadata for start mark of revalidated pair #32017luukvbaal2025-01-15
| | | | Problem: Metadata may be revised for end mark of a revalidated pair. Solution: Revise metadata for start mark of a revalidated pair.
* fix(decor): set invalid flag for end of invalidated paired marksLuuk van Baal2025-01-10
|
* fix(decoration): fix crash when on_lines decor provider modifies marktreebfredl2025-01-09
| | | | | | | | | 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
* fix(marks): skip right_gravity marks when deleting textLuuk van Baal2024-12-06
| | | | | | | | | | 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.
* fix(decor): exclude invalid marks from meta totalLuuk van Baal2024-09-04
| | | | | | 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.
* fix(marks): revalidate marks whose position did not changeLuuk van Baal2024-07-20
| | | | | | | | 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.
* feat: get/set namespace properties #28728altermo2024-06-07
| | | | ref https://github.com/neovim/neovim/pull/28432 ref https://github.com/neovim/neovim/issues/28469
* fix(extmarks): issues with revalidating marks #28961luukvbaal2024-05-26
| | | | | | 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.
* perf(extmarks): better track whether namespace has extmarks (#28615)zeertzjq2024-05-03
| | | | | | 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.
* feat(extmark): window scoped extmarkaltermo2024-02-21
| | | | Co-authored-by: zeertzjq <zeertzjq@outlook.com>
* fix(decorations): crash with revised mark with changed decoration flagsbfredl2024-02-17
| | | | fixes #27211
* fix(extmarks): redraw pre-undo position (#27437)zeertzjq2024-02-12
| | | | | | | Problem: Virtual text not redrawn properly after undo moves its extmark. Solution: Redraw the moved extmark's pre-undo position.
* perf(extmarks): avoid unnecessary invalidations for virt_text (#27435)zeertzjq2024-02-12
| | | | Invalidation of most w_valid flags isn't needed when adding or removing virtual text below cursor.
* fix(column): clear "b_signcols" before moving saved marksLuuk van Baal2024-01-27
| | | | | | 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.
* fix(extmarks): do not remove decor from invalid old marksLuuk van Baal2024-01-26
|
* refactor: IWYU (#27186)zeertzjq2024-01-25
|
* fix(column): clear "b_signcols" when marktree is clearedLuuk van Baal2024-01-22
|
* perf(extmarks): add metadata for efficient filtering of special decorationsbfredl2024-01-22
| | | | | | | | | | | | | | | | | | 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.
* refactor(extmarks): remove unused new pos from ExtmarkSavePosLuuk van Baal2024-01-18
|
* fix(extmarks): do not remove invalid marks from decor upon deletionLuuk van Baal2024-01-17
|
* fix(column): keep track of number of lines with number of signsLuuk van Baal2024-01-15
| | | | | | | | | | | | | | | | | | | 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.
* refactor(marktree): unpaired marktree_get_alt() returns itselfLuuk van Baal2024-01-13
| | | | Avoids checking for invalid mark at callsite.
* refactor(IWYU): fix headersdundargoc2024-01-11
| | | | | | 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.
* refactor: eliminate cyclic includesdundargoc2023-12-20
|
* docs: add style rule regarding initializationdundargoc2023-12-18
| | | | | Specifically, specify that each initialization should be done on a separate line.
* fix(extmark): only invalidate unpaired marks on deleted rowsLuuk van Baal2023-12-14
| | | | | | 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.
* perf(column): keep track of number of lines that hold up the 'signcolumn'Luuk van Baal2023-12-07
| | | | | | | | | | | | | | | | | | | | 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.
* fix(extmarks): restore old position before revalidatingLuuk van Baal2023-12-02
|
* fix(extmark): restore extmarks when completing original textLuuk van Baal2023-11-29
|
* refactor: move some constants out of vim_defs.h (#26298)zeertzjq2023-11-29
|
* refactor: fix headers with IWYUdundargoc2023-11-28
|
* fix(column): redraw and update signcols for paired extmarkLuuk van Baal2023-11-28
| | | | | | 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.
* build(IWYU): fix includes for undo_defs.hdundargoc2023-11-27
|
* refactor: move garray_T to garray_defs.h (#26227)zeertzjq2023-11-26
|
* build: rework IWYU mapping filesdundargoc2023-11-25
| | | | | Create mapping to most of the C spec and some POSIX specific functions. This is more robust than relying files shipped with IWYU.
* refactor(decorations): break up Decoration struct into smaller piecesbfredl2023-11-22
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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
* refactor(extmark): redundant ExtmarkInfo delenda est, use MTPair insteadbfredl2023-11-18
|