diff options
-rw-r--r-- | runtime/doc/api.txt | 7 | ||||
-rw-r--r-- | runtime/doc/diagnostic.txt | 4 | ||||
-rw-r--r-- | runtime/doc/news.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/api.lua | 9 | ||||
-rw-r--r-- | runtime/lua/vim/diagnostic.lua | 2 | ||||
-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 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 63 |
10 files changed, 142 insertions, 9 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c5ade72f93..1a5df18f6c 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2609,6 +2609,13 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) 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. diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index d4939d8cc7..6b1456d5a6 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -626,8 +626,8 @@ Lua module: vim.diagnostic *diagnostic-api* • {hl_mode}? (`'replace'|'combine'|'blend'`) See |nvim_buf_set_extmark()|. • {virt_text}? (`[string,any][]`) See |nvim_buf_set_extmark()|. - • {virt_text_pos}? (`'eol'|'overlay'|'right_align'|'inline'`) See - |nvim_buf_set_extmark()|. + • {virt_text_pos}? (`'eol'|'eol_right_align'|'inline'|'overlay'|'right_align'`) + See |nvim_buf_set_extmark()|. • {virt_text_win_col}? (`integer`) See |nvim_buf_set_extmark()|. • {virt_text_hide}? (`boolean`) See |nvim_buf_set_extmark()|. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 099fc17c5d..1d53f168ff 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -195,6 +195,8 @@ API • |nvim_open_win()| `relative` field can be set to "laststatus" and "tabline". • |nvim_buf_set_extmark()| `hl_group` field can be an array of layered groups • |vim.hl.range()| now has a optional `timeout` field which allows for a timed highlight +• |nvim_buf_set_extmark()| virt_text_pos accepts `eol_right_align` to + allow for right aligned text that truncates before covering up buffer text. DEFAULTS diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 6d9a17ea2b..50fb7e4f9d 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -589,6 +589,15 @@ function vim.api.nvim_buf_line_count(buffer) end --- (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. diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index ead75f7d51..04118999cf 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -220,7 +220,7 @@ end --- @field virt_text? [string,any][] --- --- See |nvim_buf_set_extmark()|. ---- @field virt_text_pos? 'eol'|'overlay'|'right_align'|'inline' +--- @field virt_text_pos? 'eol'|'eol_right_align'|'inline'|'overlay'|'right_align' --- --- See |nvim_buf_set_extmark()|. --- @field virt_text_win_col? integer 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); diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index c2030b9527..7969dd5d3b 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -509,6 +509,69 @@ describe('decorations providers', function() ]]} end) + it('can have virtual text of the style: eol_right_align', function() + insert(mulholland) + setup_provider [[ + local hl = api.nvim_get_hl_id_by_name "ErrorMsg" + local test_ns = api.nvim_create_namespace "mulholland" + function on_do(event, ...) + if event == "line" then + local win, buf, line = ... + api.nvim_buf_set_extmark(buf, test_ns, line, 0, { + virt_text = {{'+'}, {'1234567890', 'ErrorMsg'}}; + virt_text_pos='eol_right_align'; + ephemeral = true; + }) + end + end + ]] + + screen:expect{grid=[[ + // just to see if there was an accident | + // on Mulholland Drive +{2:1234567890}| + try_start(); +{2:1234567890}| + bufref_T save_buf; +{2:1234567890}| + switch_buffer(&save_buf, buf); +{2:12345678}| + posp = getmark(mark, false); +{2:1234567890}| + restore_buffer(&save_buf);^ +{2:1234567890}| + | + ]]} + end) + + it('multiple eol_right_align', function() + insert(mulholland) + setup_provider [[ + local hl = api.nvim_get_hl_id_by_name "ErrorMsg" + local test_ns = api.nvim_create_namespace "mulholland" + function on_do(event, ...) + if event == "line" then + local win, buf, line = ... + api.nvim_buf_set_extmark(buf, test_ns, line, 0, { + virt_text = {{'11111'}}; + virt_text_pos='eol_right_align'; + ephemeral = true; + }) + api.nvim_buf_set_extmark(0, test_ns, line, 0, { + virt_text = {{'22222'}}; + virt_text_pos='eol_right_align'; + ephemeral = true; + }) + end + end + ]] + + screen:expect{grid=[[ + // just to see if there was an accident | + // on Mulholland Drive 11111 22222| + try_start(); 11111 22222| + bufref_T save_buf; 11111 22222| + switch_buffer(&save_buf, buf); 11111 222| + posp = getmark(mark, false); 11111 22222| + restore_buffer(&save_buf);^ 11111 22222| + | + ]]} + end) + it('virtual text works with wrapped lines', function() insert(mulholland) feed('ggJj3JjJ') |