diff options
author | Yatao Li <yatli@microsoft.com> | 2021-09-16 07:53:56 +0800 |
---|---|---|
committer | Yatao Li <yatli@microsoft.com> | 2022-05-03 22:26:02 +0800 |
commit | 29a6cda3ffe981b09d4c59d49d6c97a4ea13ca8b (patch) | |
tree | aef77156a4164f79b004e92bd685d23a40ba6bfb | |
parent | 8ea84eee570fd1ec560fe149e611d10034d9a223 (diff) | |
download | rneovim-29a6cda3ffe981b09d4c59d49d6c97a4ea13ca8b.tar.gz rneovim-29a6cda3ffe981b09d4c59d49d6c97a4ea13ca8b.tar.bz2 rneovim-29a6cda3ffe981b09d4c59d49d6c97a4ea13ca8b.zip |
feat(api/ui): win_extmarks
-rw-r--r-- | runtime/doc/api.txt | 5 | ||||
-rw-r--r-- | runtime/doc/ui.txt | 4 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 14 | ||||
-rw-r--r-- | src/nvim/api/keysets.lua | 1 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 1 | ||||
-rw-r--r-- | src/nvim/api/ui.h | 1 | ||||
-rw-r--r-- | src/nvim/api/ui_events.in.h | 4 | ||||
-rw-r--r-- | src/nvim/decoration.c | 36 | ||||
-rw-r--r-- | src/nvim/decoration.h | 5 | ||||
-rw-r--r-- | src/nvim/extmark.c | 3 | ||||
-rw-r--r-- | src/nvim/screen.c | 48 | ||||
-rw-r--r-- | test/functional/api/extmark_spec.lua | 206 | ||||
-rw-r--r-- | test/functional/ui/screen.lua | 32 |
13 files changed, 332 insertions, 28 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index ed3a838b6d..ab6c6a8699 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2706,6 +2706,11 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) "hl_group" is used as highlight for the cchar if provided, otherwise it defaults to |hl-Conceal|. + • ui_watched: boolean that indicates the mark + should be drawn by a UI. When set, the UI will + receive win_extmark events. Note: the mark is + positioned by virt_text attributes. Can be + used together with virt_text. Return: ~ Id of the created/updated extmark diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 8014199dc5..eb12fd38a0 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -627,6 +627,10 @@ tabs. `botline` is set to one more than the line count of the buffer, if there are filler lines past the end. +["win_extmark", grid, win, ns_id, mark_id, row, col] + Updates the position of an extmark which is currently visible in a + window. Only emitted if the mark has the `ui_watched` attribute. + ============================================================================== Popupmenu Events *ui-popupmenu* diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index e408d88854..fa6923e6d5 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -146,6 +146,10 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) STRING_OBJ(cstr_to_string(virt_text_pos_str[decor->virt_text_pos]))); } + if (decor->ui_watched) { + PUT(dict, "ui_watched", BOOLEAN_OBJ(true)); + } + if (kv_size(decor->virt_lines)) { Array all_chunks = ARRAY_DICT_INIT; bool virt_lines_leftcol = false; @@ -170,7 +174,7 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) PUT(dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol)); } - if (decor->hl_id || kv_size(decor->virt_text)) { + if (decor->hl_id || kv_size(decor->virt_text) || decor->ui_watched) { PUT(dict, "priority", INTEGER_OBJ(decor->priority)); } @@ -472,6 +476,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// When a character is supplied it is used as |:syn-cchar|. /// "hl_group" is used as highlight for the cchar if provided, /// otherwise it defaults to |hl-Conceal|. +/// - ui_watched: boolean that indicates the mark should be drawn +/// by a UI. When set, the UI will receive win_extmark events. +/// Note: the mark is positioned by virt_text attributes. Can be +/// used together with virt_text. /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -709,6 +717,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool ephemeral = false; OPTION_TO_BOOL(ephemeral, ephemeral, false); + OPTION_TO_BOOL(decor.ui_watched, ui_watched, false); + if (line < 0) { api_set_error(err, kErrorTypeValidation, "line value outside range"); goto error; @@ -762,7 +772,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // TODO(bfredl): synergize these two branches even more if (ephemeral && decor_state.buf == buf) { - decor_add_ephemeral((int)line, (int)col, line2, col2, &decor); + decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id); } else { if (ephemeral) { api_set_error(err, kErrorTypeException, "not yet implemented"); diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 8ad4dae928..5baffaf505 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -28,6 +28,7 @@ return { "line_hl_group"; "cursorline_hl_group"; "conceal"; + "ui_watched"; }; keymap = { "noremap"; diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index d1b86ed328..4f9592bd52 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -810,3 +810,4 @@ static void remote_ui_inspect(UI *ui, Dictionary *info) UIData *data = ui->data; PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id)); } + diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h index b3af14f8a8..bc70406acb 100644 --- a/src/nvim/api/ui.h +++ b/src/nvim/api/ui.h @@ -4,6 +4,7 @@ #include <stdint.h> #include "nvim/api/private/defs.h" +#include "nvim/map.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.h.generated.h" diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index db348359eb..63aaaae38a 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -123,6 +123,10 @@ void win_viewport(Integer grid, Window win, Integer topline, Integer line_count) FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY; +void win_extmark(Integer grid, Window win, Integer ns_id, Integer mark_id, + Integer row, Integer col) + FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY; + void popupmenu_show(Array items, Integer selected, Integer row, Integer col, Integer grid) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index c8f4b3e875..c6edf44c74 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -2,6 +2,7 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include "nvim/buffer.h" +#include "nvim/api/ui.h" #include "nvim/decoration.h" #include "nvim/extmark.h" #include "nvim/highlight.h" @@ -73,7 +74,8 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) } } - if (decor && kv_size(decor->virt_text)) { + if (decor && (kv_size(decor->virt_text) + || decor->ui_watched)) { redraw_buf_line_later(buf, row1 + 1); } @@ -195,9 +197,11 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) Decoration decor = get_decor(mark); // Exclude non-paired marks unless they contain virt_text or a sign + // or they are ui-watched if (!mt_paired(mark) && !kv_size(decor.virt_text) - && !decor_has_sign(&decor)) { + && !decor_has_sign(&decor) + && !decor.ui_watched) { goto next_mark; } @@ -206,21 +210,22 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) // Exclude start marks if the end mark position is above the top row // Exclude end marks if we have already added the start mark if ((mt_start(mark) && altpos.row < top_row - && !kv_size(decor.virt_text)) + && !kv_size(decor.virt_text) + && !decor.ui_watched) || (mt_end(mark) && altpos.row >= top_row)) { goto next_mark; } if (mt_end(mark)) { decor_add(state, altpos.row, altpos.col, mark.pos.row, mark.pos.col, - &decor, false); + &decor, false, mark.ns, mark.id); } else { if (altpos.row == -1) { altpos.row = mark.pos.row; altpos.col = mark.pos.col; } decor_add(state, mark.pos.row, mark.pos.col, altpos.row, altpos.col, - &decor, false); + &decor, false, mark.ns, mark.id); } next_mark: @@ -246,13 +251,13 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state) } static void decor_add(DecorState *state, int start_row, int start_col, int end_row, int end_col, - Decoration *decor, bool owned) + Decoration *decor, bool owned, uint64_t ns_id, uint64_t mark_id) { int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0; DecorRange range = { start_row, start_col, end_row, end_col, *decor, attr_id, - kv_size(decor->virt_text) && owned, -1 }; + kv_size(decor->virt_text) && owned, -1, ns_id, mark_id }; kv_pushp(state->active); size_t index; @@ -298,13 +303,13 @@ int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState * if (endpos.row < mark.pos.row || (endpos.row == mark.pos.row && endpos.col <= mark.pos.col)) { - if (!kv_size(decor.virt_text)) { + if (!kv_size(decor.virt_text) && !decor.ui_watched) { goto next_mark; } } decor_add(state, mark.pos.row, mark.pos.col, endpos.row, endpos.col, - &decor, false); + &decor, false, mark.ns, mark.id); next_mark: marktree_itr_next(buf->b_marktree, state->itr); @@ -321,7 +326,8 @@ next_mark: bool active = false, keep = true; if (item.end_row < state->row || (item.end_row == state->row && item.end_col <= col)) { - if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) { + if (!(item.start_row >= state->row + && (kv_size(item.decor.virt_text) || item.decor.ui_watched))) { keep = false; } } else { @@ -349,7 +355,7 @@ next_mark: } } if ((item.start_row == state->row && item.start_col <= col) - && kv_size(item.decor.virt_text) + && (kv_size(item.decor.virt_text) || item.decor.ui_watched) && item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) { item.win_col = (item.decor.virt_text_hide && hidden) ? -2 : win_col; } @@ -517,7 +523,8 @@ bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col) bool has_virttext = false; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); - if (item.start_row == state->row && kv_size(item.decor.virt_text)) { + if (item.start_row == state->row + && (kv_size(item.decor.virt_text) || item.decor.ui_watched)) { has_virttext = true; } @@ -528,13 +535,14 @@ bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col) return has_virttext; } -void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor) +void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, + Decoration *decor, uint64_t ns_id, uint64_t mark_id) { if (end_row == -1) { end_row = start_row; end_col = start_col; } - decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true); + decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id); } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 97ab218f86..9a82af8661 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -60,10 +60,11 @@ struct Decoration { // TODO(bfredl): in principle this should be a schar_T, but we // probably want some kind of glyph cache for that.. int conceal_char; + bool ui_watched; // watched for win_extmark }; #define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \ kHlModeUnknown, false, false, false, false, DECOR_PRIORITY_BASE, \ - 0, 0, NULL, 0, 0, 0, 0, 0 } + 0, 0, NULL, 0, 0, 0, 0, 0, false } typedef struct { int start_row; @@ -74,6 +75,8 @@ typedef struct { int attr_id; // cached lookup of decor.hl_id bool virt_text_owned; int win_col; + uint64_t ns_id; + uint64_t mark_id; } DecorRange; typedef struct { diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index ffd68926f1..1b0e2c8590 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -69,7 +69,8 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col if (decor) { if (kv_size(decor->virt_text) || kv_size(decor->virt_lines) - || decor_has_sign(decor)) { + || decor_has_sign(decor) + || decor->ui_watched) { decor_full = true; decor = xmemdup(decor, sizeof *decor); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 58abc1599a..2c3f8f307b 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -67,6 +67,7 @@ #include "nvim/api/extmark.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/ui.h" #include "nvim/api/vim.h" #include "nvim/arabic.h" #include "nvim/ascii.h" @@ -161,6 +162,14 @@ static bool msg_grid_invalid = false; static bool resizing = false; +typedef struct { + NS ns_id; + uint64_t mark_id; + int win_row; + int win_col; +} WinExtmark; +static kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" @@ -1314,6 +1323,8 @@ static void win_update(win_T *wp, DecorProviders *providers) srow = 0; lnum = wp->w_topline; // first line shown in window + win_extmark_arr.size = 0; + decor_redraw_reset(buf, &decor_state); DecorProviders line_providers; @@ -1692,6 +1703,16 @@ static void win_update(win_T *wp, DecorProviders *providers) wp->w_old_topfill = wp->w_topfill; wp->w_old_botfill = wp->w_botfill; + // Send win_extmarks if needed + if (kv_size(win_extmark_arr) > 0) { + for (size_t n = 0; n < kv_size(win_extmark_arr); n++) { + ui_call_win_extmark( + wp->w_grid_alloc.handle, wp->handle, + kv_A(win_extmark_arr, n).ns_id, kv_A(win_extmark_arr, n).mark_id, + kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col); + } + } + if (dollar_vcol == -1) { /* * There is a trick with w_botline. If we invalidate it on each @@ -1964,7 +1985,7 @@ static inline void provider_err_virt_text(linenr_T lnum, char *err) ((VirtTextChunk){ .text = provider_err, .hl_id = hl_err })); err_decor.virt_text_width = mb_string2cells((char_u *)err); - decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor); + decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0); } static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len) @@ -2881,7 +2902,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && vcol >= (long)wp->w_virtcol) || (number_only && draw_state > WL_NR)) && filler_todo <= 0) { - draw_virt_text(buf, win_col_offset, &col, grid->Columns); + draw_virt_text(wp, buf, win_col_offset, &col, grid->Columns, row); grid_put_linebuf(grid, row, 0, col, -grid->Columns, wp->w_p_rl, wp, wp->w_hl_attr_normal, false); // Pretend we have finished updating the window. Except when @@ -3951,7 +3972,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - draw_virt_text(buf, win_col_offset, &col, grid->Columns); + draw_virt_text(wp, buf, win_col_offset, &col, grid->Columns, row); grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp, wp->w_hl_attr_normal, false); row++; @@ -4195,7 +4216,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc kHlModeReplace, grid->Columns, offset); } } else { - draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns); + draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->Columns, row); } grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl, @@ -4274,14 +4295,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc return row; } -void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) +void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col, int win_row) { DecorState *state = &decor_state; int right_pos = max_col; bool do_eol = state->eol_col > -1; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); - if (!(item->start_row == state->row && kv_size(item->decor.virt_text))) { + if (!(item->start_row == state->row + && (kv_size(item->decor.virt_text) || item->decor.ui_watched))) { continue; } if (item->win_col == -1) { @@ -4297,9 +4319,17 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) if (item->win_col < 0) { continue; } - - int col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text, - item->decor.hl_mode, max_col, item->win_col - col_off); + int col; + if (item->decor.ui_watched) { + // send mark position to UI + col = item->win_col; + WinExtmark m = { item->ns_id, item->mark_id, win_row, col }; + kv_push(win_extmark_arr, m); + } + if (kv_size(item->decor.virt_text)) { + col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text, + item->decor.hl_mode, max_col, item->win_col - col_off); + } item->win_col = -2; // deactivate if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { state->eol_col = col + 1; diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index e298eb3582..bc8d811c6d 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -1606,3 +1606,209 @@ describe('Extmarks buffer api with many marks', function() eq({}, get_marks(ns2)) end) end) + +describe('API/win_extmark', function() + local screen + local marks, line1, line2 + local ns + + before_each(function() + -- Initialize some namespaces and insert text into a buffer + marks = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} + + line1 = "non ui-watched line" + line2 = "ui-watched line" + + clear() + + insert(line1) + feed("o<esc>") + insert(line2) + ns = request('nvim_create_namespace', "extmark-ui") + end) + + it('sends and only sends ui-watched marks to ui', function() + screen = Screen.new(20, 4) + screen:attach() + -- should send this + set_extmark(ns, marks[1], 1, 0, { ui_watched = true }) + -- should not send this + set_extmark(ns, marks[2], 0, 0, { ui_watched = false }) + screen:expect({ + grid = [[ + non ui-watched line | + ui-watched lin^e | + ~ | + | + ]], + extmarks = { + [2] = { + -- positioned at the end of the 2nd line + { {id = 1000}, 1, 1, 1, 16 }, + } + }, + }) + end) + + it('sends multiple ui-watched marks to ui', function() + screen = Screen.new(20, 4) + screen:attach() + -- should send all of these + set_extmark(ns, marks[1], 1, 0, { ui_watched = true, virt_text_pos = "overlay" }) + set_extmark(ns, marks[2], 1, 2, { ui_watched = true, virt_text_pos = "overlay" }) + set_extmark(ns, marks[3], 1, 4, { ui_watched = true, virt_text_pos = "overlay" }) + set_extmark(ns, marks[4], 1, 6, { ui_watched = true, virt_text_pos = "overlay" }) + set_extmark(ns, marks[5], 1, 8, { ui_watched = true }) + screen:expect({ + grid = [[ + non ui-watched line | + ui-watched lin^e | + ~ | + | + ]], + extmarks = { + [2] = { + -- earlier notifications + { {id = 1000}, 1, 1, 1, 0 }, + { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, + { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, { {id = 1000}, 1, 3, 1, 4 }, + { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, { {id = 1000}, 1, 3, 1, 4 }, { {id = 1000}, 1, 4, 1, 6 }, + -- final + -- overlay + { {id = 1000}, 1, 1, 1, 0 }, + { {id = 1000}, 1, 2, 1, 2 }, + { {id = 1000}, 1, 3, 1, 4 }, + { {id = 1000}, 1, 4, 1, 6 }, + -- eol + { {id = 1000}, 1, 5, 1, 16 }, + } + }, + }) + end) + + it('updates ui-watched marks', function() + screen = Screen.new(20, 4) + screen:attach() + -- should send this + set_extmark(ns, marks[1], 1, 0, { ui_watched = true }) + -- should not send this + set_extmark(ns, marks[2], 0, 0, { ui_watched = false }) + -- make some changes + insert(" update") + screen:expect({ + grid = [[ + non ui-watched line | + ui-watched linupdat^e| + e | + | + ]], + extmarks = { + [2] = { + -- positioned at the end of the 2nd line + { {id = 1000}, 1, 1, 1, 16 }, + -- updated and wrapped to 3rd line + { {id = 1000}, 1, 1, 2, 2 }, + } + } + }) + feed("<c-e>") + screen:expect({ + grid = [[ + ui-watched linupdat^e| + e | + ~ | + | + ]], + extmarks = { + [2] = { + -- positioned at the end of the 2nd line + { {id = 1000}, 1, 1, 1, 16 }, + -- updated and wrapped to 3rd line + { {id = 1000}, 1, 1, 2, 2 }, + -- scrolled up one line, should be handled by grid scroll + } + } + }) + end) + + it('sends ui-watched to splits', function() + screen = Screen.new(20, 8) + screen:attach({ext_multigrid=true}) + -- should send this + set_extmark(ns, marks[1], 1, 0, { ui_watched = true }) + -- should not send this + set_extmark(ns, marks[2], 0, 0, { ui_watched = false }) + command('split') + screen:expect({ + grid = [[ + ## grid 1 + [4:--------------------]| + [4:--------------------]| + [4:--------------------]| + [No Name] [+] | + [2:--------------------]| + [2:--------------------]| + [No Name] [+] | + [3:--------------------]| + ## grid 2 + non ui-watched line | + ui-watched line | + ## grid 3 + | + ## grid 4 + non ui-watched line | + ui-watched lin^e | + ~ | + ]], + extmarks = { + [2] = { + -- positioned at the end of the 2nd line + { {id = 1000}, 1, 1, 1, 16 }, + -- updated after split + { {id = 1000}, 1, 1, 1, 16 }, + }, + [4] = { + -- only after split + { {id = 1001}, 1, 1, 1, 16 }, + } + } + }) + -- make some changes + insert(" update") + screen:expect({ + grid = [[ + ## grid 1 + [4:--------------------]| + [4:--------------------]| + [4:--------------------]| + [No Name] [+] | + [2:--------------------]| + [2:--------------------]| + [No Name] [+] | + [3:--------------------]| + ## grid 2 + non ui-watched line | + ui-watched linupd@@@| + ## grid 3 + | + ## grid 4 + non ui-watched line | + ui-watched linupdat^e| + e | + ]], + extmarks = { + [2] = { + -- positioned at the end of the 2nd line + { {id = 1000}, 1, 1, 1, 16 }, + -- updated after split + { {id = 1000}, 1, 1, 1, 16 }, + }, + [4] = { + { {id = 1001}, 1, 1, 1, 16 }, + -- updated + { {id = 1001}, 1, 1, 2, 2 }, + } + } + }) + end) +end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index e8a39ab6f8..b3d1abca69 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -179,6 +179,7 @@ function Screen.new(width, height) _width = width, _height = height, _grids = {}, + _grid_win_extmarks = {}, _cursor = { grid = 1, row = 1, col = 1 }, @@ -278,6 +279,8 @@ local ext_keys = { -- attributes in the final state are an error. -- Use screen:set_default_attr_ids() to define attributes for many -- expect() calls. +-- extmarks: Expected win_extmarks accumulated for the grids. For each grid, +-- the win_extmark messages are accumulated into an array. -- condition: Function asserting some arbitrary condition. Return value is -- ignored, throw an error (use eq() or similar) to signal failure. -- any: Lua pattern string expected to match a screen line. NB: the @@ -320,7 +323,7 @@ function Screen:expect(expected, attr_ids, ...) assert(not (attr_ids ~= nil)) local is_key = {grid=true, attr_ids=true, condition=true, mouse_enabled=true, any=true, mode=true, unchanged=true, intermediate=true, - reset=true, timeout=true, request_cb=true, hl_groups=true} + reset=true, timeout=true, request_cb=true, hl_groups=true, extmarks=true} for _, v in ipairs(ext_keys) do is_key[v] = true end @@ -459,6 +462,25 @@ screen:redraw_debug() to show all intermediate screen states. ]]) end end end + + if expected.extmarks ~= nil then + for gridid, expected_marks in pairs(expected.extmarks) do + local stored_marks = self._grid_win_extmarks[gridid] + if stored_marks == nil then + return 'no win_extmark for grid '..tostring(gridid) + end + local status, res = pcall(eq, expected_marks, stored_marks, "extmarks for grid "..tostring(gridid)) + if not status then + return tostring(res) + end + end + for gridid, _ in pairs(self._grid_win_extmarks) do + local expected_marks = expected.extmarks[gridid] + if expected_marks == nil then + return 'unexpected win_extmark for grid '..tostring(gridid) + end + end + end end, expected) end @@ -703,6 +725,7 @@ function Screen:_reset() self.cmdline_block = {} self.wildmenu_items = nil self.wildmenu_pos = nil + self._grid_win_extmarks = {} end function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info) @@ -803,6 +826,13 @@ function Screen:_handle_win_close(grid) self.float_pos[grid] = nil end +function Screen:_handle_win_extmark(grid, ...) + if self._grid_win_extmarks[grid] == nil then + self._grid_win_extmarks[grid] = {} + end + table.insert(self._grid_win_extmarks[grid], {...}) +end + function Screen:_handle_busy_start() self._busy = true end |