mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge #7082 'api: nvim_get_hl_by_name/by_id'
This commit is contained in:
		| @@ -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); | ||||
|   | ||||
| @@ -55,6 +55,47 @@ void nvim_command(String command, Error *err) | ||||
|   try_end(err); | ||||
| } | ||||
|  | ||||
| /// Gets a highlight definition by name. | ||||
| /// | ||||
| /// @param name Highlight group name | ||||
| /// @param rgb Export RGB colors | ||||
| /// @param[out] err Error details, if any | ||||
| /// @return Highlight definition map | ||||
| /// @see nvim_get_hl_by_id | ||||
| Dictionary nvim_get_hl_by_name(String name, Boolean rgb, 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, rgb, err); | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| /// Gets a highlight definition by id. |hlID()| | ||||
| /// | ||||
| /// @param hl_id Highlight id as returned by |hlID()| | ||||
| /// @param rgb Export RGB colors | ||||
| /// @param[out] err Error details, if any | ||||
| /// @return Highlight definition map | ||||
| /// @see nvim_get_hl_by_name | ||||
| Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, 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 hl_get_attr_by_id(attrcode, rgb, 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; | ||||
|  | ||||
| @@ -6644,7 +6645,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(); | ||||
| @@ -6879,6 +6879,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; | ||||
| @@ -7123,22 +7124,14 @@ 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; | ||||
| @@ -8247,6 +8240,30 @@ RgbValue name_to_color(const uint8_t *name) | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| /// Gets highlight description for id `attr_id` as a map. | ||||
| Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, 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, rgb); | ||||
|  | ||||
| end: | ||||
|   return hlattrs2dict(attrs); | ||||
| } | ||||
|  | ||||
|  | ||||
| /************************************** | ||||
| *  End of Highlighting stuff	      * | ||||
| **************************************/ | ||||
|   | ||||
| @@ -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++) { \ | ||||
|   | ||||
							
								
								
									
										124
									
								
								src/nvim/ui.c
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								src/nvim/ui.c
									
									
									
									
									
								
							| @@ -166,6 +166,90 @@ void ui_event(char *name, Array args) | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| /// 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 +489,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 { | ||||
|   | ||||
							
								
								
									
										103
									
								
								test/functional/api/highlight_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								test/functional/api/highlight_spec.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| 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 meths = helpers.meths | ||||
|  | ||||
| describe('highlight api',function() | ||||
|   local expected_rgb = { | ||||
|     background = Screen.colors.Yellow, | ||||
|     foreground = Screen.colors.Red, | ||||
|     special = Screen.colors.Blue, | ||||
|     bold = true, | ||||
|   } | ||||
|   local expected_cterm = { | ||||
|     background = 10, | ||||
|     underline = true, | ||||
|   } | ||||
|   local expected_rgb2 = { | ||||
|     background = Screen.colors.Yellow, | ||||
|     foreground = Screen.colors.Red, | ||||
|     special = Screen.colors.Blue, | ||||
|     bold = true, | ||||
|     italic = true, | ||||
|     reverse = true, | ||||
|     undercurl = true, | ||||
|     underline = true, | ||||
|   } | ||||
|  | ||||
|   before_each(function() | ||||
|     clear() | ||||
|     command("hi NewHighlight cterm=underline ctermbg=green guifg=red guibg=yellow guisp=blue gui=bold") | ||||
|   end) | ||||
|  | ||||
|   it("nvim_get_hl_by_id", function() | ||||
|     local hl_id = eval("hlID('NewHighlight')") | ||||
|     eq(expected_cterm, nvim("get_hl_by_id", hl_id, false)) | ||||
|  | ||||
|     hl_id = eval("hlID('NewHighlight')") | ||||
|     -- Test valid id. | ||||
|     eq(expected_rgb, nvim("get_hl_by_id", hl_id, true)) | ||||
|  | ||||
|     -- Test invalid id. | ||||
|     local err, emsg = pcall(meths.get_hl_by_id, 30000, false) | ||||
|     eq(false, err) | ||||
|     eq('Invalid highlight id: 30000', string.match(emsg, 'Invalid.*')) | ||||
|  | ||||
|     -- Test all highlight properties. | ||||
|     command('hi NewHighlight gui=underline,bold,undercurl,italic,reverse') | ||||
|     eq(expected_rgb2, nvim("get_hl_by_id", hl_id, true)) | ||||
|  | ||||
|     -- Test nil argument. | ||||
|     err, emsg = pcall(meths.get_hl_by_id, { nil }, false) | ||||
|     eq(false, err) | ||||
|     eq('Wrong type for argument 1, expecting Integer', | ||||
|        string.match(emsg, 'Wrong.*')) | ||||
|  | ||||
|     -- Test 0 argument. | ||||
|     err, emsg = pcall(meths.get_hl_by_id, 0, false) | ||||
|     eq(false, err) | ||||
|     eq('Invalid highlight id: 0', | ||||
|        string.match(emsg, 'Invalid.*')) | ||||
|  | ||||
|     -- Test -1 argument. | ||||
|     err, emsg = pcall(meths.get_hl_by_id, -1, false) | ||||
|     eq(false, err) | ||||
|     eq('Invalid highlight id: -1', | ||||
|        string.match(emsg, 'Invalid.*')) | ||||
|   end) | ||||
|  | ||||
|   it("nvim_get_hl_by_name", function() | ||||
|     local expected_normal = { background = Screen.colors.Yellow, | ||||
|                               foreground = Screen.colors.Red } | ||||
|  | ||||
|     -- Test `Normal` default values. | ||||
|     eq({}, nvim("get_hl_by_name", 'Normal', true)) | ||||
|  | ||||
|     eq(expected_cterm, nvim("get_hl_by_name", 'NewHighlight', false)) | ||||
|     eq(expected_rgb, nvim("get_hl_by_name", 'NewHighlight', true)) | ||||
|  | ||||
|     -- Test `Normal` modified values. | ||||
|     command('hi Normal guifg=red guibg=yellow') | ||||
|     eq(expected_normal, nvim("get_hl_by_name", 'Normal', true)) | ||||
|  | ||||
|     -- Test invalid name. | ||||
|     local err, emsg = pcall(meths.get_hl_by_name , 'unknown_highlight', false) | ||||
|     eq(false, err) | ||||
|     eq('Invalid highlight name: unknown_highlight', | ||||
|        string.match(emsg, 'Invalid.*')) | ||||
|  | ||||
|     -- Test nil argument. | ||||
|     err, emsg = pcall(meths.get_hl_by_name , { nil }, false) | ||||
|     eq(false, err) | ||||
|     eq('Wrong type for argument 1, expecting String', | ||||
|        string.match(emsg, 'Wrong.*')) | ||||
|  | ||||
|     -- Test empty string argument. | ||||
|     err, emsg = pcall(meths.get_hl_by_name , '', false) | ||||
|     eq(false, err) | ||||
|     eq('Invalid highlight name: ', | ||||
|        string.match(emsg, 'Invalid.*')) | ||||
|   end) | ||||
| end) | ||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes