mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Adds nvim_get_hl_by_name/by_id
...in order to retrieve highlights. Added test/functional/api/highlight_spec.lua HL_NORMAL is not really a good name, since it's more like an empty attribute than the normal's one. If one pays attention, syn_cterm_attr2entry is never called with attr=0 because it's always special cased before. I suggest in subsequent PRs we remove the ATTR_OFF and just insert an EMPTY ATTR/RESET_ATTR/UNINITIALIZED for id 0.
This commit is contained in:
parent
c580ef68e8
commit
ba7277cfb4
@ -242,39 +242,7 @@ static void push_call(UI *ui, char *name, Array args)
|
||||
static void remote_ui_highlight_set(UI *ui, HlAttrs attrs)
|
||||
{
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
Dictionary hl = ARRAY_DICT_INIT;
|
||||
|
||||
if (attrs.bold) {
|
||||
PUT(hl, "bold", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.underline) {
|
||||
PUT(hl, "underline", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.undercurl) {
|
||||
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.italic) {
|
||||
PUT(hl, "italic", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.reverse) {
|
||||
PUT(hl, "reverse", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.foreground != -1) {
|
||||
PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground));
|
||||
}
|
||||
|
||||
if (attrs.background != -1) {
|
||||
PUT(hl, "background", INTEGER_OBJ(attrs.background));
|
||||
}
|
||||
|
||||
if (attrs.special != -1) {
|
||||
PUT(hl, "special", INTEGER_OBJ(attrs.special));
|
||||
}
|
||||
Dictionary hl = hlattrs2dict(attrs);
|
||||
|
||||
ADD(args, DICTIONARY_OBJ(hl));
|
||||
push_call(ui, "highlight_set", args);
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/ui.h"
|
||||
|
||||
#define LINE_BUFFER_SIZE 4096
|
||||
|
||||
@ -55,6 +56,42 @@ void nvim_command(String command, Error *err)
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
/// Retrieves highlight description from its name
|
||||
///
|
||||
/// @param name Highlight group name
|
||||
/// @return a highlight description e.g. {'bold': true, 'bg': 123, 'fg': 42}
|
||||
/// @see nvim_get_hl_by_id
|
||||
Dictionary nvim_get_hl_by_name(String name, Error *err)
|
||||
FUNC_API_SINCE(3)
|
||||
{
|
||||
Dictionary result = ARRAY_DICT_INIT;
|
||||
int id = syn_name2id((const char_u *)name.data);
|
||||
|
||||
if (id == 0) {
|
||||
api_set_error(err, kErrorTypeException, "Invalid highlight name %s",
|
||||
name.data);
|
||||
return result;
|
||||
}
|
||||
result = nvim_get_hl_by_id(id, err);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Retrieves highlight description from its id
|
||||
///
|
||||
/// @param hl_id highlight id as returned by hlID()
|
||||
/// @see nvim_get_hl_by_name
|
||||
Dictionary nvim_get_hl_by_id(Integer hl_id, Error *err)
|
||||
FUNC_API_SINCE(3)
|
||||
{
|
||||
Dictionary dic = ARRAY_DICT_INIT;
|
||||
if (syn_get_final_id((int)hl_id) == 0) {
|
||||
api_set_error(err, kErrorTypeException, "Invalid highlight id %d", hl_id);
|
||||
return dic;
|
||||
}
|
||||
int attrcode = syn_id2attr((int)hl_id);
|
||||
return get_attr_by_id(attrcode, err);
|
||||
}
|
||||
|
||||
/// Passes input keys to Nvim.
|
||||
/// On VimL error: Does not fail, but updates v:errmsg.
|
||||
///
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/time.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
|
||||
static bool did_syntax_onoff = false;
|
||||
|
||||
@ -6622,7 +6623,6 @@ do_highlight(char_u *line, int forceit, int init) {
|
||||
syn_unadd_group();
|
||||
} else {
|
||||
if (is_normal_group) {
|
||||
HL_TABLE()[idx].sg_attr = 0;
|
||||
// Need to update all groups, because they might be using "bg" and/or
|
||||
// "fg", which have been changed now.
|
||||
highlight_attr_set_all();
|
||||
@ -6826,7 +6826,7 @@ int hl_combine_attr(int char_attr, int prim_attr)
|
||||
// Copy all attributes from char_aep to the new entry
|
||||
new_en = *char_aep;
|
||||
} else {
|
||||
memset(&new_en, 0, sizeof(new_en));
|
||||
new_en = (attrentry_T)ATTRENTRY_INIT;
|
||||
}
|
||||
|
||||
spell_aep = syn_cterm_attr2entry(prim_attr);
|
||||
@ -6859,6 +6859,7 @@ int hl_combine_attr(int char_attr, int prim_attr)
|
||||
|
||||
/// \note this function does not apply exclusively to cterm attr contrary
|
||||
/// to what its name implies
|
||||
/// \warn don't call it with attr 0 (i.e., the null attribute)
|
||||
attrentry_T *syn_cterm_attr2entry(int attr)
|
||||
{
|
||||
attr -= ATTR_OFF;
|
||||
@ -7103,22 +7104,15 @@ syn_list_header(int did_header, int outlen, int id)
|
||||
return newline;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the attribute numbers for a highlight group.
|
||||
* Called after one of the attributes has changed.
|
||||
*/
|
||||
static void
|
||||
set_hl_attr (
|
||||
int idx /* index in array */
|
||||
)
|
||||
/// Set the attribute numbers for a highlight group.
|
||||
/// Called after one of the attributes has changed.
|
||||
/// @param idx corrected highlight index
|
||||
static void
|
||||
set_hl_attr(int idx)
|
||||
{
|
||||
attrentry_T at_en = ATTRENTRY_INIT;
|
||||
struct hl_group *sgp = HL_TABLE() + idx;
|
||||
|
||||
// The "Normal" group doesn't need an attribute number
|
||||
if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
at_en.cterm_ae_attr = sgp->sg_cterm;
|
||||
at_en.cterm_fg_color = sgp->sg_cterm_fg;
|
||||
|
@ -288,7 +288,7 @@ static void terminfo_stop(UI *ui)
|
||||
static void tui_terminal_start(UI *ui)
|
||||
{
|
||||
TUIData *data = ui->data;
|
||||
data->print_attrs = EMPTY_ATTRS;
|
||||
data->print_attrs = HLATTRS_INIT;
|
||||
ugrid_init(&data->grid);
|
||||
terminfo_start(ui);
|
||||
update_size(ui);
|
||||
@ -628,7 +628,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right)
|
||||
if (grid->bg == -1 && right == ui->width -1) {
|
||||
// Background is set to the default color and the right edge matches the
|
||||
// screen end, try to use terminal codes for clearing the requested area.
|
||||
HlAttrs clear_attrs = EMPTY_ATTRS;
|
||||
HlAttrs clear_attrs = HLATTRS_INIT;
|
||||
clear_attrs.foreground = grid->fg;
|
||||
clear_attrs.background = grid->bg;
|
||||
update_attrs(ui, clear_attrs);
|
||||
@ -926,7 +926,7 @@ static void tui_scroll(UI *ui, Integer count)
|
||||
cursor_goto(ui, grid->top, grid->left);
|
||||
// also set default color attributes or some terminals can become funny
|
||||
if (scroll_clears_to_current_colour) {
|
||||
HlAttrs clear_attrs = EMPTY_ATTRS;
|
||||
HlAttrs clear_attrs = HLATTRS_INIT;
|
||||
clear_attrs.foreground = grid->fg;
|
||||
clear_attrs.background = grid->bg;
|
||||
update_attrs(ui, clear_attrs);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
void ugrid_init(UGrid *grid)
|
||||
{
|
||||
grid->attrs = EMPTY_ATTRS;
|
||||
grid->attrs = HLATTRS_INIT;
|
||||
grid->fg = grid->bg = -1;
|
||||
grid->cells = NULL;
|
||||
}
|
||||
@ -118,7 +118,7 @@ UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size)
|
||||
|
||||
static void clear_region(UGrid *grid, int top, int bot, int left, int right)
|
||||
{
|
||||
HlAttrs clear_attrs = EMPTY_ATTRS;
|
||||
HlAttrs clear_attrs = HLATTRS_INIT;
|
||||
clear_attrs.foreground = grid->fg;
|
||||
clear_attrs.background = grid->bg;
|
||||
UGRID_FOREACH_CELL(grid, top, bot, left, right, {
|
||||
|
@ -21,8 +21,6 @@ struct ugrid {
|
||||
UCell **cells;
|
||||
};
|
||||
|
||||
#define EMPTY_ATTRS ((HlAttrs){ false, false, false, false, false, -1, -1, -1 })
|
||||
|
||||
#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \
|
||||
do { \
|
||||
for (int row = top; row <= bot; row++) { \
|
||||
|
149
src/nvim/ui.c
149
src/nvim/ui.c
@ -166,6 +166,115 @@ void ui_event(char *name, Array args)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves attribute description from its id
|
||||
///
|
||||
/// @param attr_id attribute id
|
||||
Dictionary get_attr_by_id(Integer attr_id, Error *err)
|
||||
{
|
||||
HlAttrs attrs = HLATTRS_INIT;
|
||||
Dictionary dic = ARRAY_DICT_INIT;
|
||||
|
||||
if (attr_id == 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
attrentry_T *aep = syn_cterm_attr2entry((int)attr_id);
|
||||
if (!aep) {
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Invalid attribute id %d", attr_id);
|
||||
return dic;
|
||||
}
|
||||
|
||||
attrs = attrentry2hlattrs(aep, p_tgc);
|
||||
|
||||
end:
|
||||
return hlattrs2dict(attrs);
|
||||
}
|
||||
|
||||
|
||||
/// Converts an attrentry_T into an HlAttrs
|
||||
///
|
||||
/// @param[in] aep data to convert
|
||||
/// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*'
|
||||
HlAttrs attrentry2hlattrs(const attrentry_T *aep, bool use_rgb)
|
||||
{
|
||||
assert(aep);
|
||||
|
||||
HlAttrs attrs = HLATTRS_INIT;
|
||||
int mask = 0;
|
||||
|
||||
mask = use_rgb ? aep->rgb_ae_attr : aep->cterm_ae_attr;
|
||||
|
||||
attrs.bold = mask & HL_BOLD;
|
||||
attrs.underline = mask & HL_UNDERLINE;
|
||||
attrs.undercurl = mask & HL_UNDERCURL;
|
||||
attrs.italic = mask & HL_ITALIC;
|
||||
attrs.reverse = mask & (HL_INVERSE | HL_STANDOUT);
|
||||
|
||||
if (use_rgb) {
|
||||
if (aep->rgb_fg_color != -1) {
|
||||
attrs.foreground = aep->rgb_fg_color;
|
||||
}
|
||||
|
||||
if (aep->rgb_bg_color != -1) {
|
||||
attrs.background = aep->rgb_bg_color;
|
||||
}
|
||||
|
||||
if (aep->rgb_sp_color != -1) {
|
||||
attrs.special = aep->rgb_sp_color;
|
||||
}
|
||||
} else {
|
||||
if (cterm_normal_fg_color != aep->cterm_fg_color) {
|
||||
attrs.foreground = aep->cterm_fg_color - 1;
|
||||
}
|
||||
|
||||
if (cterm_normal_bg_color != aep->cterm_bg_color) {
|
||||
attrs.background = aep->cterm_bg_color - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
Dictionary hlattrs2dict(HlAttrs attrs)
|
||||
{
|
||||
Dictionary hl = ARRAY_DICT_INIT;
|
||||
|
||||
if (attrs.bold) {
|
||||
PUT(hl, "bold", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.underline) {
|
||||
PUT(hl, "underline", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.undercurl) {
|
||||
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.italic) {
|
||||
PUT(hl, "italic", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.reverse) {
|
||||
PUT(hl, "reverse", BOOLEAN_OBJ(true));
|
||||
}
|
||||
|
||||
if (attrs.foreground != -1) {
|
||||
PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground));
|
||||
}
|
||||
|
||||
if (attrs.background != -1) {
|
||||
PUT(hl, "background", INTEGER_OBJ(attrs.background));
|
||||
}
|
||||
|
||||
if (attrs.special != -1) {
|
||||
PUT(hl, "special", INTEGER_OBJ(attrs.special));
|
||||
}
|
||||
|
||||
return hl;
|
||||
}
|
||||
|
||||
void ui_refresh(void)
|
||||
{
|
||||
if (!ui_active()) {
|
||||
@ -405,54 +514,20 @@ void ui_flush(void)
|
||||
|
||||
static void set_highlight_args(int attr_code)
|
||||
{
|
||||
HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1, -1 };
|
||||
HlAttrs rgb_attrs = HLATTRS_INIT;
|
||||
HlAttrs cterm_attrs = rgb_attrs;
|
||||
|
||||
if (attr_code == HL_NORMAL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
int rgb_mask = 0;
|
||||
int cterm_mask = 0;
|
||||
attrentry_T *aep = syn_cterm_attr2entry(attr_code);
|
||||
|
||||
if (!aep) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
rgb_mask = aep->rgb_ae_attr;
|
||||
cterm_mask = aep->cterm_ae_attr;
|
||||
|
||||
rgb_attrs.bold = rgb_mask & HL_BOLD;
|
||||
rgb_attrs.underline = rgb_mask & HL_UNDERLINE;
|
||||
rgb_attrs.undercurl = rgb_mask & HL_UNDERCURL;
|
||||
rgb_attrs.italic = rgb_mask & HL_ITALIC;
|
||||
rgb_attrs.reverse = rgb_mask & (HL_INVERSE | HL_STANDOUT);
|
||||
cterm_attrs.bold = cterm_mask & HL_BOLD;
|
||||
cterm_attrs.underline = cterm_mask & HL_UNDERLINE;
|
||||
cterm_attrs.undercurl = cterm_mask & HL_UNDERCURL;
|
||||
cterm_attrs.italic = cterm_mask & HL_ITALIC;
|
||||
cterm_attrs.reverse = cterm_mask & (HL_INVERSE | HL_STANDOUT);
|
||||
|
||||
if (aep->rgb_fg_color != normal_fg) {
|
||||
rgb_attrs.foreground = aep->rgb_fg_color;
|
||||
}
|
||||
|
||||
if (aep->rgb_bg_color != normal_bg) {
|
||||
rgb_attrs.background = aep->rgb_bg_color;
|
||||
}
|
||||
|
||||
if (aep->rgb_sp_color != normal_sp) {
|
||||
rgb_attrs.special = aep->rgb_sp_color;
|
||||
}
|
||||
|
||||
if (cterm_normal_fg_color != aep->cterm_fg_color) {
|
||||
cterm_attrs.foreground = aep->cterm_fg_color - 1;
|
||||
}
|
||||
|
||||
if (cterm_normal_bg_color != aep->cterm_bg_color) {
|
||||
cterm_attrs.background = aep->cterm_bg_color - 1;
|
||||
}
|
||||
rgb_attrs = attrentry2hlattrs(aep, true);
|
||||
cterm_attrs = attrentry2hlattrs(aep, false);
|
||||
|
||||
end:
|
||||
UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs));
|
||||
|
@ -21,6 +21,9 @@ typedef struct {
|
||||
int foreground, background, special;
|
||||
} HlAttrs;
|
||||
|
||||
#define HLATTRS_INIT \
|
||||
((HlAttrs){ false, false, false, false, false, -1, -1, -1 })
|
||||
|
||||
typedef struct ui_t UI;
|
||||
|
||||
struct ui_t {
|
||||
|
54
test/functional/api/highlight_spec.lua
Normal file
54
test/functional/api/highlight_spec.lua
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local clear, nvim = helpers.clear, helpers.nvim
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local eq, eval = helpers.eq, helpers.eval
|
||||
local command = helpers.command
|
||||
local ok = helpers.ok
|
||||
local meths = helpers.meths
|
||||
|
||||
|
||||
describe('highlight api', function()
|
||||
|
||||
before_each(function()
|
||||
clear('--cmd', 'set termguicolors')
|
||||
end)
|
||||
|
||||
it("nvim_get_hl_by_id", function()
|
||||
local expected_hl = { background = Screen.colors.Yellow,
|
||||
foreground = Screen.colors.Red,
|
||||
special = Screen.colors.Blue
|
||||
}
|
||||
|
||||
command('hi NewHighlight guifg=red guibg=yellow guisp=blue')
|
||||
|
||||
local hl_id = eval("hlID('NewHighlight')")
|
||||
eq(expected_hl, nvim("get_hl_by_id", hl_id))
|
||||
|
||||
-- assume there is no hl with 30000
|
||||
local err, emsg = pcall(meths.get_hl_by_id, 30000)
|
||||
eq(false, err)
|
||||
ok(string.find(emsg, 'Invalid highlight id') ~= nil)
|
||||
end)
|
||||
|
||||
it("nvim_get_hl_by_name", function()
|
||||
local expected_hl = { background = Screen.colors.Yellow,
|
||||
foreground = Screen.colors.Red }
|
||||
|
||||
-- test "Normal" hl defaults
|
||||
eq({}, nvim("get_hl_by_name", 'Normal'))
|
||||
|
||||
command('hi NewHighlight guifg=red guibg=yellow')
|
||||
eq(expected_hl, nvim("get_hl_by_name", 'NewHighlight'))
|
||||
|
||||
command('hi Normal guifg=red guibg=yellow')
|
||||
eq(expected_hl, nvim("get_hl_by_name", 'Normal'))
|
||||
local err, emsg = pcall(meths.get_hl_by_name , 'unknown_highlight')
|
||||
eq(false, err)
|
||||
ok(string.find(emsg, 'Invalid highlight name') ~= nil)
|
||||
end)
|
||||
|
||||
|
||||
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user