diff options
author | georgev93 <39860568+georgev93@users.noreply.github.com> | 2025-01-24 22:57:45 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-01-24 19:57:45 -0800 |
commit | 931ee5591fa764a769946318e05062098baf7c21 (patch) | |
tree | 82dd7f486cc47d1e7a2d44d204ab12f967a5e0da /src | |
parent | c6d2cbf8f51abfa0c9d244ef384a15b0b69e16c6 (diff) | |
download | rneovim-931ee5591fa764a769946318e05062098baf7c21.tar.gz rneovim-931ee5591fa764a769946318e05062098baf7c21.tar.bz2 rneovim-931ee5591fa764a769946318e05062098baf7c21.zip |
feat(extmarks): virtual text can be right-aligned, truncated #31921
Problem: Right aligned virtual text can cover up buffer text if virtual
text is too long
Solution: An additional option for `virt_text_pos` called
`eol_right_align` has been added to truncate virtual text if it would
have otherwise covered up buffer text. This ensures the virtual text
extends no further left than EOL.
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/extmark.c | 11 | ||||
-rw-r--r-- | src/nvim/decoration.h | 4 | ||||
-rw-r--r-- | src/nvim/decoration_defs.h | 5 | ||||
-rw-r--r-- | src/nvim/drawline.c | 44 |
4 files changed, 58 insertions, 6 deletions
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index e66140da5a..778e857057 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -400,6 +400,15 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// (highest priority last). /// - virt_text_pos : position of virtual text. Possible values: /// - "eol": right after eol character (default). +/// - "eol_right_align": display right aligned in the window +/// unless the virtual text is longer than +/// the space available. If the virtual +/// text is too long, it is truncated to +/// fit in the window after the EOL +/// character. If the line is wrapped, the +/// virtual text is shown after the end of +/// the line rather than the previous +/// screen line. /// - "overlay": display over the specified column, without /// shifting the underlying text. /// - "right_align": display right aligned in the window. @@ -620,6 +629,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer virt_text.pos = kVPosOverlay; } else if (strequal("right_align", str.data)) { virt_text.pos = kVPosRightAlign; + } else if (strequal("eol_right_align", str.data)) { + virt_text.pos = kVPosEndOfLineRightAlign; } else if (strequal("inline", str.data)) { virt_text.pos = kVPosInline; } else { diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index a2f4fefd45..bdbb1795cb 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -15,8 +15,8 @@ // actual Decor* data is in decoration_defs.h /// Keep in sync with VirtTextPos in decoration_defs.h -EXTERN const char *const virt_text_pos_str[] -INIT( = { "eol", "overlay", "win_col", "right_align", "inline" }); +EXTERN const char *const virt_text_pos_str[] INIT( = { "eol", "eol_right_align", "inline", + "overlay", "right_align", "win_col" }); /// Keep in sync with HlMode in decoration_defs.h EXTERN const char *const hl_mode_str[] INIT( = { "", "replace", "combine", "blend" }); diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h index 58ba93a7ba..36ad6df7a0 100644 --- a/src/nvim/decoration_defs.h +++ b/src/nvim/decoration_defs.h @@ -19,10 +19,11 @@ typedef kvec_t(VirtTextChunk) VirtText; /// Keep in sync with virt_text_pos_str[] in decoration.h typedef enum { kVPosEndOfLine, + kVPosEndOfLineRightAlign, + kVPosInline, kVPosOverlay, - kVPosWinCol, kVPosRightAlign, - kVPosInline, + kVPosWinCol, } VirtTextPos; typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 4a7bc9170a..37a42917b0 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -263,6 +263,9 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int int *const indices = state->ranges_i.items; DecorRangeSlot *const slots = state->slots.items; + /// Total width of all virtual text with "eol_right_align" alignment + int totalWidthOfEolRightAlignedVirtText = 0; + for (int i = 0; i < end; i++) { DecorRange *item = &slots[indices[i]].range; if (!(item->start_row == state->row && decor_virt_pos(item))) { @@ -277,7 +280,44 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int if (decor_virt_pos(item) && item->draw_col == -1) { bool updated = true; VirtTextPos pos = decor_virt_pos_kind(item); - if (pos == kVPosRightAlign) { + + if (do_eol && pos == kVPosEndOfLineRightAlign) { + int eolOffset = 0; + if (totalWidthOfEolRightAlignedVirtText == 0) { + // Look ahead to the remaining decor items + for (int j = i; j < end; j++) { + /// A future decor to be handled in this function's call + DecorRange *lookaheadItem = &slots[indices[j]].range; + + if (lookaheadItem->start_row != state->row + || !decor_virt_pos(lookaheadItem) + || lookaheadItem->draw_col != -1) { + continue; + } + + /// The Virtual Text of the decor item we're looking ahead to + DecorVirtText *lookaheadVt = NULL; + if (item->kind == kDecorKindVirtText) { + assert(item->data.vt); + lookaheadVt = item->data.vt; + } + + if (decor_virt_pos_kind(lookaheadItem) == kVPosEndOfLineRightAlign) { + // An extra space is added for single character spacing in EOL alignment + totalWidthOfEolRightAlignedVirtText += (lookaheadVt->width + 1); + } + } + + // Remove one space from the total width since there's no single space after the last entry + totalWidthOfEolRightAlignedVirtText--; + + if (totalWidthOfEolRightAlignedVirtText <= (right_pos - state->eol_col)) { + eolOffset = right_pos - totalWidthOfEolRightAlignedVirtText - state->eol_col; + } + } + + item->draw_col = state->eol_col + eolOffset; + } else if (pos == kVPosRightAlign) { right_pos -= vt->width; item->draw_col = right_pos; } else if (pos == kVPosEndOfLine && do_eol) { @@ -304,7 +344,7 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int int vcol = item->draw_col - col_off; int col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text, vt->hl_mode, max_col, vcol); - if (vt->pos == kVPosEndOfLine && do_eol) { + if (do_eol && ((vt->pos == kVPosEndOfLine) || (vt->pos == kVPosEndOfLineRightAlign))) { state->eol_col = col + 1; } *end_col = MAX(*end_col, col); |