From b7763d7f6b7fdcabe06658c664457df8bc147563 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Sep 2023 17:56:05 +0800 Subject: [PATCH] fix(api): get virtual text with multiple hl properly (#25307) --- src/nvim/api/extmark.c | 57 +++++++++++++++---------- src/nvim/api/win_config.c | 18 +++----- test/functional/api/extmark_spec.lua | 18 +++++--- test/functional/api/window_spec.lua | 22 ++++++++-- test/functional/ui/decorations_spec.lua | 5 ++- 5 files changed, 74 insertions(+), 46 deletions(-) diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 1e44c66974..91e197bea7 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -113,6 +113,36 @@ static Object hl_group_name(int hl_id, bool hl_name) } } +Array virt_text_to_array(VirtText vt, bool hl_name) +{ + Array chunks = ARRAY_DICT_INIT; + Array hl_array = ARRAY_DICT_INIT; + for (size_t i = 0; i < kv_size(vt); i++) { + char *text = kv_A(vt, i).text; + int hl_id = kv_A(vt, i).hl_id; + if (text == NULL) { + if (hl_id > 0) { + ADD(hl_array, hl_group_name(hl_id, hl_name)); + } + continue; + } + Array chunk = ARRAY_DICT_INIT; + ADD(chunk, CSTR_TO_OBJ(text)); + if (hl_array.size > 0) { + if (hl_id > 0) { + ADD(hl_array, hl_group_name(hl_id, hl_name)); + } + ADD(chunk, ARRAY_OBJ(hl_array)); + hl_array = (Array)ARRAY_DICT_INIT; + } else if (hl_id > 0) { + ADD(chunk, hl_group_name(hl_id, hl_name)); + } + ADD(chunks, ARRAY_OBJ(chunk)); + } + assert(hl_array.size == 0); + return chunks; +} + static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict, bool hl_name) { Array rv = ARRAY_DICT_INIT; @@ -145,16 +175,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict } if (kv_size(decor->virt_text)) { - Array chunks = ARRAY_DICT_INIT; - for (size_t i = 0; i < decor->virt_text.size; i++) { - Array chunk = ARRAY_DICT_INIT; - VirtTextChunk *vtc = &decor->virt_text.items[i]; - ADD(chunk, CSTR_TO_OBJ(vtc->text)); - if (vtc->hl_id > 0) { - ADD(chunk, hl_group_name(vtc->hl_id, hl_name)); - } - ADD(chunks, ARRAY_OBJ(chunk)); - } + Array chunks = virt_text_to_array(decor->virt_text, hl_name); PUT(dict, "virt_text", ARRAY_OBJ(chunks)); PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide)); if (decor->virt_text_pos == kVTWinCol) { @@ -171,19 +192,9 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict if (kv_size(decor->virt_lines)) { Array all_chunks = ARRAY_DICT_INIT; bool virt_lines_leftcol = false; - for (size_t i = 0; i < decor->virt_lines.size; i++) { - Array chunks = ARRAY_DICT_INIT; - VirtText *vt = &decor->virt_lines.items[i].line; - virt_lines_leftcol = decor->virt_lines.items[i].left_col; - for (size_t j = 0; j < vt->size; j++) { - Array chunk = ARRAY_DICT_INIT; - VirtTextChunk *vtc = &vt->items[j]; - ADD(chunk, CSTR_TO_OBJ(vtc->text)); - if (vtc->hl_id > 0) { - ADD(chunk, hl_group_name(vtc->hl_id, hl_name)); - } - ADD(chunks, ARRAY_OBJ(chunk)); - } + for (size_t i = 0; i < kv_size(decor->virt_lines); i++) { + virt_lines_leftcol = kv_A(decor->virt_lines, i).left_col; + Array chunks = virt_text_to_array(kv_A(decor->virt_lines, i).line, hl_name); ADD(all_chunks, ARRAY_OBJ(chunks)); } PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks)); diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index ebdbd896a5..ea0b7ce512 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -259,34 +259,28 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, BorderTextType bordertext_type) { - VirtText chunks; + VirtText vt; AlignTextPos align; char *field_name; char *field_pos_name; switch (bordertext_type) { case kBorderTextTitle: - chunks = fconfig->title_chunks; + vt = fconfig->title_chunks; align = fconfig->title_pos; field_name = "title"; field_pos_name = "title_pos"; break; case kBorderTextFooter: - chunks = fconfig->footer_chunks; + vt = fconfig->footer_chunks; align = fconfig->footer_pos; field_name = "footer"; field_pos_name = "footer_pos"; break; + default: + abort(); } - Array bordertext = ARRAY_DICT_INIT; - for (size_t i = 0; i < chunks.size; i++) { - Array tuple = ARRAY_DICT_INIT; - ADD(tuple, CSTR_TO_OBJ(chunks.items[i].text)); - if (chunks.items[i].hl_id > 0) { - ADD(tuple, CSTR_TO_OBJ(syn_id2name(chunks.items[i].hl_id))); - } - ADD(bordertext, ARRAY_OBJ(tuple)); - } + Array bordertext = virt_text_to_array(vt, true); PUT(config, field_name, ARRAY_OBJ(bordertext)); char *pos; diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index c4449bc201..133ec67942 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -1524,16 +1524,19 @@ describe('API/extmarks', function() sign_hl_group = "Statement", sign_text = ">>", spell = true, - virt_lines = { { { "lines", "Statement" } }}, + virt_lines = { + { { "lines", "Macro" }, { "???" } }, + { { "stack", { "Type", "Search" } }, { "!!!" } }, + }, virt_lines_above = true, virt_lines_leftcol = true, - virt_text = { { "text", "Statement" } }, + virt_text = { { "text", "Macro" }, { "???" }, { "stack", { "Type", "Search" } } }, virt_text_hide = true, virt_text_pos = "right_align", }) set_extmark(ns, marks[2], 0, 0, { priority = 0, - virt_text = { { "text", "Statement" } }, + virt_text = { { "", "Macro" }, { "", { "Type", "Search" } }, { "" } }, virt_text_win_col = 1, }) eq({0, 0, { @@ -1553,10 +1556,13 @@ describe('API/extmarks', function() sign_hl_group = "Statement", sign_text = ">>", spell = true, - virt_lines = { { { "lines", "Statement" } }}, + virt_lines = { + { { "lines", "Macro" }, { "???" } }, + { { "stack", { "Type", "Search" } }, { "!!!" } }, + }, virt_lines_above = true, virt_lines_leftcol = true, - virt_text = { { "text", "Statement" } }, + virt_text = { { "text", "Macro" }, { "???" }, { "stack", { "Type", "Search" } } }, virt_text_hide = true, virt_text_pos = "right_align", } }, get_extmark_by_id(ns, marks[1], { details = true })) @@ -1564,7 +1570,7 @@ describe('API/extmarks', function() ns_id = 1, right_gravity = true, priority = 0, - virt_text = { { "text", "Statement" } }, + virt_text = { { "", "Macro" }, { "", { "Type", "Search" } }, { "" } }, virt_text_hide = false, virt_text_pos = "win_col", virt_text_win_col = 1, diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 44d4470337..6737c2d15b 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -895,13 +895,14 @@ describe('API/win', function() it('includes border', function() local b = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' } local win = meths.open_win(0, true, { - relative='win', row=3, col=3, width=12, height=3, - border = b, + relative='win', row=3, col=3, width=12, height=3, + border = b, }) local cfg = meths.win_get_config(win) eq(b, cfg.border) end) + it('includes border with highlight group', function() local b = { {'a', 'Normal'}, @@ -914,12 +915,25 @@ describe('API/win', function() {'h', 'PreProc'}, } local win = meths.open_win(0, true, { - relative='win', row=3, col=3, width=12, height=3, - border = b, + relative='win', row=3, col=3, width=12, height=3, + border = b, }) local cfg = meths.win_get_config(win) eq(b, cfg.border) end) + + it('includes title and footer', function() + local title = { {'A', {'StatusLine', 'TabLine'}}, {'B'}, {'C', 'WinBar'} } + local footer = { {'A', 'WinBar'}, {'B'}, {'C', {'StatusLine', 'TabLine'}} } + local win = meths.open_win(0, true, { + relative='win', row=3, col=3, width=12, height=3, + border = 'single', title = title, footer = footer, + }) + + local cfg = meths.win_get_config(win) + eq(title, cfg.title) + eq(footer, cfg.footer) + end) end) end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index c4fd6379f3..f378c50792 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1464,7 +1464,7 @@ describe('extmark decorations', function() end) it('can have virtual text which combines foreground and background groups', function() - screen:try_resize(20, 3) + screen:try_resize(20, 5) screen:set_default_attr_ids { [1] = {bold=true, foreground=Screen.colors.Blue}; @@ -1494,8 +1494,11 @@ describe('extmark decorations', function() meths.buf_set_extmark(0, ns, 0, 0, { virt_text = vt, virt_text_pos = 'eol' }) meths.buf_set_extmark(0, ns, 0, 0, { virt_text = vt, virt_text_pos = 'right_align' }) meths.buf_set_extmark(0, ns, 0, 0, { virt_text = vt, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 0, 0, { virt_lines = { vt, vt } }) screen:expect{grid=[[ {2:a}{3:b}{4:c}{5:d}{6:X}#^# {2:a}{3:b}{4:c}{5:d}{6:X} {2:a}{3:b}{4:c}{5:d}{6:X}| + {2:a}{3:b}{4:c}{5:d}{6:X} | + {2:a}{3:b}{4:c}{5:d}{6:X} | {1:~ }| | ]]}