aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYatao Li <yatli@microsoft.com>2021-09-16 07:53:56 +0800
committerYatao Li <yatli@microsoft.com>2022-05-03 22:26:02 +0800
commit29a6cda3ffe981b09d4c59d49d6c97a4ea13ca8b (patch)
treeaef77156a4164f79b004e92bd685d23a40ba6bfb
parent8ea84eee570fd1ec560fe149e611d10034d9a223 (diff)
downloadrneovim-29a6cda3ffe981b09d4c59d49d6c97a4ea13ca8b.tar.gz
rneovim-29a6cda3ffe981b09d4c59d49d6c97a4ea13ca8b.tar.bz2
rneovim-29a6cda3ffe981b09d4c59d49d6c97a4ea13ca8b.zip
feat(api/ui): win_extmarks
-rw-r--r--runtime/doc/api.txt5
-rw-r--r--runtime/doc/ui.txt4
-rw-r--r--src/nvim/api/extmark.c14
-rw-r--r--src/nvim/api/keysets.lua1
-rw-r--r--src/nvim/api/ui.c1
-rw-r--r--src/nvim/api/ui.h1
-rw-r--r--src/nvim/api/ui_events.in.h4
-rw-r--r--src/nvim/decoration.c36
-rw-r--r--src/nvim/decoration.h5
-rw-r--r--src/nvim/extmark.c3
-rw-r--r--src/nvim/screen.c48
-rw-r--r--test/functional/api/extmark_spec.lua206
-rw-r--r--test/functional/ui/screen.lua32
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