diff options
author | bfredl <bjorn.linse@gmail.com> | 2025-01-17 13:44:07 +0100 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2025-01-21 12:00:24 +0100 |
commit | 4cced601c8cdfd6253266b035667dd0383a07ebe (patch) | |
tree | 86409020fbd2442369d5008348844cc19c9103e0 | |
parent | 5dd60e01ace2621f2307eebeb92e9e7351210d3a (diff) | |
download | rneovim-4cced601c8cdfd6253266b035667dd0383a07ebe.tar.gz rneovim-4cced601c8cdfd6253266b035667dd0383a07ebe.tar.bz2 rneovim-4cced601c8cdfd6253266b035667dd0383a07ebe.zip |
feat(extmark): stack multiple highlight groups in `hl_group`
This has been possible in the "backend" for a while but
API was missing.
Followup: we will need a `details2=true` mode for `nvim_get_hl_id_by_name`
to return information in a way forward compatible with even further
enhancements.
-rw-r--r-- | runtime/doc/api.txt | 2 | ||||
-rw-r--r-- | runtime/doc/news.txt | 1 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/api.lua | 3 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/api_keysets.lua | 2 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 48 | ||||
-rw-r--r-- | src/nvim/api/keysets_defs.h | 2 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 43 |
7 files changed, 97 insertions, 4 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 5a6361d45f..1cc4350654 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2660,6 +2660,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) and below highlight groups can be supplied either as a string or as an integer, the latter of which can be obtained using |nvim_get_hl_id_by_name()|. + Multiple highlight groups can be stacked by passing an + array (highest priority last). • hl_eol : when true, for a multiline highlight covering the EOL of a line, continue the highlight for the rest of the screen line (just like for diff and cursorline highlight). diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 33ffeae2bb..b777d22a26 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -188,6 +188,7 @@ API • |nvim_echo()| `err` field to print error messages and `chunks` accepts highlight group IDs. • |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 DEFAULTS diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 670e867c1e..3ffbc89b08 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -595,6 +595,9 @@ function vim.api.nvim_buf_line_count(buffer) end --- - hl_group : highlight group used for the text range. This and below --- highlight groups can be supplied either as a string or as an integer, --- the latter of which can be obtained using `nvim_get_hl_id_by_name()`. +--- +--- Multiple highlight groups can be stacked by passing an array (highest +--- priority last). --- - hl_eol : when true, for a multiline highlight covering the --- EOL of a line, continue the highlight for the rest --- of the screen line (just like for diff and diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 98e916115e..26c2c963de 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -241,7 +241,7 @@ error('Cannot require a meta file') --- @field end_line? integer --- @field end_row? integer --- @field end_col? integer ---- @field hl_group? integer|string +--- @field hl_group? any --- @field virt_text? any[] --- @field virt_text_pos? string --- @field virt_text_win_col? integer diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index a237e7531a..18e37012ee 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -385,6 +385,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - hl_group : highlight group used for the text range. This and below /// highlight groups can be supplied either as a string or as an integer, /// the latter of which can be obtained using |nvim_get_hl_id_by_name()|. +/// +/// Multiple highlight groups can be stacked by passing an array (highest +/// priority last). /// - hl_eol : when true, for a multiline highlight covering the /// EOL of a line, continue the highlight for the rest /// of the screen line (just like for diff and @@ -499,6 +502,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT; char *url = NULL; bool has_hl = false; + bool has_hl_multiple = false; buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -551,8 +555,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = (int)val; } - hl.hl_id = (int)opts->hl_group; - has_hl = hl.hl_id > 0; + if (HAS_KEY(opts, set_extmark, hl_group)) { + if (opts->hl_group.type == kObjectTypeArray) { + Array arr = opts->hl_group.data.array; + if (arr.size >= 1) { + hl.hl_id = object_to_hl_id(arr.items[0], "hl_group item", err); + if (ERROR_SET(err)) { + goto error; + } + } + for (size_t i = 1; i < arr.size; i++) { + int hl_id = object_to_hl_id(arr.items[i], "hl_group item", err); + if (ERROR_SET(err)) { + goto error; + } + if (hl_id) { + has_hl_multiple = true; + } + } + } else { + hl.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err); + if (ERROR_SET(err)) { + goto error; + } + } + has_hl = hl.hl_id > 0; + } + sign.hl_id = (int)opts->sign_hl_group; sign.cursorline_hl_id = (int)opts->cursorline_hl_group; sign.number_hl_id = (int)opts->number_hl_group; @@ -794,6 +823,21 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } } + if (has_hl_multiple) { + Array arr = opts->hl_group.data.array; + for (size_t i = arr.size - 1; i > 0; i--) { // skip hl_group[0], handled as hl.hl_id below + int hl_id = object_to_hl_id(arr.items[i], "hl_group item", err); + if (hl_id > 0) { + DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; + sh.hl_id = hl_id; + sh.flags = opts->hl_eol ? kSHHlEol : 0; + sh.next = decor_indexed; + decor_indexed = decor_put_sh(sh); + decor_flags |= MT_FLAG_DECOR_HL; + } + } + } + DecorInline decor = DECOR_INLINE_INIT; if (decor_alloc || decor_indexed != DECOR_ID_INVALID || url != NULL || schar_high(hl.conceal_char)) { diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 664406ab6e..953e467f1e 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -29,7 +29,7 @@ typedef struct { Integer end_line; Integer end_row; Integer end_col; - HLGroupID hl_group; + Object hl_group; Array virt_text; String virt_text_pos; Integer virt_text_win_col; diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index e364c473b7..c2030b9527 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -834,6 +834,9 @@ describe('extmark decorations', function() [42] = {undercurl = true, special = Screen.colors.Red}; [43] = {background = Screen.colors.Yellow, undercurl = true, special = Screen.colors.Red}; [44] = {background = Screen.colors.LightMagenta}; + [45] = { background = Screen.colors.Red, special = Screen.colors.Red, foreground = Screen.colors.Red }; + [46] = { background = Screen.colors.Blue, foreground = Screen.colors.Blue, special = Screen.colors.Red }; + [47] = { background = Screen.colors.Green, foreground = Screen.colors.Blue, special = Screen.colors.Red }; } ns = api.nvim_create_namespace 'test' @@ -1924,6 +1927,46 @@ describe('extmark decorations', function() ]]} end) + it('highlight can combine multiple groups', function() + screen:try_resize(50, 3) + command('hi Group1 guibg=Red guifg=Red guisp=Red') + command('hi Group2 guibg=Blue guifg=Blue') + command('hi Group3 guibg=Green') + insert([[example text]]) + api.nvim_buf_set_extmark(0, ns, 0, 0, { end_row=1, hl_group = {} }) + screen:expect([[ + example tex^t | + {1:~ }| + | + ]]) + + api.nvim_buf_clear_namespace(0, ns, 0, -1) + api.nvim_buf_set_extmark(0, ns, 0, 0, { end_row=1, hl_group = {'Group1'} }) + screen:expect([[ + {45:example tex^t} | + {1:~ }| + | + ]]) + api.nvim_buf_clear_namespace(0, ns, 0, -1) + api.nvim_buf_set_extmark(0, ns, 0, 0, { end_row = 1, hl_group = {'Group1', 'Group2'} }) + screen:expect([[ + {46:example tex^t} | + {1:~ }| + | + ]]) + api.nvim_buf_clear_namespace(0, ns, 0, -1) + api.nvim_buf_set_extmark(0, ns, 0, 0, { end_row = 1, hl_group = {'Group1', 'Group2', 'Group3'}, hl_eol=true }) + screen:expect([[ + {47:example tex^t }| + {1:~ }| + | + ]]) + + eq('Invalid hl_group: hl_group item', + pcall_err(api.nvim_buf_set_extmark, 0, ns, 0, 0, { end_row = 1, hl_group = {'Group1', 'Group2', {'fail'}}, hl_eol=true })) + end) + + it('highlight works after TAB with sidescroll #14201', function() screen:try_resize(50, 3) command('set nowrap') |