mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge #4633: support "special" highlight (undercurl)
Closes #2040 Closes #3370
This commit is contained in:
		| @@ -504,6 +504,7 @@ EXTERN int cterm_normal_fg_bold INIT(= 0); | ||||
| EXTERN int cterm_normal_bg_color INIT(= 0); | ||||
| EXTERN RgbValue normal_fg INIT(= -1); | ||||
| EXTERN RgbValue normal_bg INIT(= -1); | ||||
| EXTERN RgbValue normal_sp INIT(= -1); | ||||
|  | ||||
| EXTERN int autocmd_busy INIT(= FALSE);          /* Is apply_autocmds() busy? */ | ||||
| EXTERN int autocmd_no_enter INIT(= FALSE);      /* *Enter autocmds disabled */ | ||||
|   | ||||
| @@ -96,6 +96,7 @@ static Object remote_ui_attach(uint64_t channel_id, uint64_t request_id, | ||||
|   ui->visual_bell = remote_ui_visual_bell; | ||||
|   ui->update_fg = remote_ui_update_fg; | ||||
|   ui->update_bg = remote_ui_update_bg; | ||||
|   ui->update_sp = remote_ui_update_sp; | ||||
|   ui->flush = remote_ui_flush; | ||||
|   ui->suspend = remote_ui_suspend; | ||||
|   ui->set_title = remote_ui_set_title; | ||||
| @@ -285,6 +286,10 @@ static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) | ||||
|     PUT(hl, "background", INTEGER_OBJ(attrs.background)); | ||||
|   } | ||||
|  | ||||
|   if (attrs.special != -1) { | ||||
|     PUT(hl, "special", INTEGER_OBJ(attrs.special)); | ||||
|   } | ||||
|  | ||||
|   ADD(args, DICTIONARY_OBJ(hl)); | ||||
|   push_call(ui, "highlight_set", args); | ||||
| } | ||||
| @@ -323,6 +328,13 @@ static void remote_ui_update_bg(UI *ui, int bg) | ||||
|   push_call(ui, "update_bg", args); | ||||
| } | ||||
|  | ||||
| static void remote_ui_update_sp(UI *ui, int sp) | ||||
| { | ||||
|   Array args = ARRAY_DICT_INIT; | ||||
|   ADD(args, INTEGER_OBJ(sp)); | ||||
|   push_call(ui, "update_sp", args); | ||||
| } | ||||
|  | ||||
| static void remote_ui_flush(UI *ui) | ||||
| { | ||||
|   UIData *data = ui->data; | ||||
|   | ||||
| @@ -62,8 +62,10 @@ struct hl_group { | ||||
|   int sg_gui;                   // "gui=" highlighting attributes | ||||
|   RgbValue sg_rgb_fg;           // RGB foreground color | ||||
|   RgbValue sg_rgb_bg;           // RGB background color | ||||
|   RgbValue sg_rgb_sp;           // RGB special color | ||||
|   uint8_t *sg_rgb_fg_name;      // RGB foreground color name | ||||
|   uint8_t *sg_rgb_bg_name;      // RGB background color name | ||||
|   uint8_t *sg_rgb_sp_name;      // RGB special color name | ||||
| }; | ||||
|  | ||||
| #define SG_CTERM        2       // cterm has been set | ||||
| @@ -6169,12 +6171,11 @@ do_highlight ( | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       /* | ||||
|        * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or | ||||
|        * "guibg"). | ||||
|        */ | ||||
|       while (*linep && !ascii_iswhite(*linep) && *linep != '=') | ||||
|         ++linep; | ||||
|       // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg", | ||||
|       // "guibg" or "guisp"). | ||||
|       while (*linep && !ascii_iswhite(*linep) && *linep != '=') { | ||||
|         linep++; | ||||
|       } | ||||
|       xfree(key); | ||||
|       key = vim_strnsave_up(key_start, (int)(linep - key_start)); | ||||
|       linep = skipwhite(linep); | ||||
| @@ -6452,7 +6453,23 @@ do_highlight ( | ||||
|           normal_bg = HL_TABLE()[idx].sg_rgb_bg; | ||||
|         } | ||||
|       } else if (STRCMP(key, "GUISP") == 0)   { | ||||
|         // Ignored for now | ||||
|         if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { | ||||
|           if (!init) | ||||
|             HL_TABLE()[idx].sg_set |= SG_GUI; | ||||
|  | ||||
|           xfree(HL_TABLE()[idx].sg_rgb_sp_name); | ||||
|           if (STRCMP(arg, "NONE") != 0) { | ||||
|             HL_TABLE()[idx].sg_rgb_sp_name = (uint8_t *)xstrdup((char *)arg); | ||||
|             HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); | ||||
|           } else { | ||||
|             HL_TABLE()[idx].sg_rgb_sp_name = NULL; | ||||
|             HL_TABLE()[idx].sg_rgb_sp = -1; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if (is_normal_group) { | ||||
|           normal_sp = HL_TABLE()[idx].sg_rgb_sp; | ||||
|         } | ||||
|       } else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)   { | ||||
|         // Ignored for now | ||||
|       } else { | ||||
| @@ -6516,6 +6533,7 @@ void restore_cterm_colors(void) | ||||
| { | ||||
|   normal_fg = -1; | ||||
|   normal_bg = -1; | ||||
|   normal_sp = -1; | ||||
|   cterm_normal_fg_color = 0; | ||||
|   cterm_normal_fg_bold = 0; | ||||
|   cterm_normal_bg_color = 0; | ||||
| @@ -6532,6 +6550,7 @@ static int hl_has_settings(int idx, int check_link) | ||||
|          || HL_TABLE()[idx].sg_cterm_bg != 0 | ||||
|          || HL_TABLE()[idx].sg_rgb_fg_name != NULL | ||||
|          || HL_TABLE()[idx].sg_rgb_bg_name != NULL | ||||
|          || HL_TABLE()[idx].sg_rgb_sp_name != NULL | ||||
|          || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)); | ||||
| } | ||||
|  | ||||
| @@ -6548,14 +6567,18 @@ static void highlight_clear(int idx) | ||||
|   HL_TABLE()[idx].sg_gui = 0; | ||||
|   HL_TABLE()[idx].sg_rgb_fg = -1; | ||||
|   HL_TABLE()[idx].sg_rgb_bg = -1; | ||||
|   HL_TABLE()[idx].sg_rgb_sp = -1; | ||||
|   xfree(HL_TABLE()[idx].sg_rgb_fg_name); | ||||
|   HL_TABLE()[idx].sg_rgb_fg_name = NULL; | ||||
|   xfree(HL_TABLE()[idx].sg_rgb_bg_name); | ||||
|   HL_TABLE()[idx].sg_rgb_bg_name = NULL; | ||||
|   /* Clear the script ID only when there is no link, since that is not | ||||
|    * cleared. */ | ||||
|   if (HL_TABLE()[idx].sg_link == 0) | ||||
|   xfree(HL_TABLE()[idx].sg_rgb_sp_name); | ||||
|   HL_TABLE()[idx].sg_rgb_sp_name = NULL; | ||||
|   // Clear the script ID only when there is no link, since that is not | ||||
|   // cleared. | ||||
|   if (HL_TABLE()[idx].sg_link == 0) { | ||||
|     HL_TABLE()[idx].sg_scriptID = 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -6597,7 +6620,8 @@ int get_attr_entry(attrentry_T *aep) | ||||
|         && aep->cterm_bg_color == taep->cterm_bg_color | ||||
|         && aep->rgb_ae_attr == taep->rgb_ae_attr | ||||
|         && aep->rgb_fg_color == taep->rgb_fg_color | ||||
|         && aep->rgb_bg_color == taep->rgb_bg_color) { | ||||
|         && aep->rgb_bg_color == taep->rgb_bg_color | ||||
|         && aep->rgb_sp_color == taep->rgb_sp_color) { | ||||
|       return i + ATTR_OFF; | ||||
|     } | ||||
|   } | ||||
| @@ -6635,6 +6659,7 @@ int get_attr_entry(attrentry_T *aep) | ||||
|   taep->rgb_ae_attr = aep->rgb_ae_attr; | ||||
|   taep->rgb_fg_color = aep->rgb_fg_color; | ||||
|   taep->rgb_bg_color = aep->rgb_bg_color; | ||||
|   taep->rgb_sp_color = aep->rgb_sp_color; | ||||
|  | ||||
|   return table->ga_len - 1 + ATTR_OFF; | ||||
| } | ||||
| @@ -6696,6 +6721,10 @@ int hl_combine_attr(int char_attr, int prim_attr) | ||||
|     if (spell_aep->rgb_bg_color >= 0) { | ||||
|       new_en.rgb_bg_color = spell_aep->rgb_bg_color; | ||||
|     } | ||||
|  | ||||
|     if (spell_aep->rgb_sp_color >= 0) { | ||||
|       new_en.rgb_sp_color = spell_aep->rgb_sp_color; | ||||
|     } | ||||
|   } | ||||
|   return get_attr_entry(&new_en); | ||||
| } | ||||
| @@ -6733,7 +6762,7 @@ static void highlight_list_one(int id) | ||||
|   didh = highlight_list_arg(id, didh, LIST_STRING, | ||||
|       0, sgp->sg_rgb_bg_name, "guibg"); | ||||
|   didh = highlight_list_arg(id, didh, LIST_STRING, | ||||
|       0, NULL, "guisp"); | ||||
|                             0, sgp->sg_rgb_sp_name, "guisp"); | ||||
|  | ||||
|   if (sgp->sg_link && !got_int) { | ||||
|     (void)syn_list_header(didh, 9999, id); | ||||
| @@ -6847,8 +6876,9 @@ highlight_color ( | ||||
|   if (modec == 'g') { | ||||
|     if (fg) | ||||
|       return HL_TABLE()[id - 1].sg_rgb_fg_name; | ||||
|     if (sp) | ||||
|       return NULL; | ||||
|     if (sp) { | ||||
|       return HL_TABLE()[id - 1].sg_rgb_sp_name; | ||||
|     } | ||||
|     return HL_TABLE()[id - 1].sg_rgb_bg_name; | ||||
|   } | ||||
|   if (font || sp) | ||||
| @@ -6935,10 +6965,12 @@ set_hl_attr ( | ||||
|   // before setting attr_entry->{f,g}g_color to a other than -1 | ||||
|   at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; | ||||
|   at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; | ||||
|   at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; | ||||
|  | ||||
|   if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0 | ||||
|       || at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1 | ||||
|       || at_en.cterm_ae_attr != 0 || at_en.rgb_ae_attr != 0) { | ||||
|       || at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0 | ||||
|       || at_en.rgb_ae_attr != 0) { | ||||
|     sgp->sg_attr = get_attr_entry(&at_en); | ||||
|   } | ||||
| } | ||||
| @@ -7271,6 +7303,10 @@ int highlight_changed(void) | ||||
|         hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg; | ||||
|       } | ||||
|  | ||||
|       if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) { | ||||
|         hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp; | ||||
|       } | ||||
|  | ||||
|       highlight_ga.ga_len = hlcnt + i + 1; | ||||
|       set_hl_attr(hlcnt + i);           /* At long last we can apply */ | ||||
|       highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1); | ||||
|   | ||||
| @@ -69,8 +69,8 @@ struct syn_state { | ||||
|  | ||||
| // Structure shared between syntax.c, screen.c | ||||
| typedef struct attr_entry { | ||||
|   short rgb_ae_attr, cterm_ae_attr;  // HL_BOLD, etc. | ||||
|   RgbValue rgb_fg_color, rgb_bg_color; | ||||
|   int16_t rgb_ae_attr, cterm_ae_attr;  // HL_BOLD, etc. | ||||
|   RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color; | ||||
|   int cterm_fg_color, cterm_bg_color; | ||||
| } attrentry_T; | ||||
|  | ||||
|   | ||||
| @@ -100,6 +100,7 @@ UI *tui_start(void) | ||||
|   ui->visual_bell = tui_visual_bell; | ||||
|   ui->update_fg = tui_update_fg; | ||||
|   ui->update_bg = tui_update_bg; | ||||
|   ui->update_sp = tui_update_sp; | ||||
|   ui->flush = tui_flush; | ||||
|   ui->suspend = tui_suspend; | ||||
|   ui->set_title = tui_set_title; | ||||
| @@ -573,6 +574,11 @@ static void tui_update_bg(UI *ui, int bg) | ||||
|   ((TUIData *)ui->data)->grid.bg = bg; | ||||
| } | ||||
|  | ||||
| static void tui_update_sp(UI *ui, int sp) | ||||
| { | ||||
|   // Do nothing; 'special' color is for GUI only | ||||
| } | ||||
|  | ||||
| static void tui_flush(UI *ui) | ||||
| { | ||||
|   TUIData *data = ui->data; | ||||
|   | ||||
| @@ -21,7 +21,7 @@ struct ugrid { | ||||
|   UCell **cells; | ||||
| }; | ||||
|  | ||||
| #define EMPTY_ATTRS ((HlAttrs){false, false, false, false, false, -1, -1}) | ||||
| #define EMPTY_ATTRS ((HlAttrs){ false, false, false, false, false, -1, -1, -1 }) | ||||
|  | ||||
| #define UGRID_FOREACH_CELL(grid, top, bot, left, right, code)           \ | ||||
|   do {                                                                  \ | ||||
|   | ||||
| @@ -155,6 +155,7 @@ void ui_resize(int new_width, int new_height) | ||||
|  | ||||
|   UI_CALL(update_fg, (ui->rgb ? normal_fg : cterm_normal_fg_color - 1)); | ||||
|   UI_CALL(update_bg, (ui->rgb ? normal_bg : cterm_normal_bg_color - 1)); | ||||
|   UI_CALL(update_sp, (ui->rgb ? normal_sp : -1)); | ||||
|  | ||||
|   sr.top = 0; | ||||
|   sr.bot = height - 1; | ||||
| @@ -388,7 +389,7 @@ static void parse_control_character(uint8_t c) | ||||
|  | ||||
| static void set_highlight_args(int attr_code) | ||||
| { | ||||
|   HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1 }; | ||||
|   HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1, -1 }; | ||||
|   HlAttrs cterm_attrs = rgb_attrs; | ||||
|  | ||||
|   if (attr_code == HL_NORMAL) { | ||||
| @@ -425,6 +426,10 @@ static void set_highlight_args(int attr_code) | ||||
|     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; | ||||
|   } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  | ||||
| typedef struct { | ||||
|   bool bold, underline, undercurl, italic, reverse; | ||||
|   int foreground, background; | ||||
|   int foreground, background, special; | ||||
| } HlAttrs; | ||||
|  | ||||
| typedef struct ui_t UI; | ||||
| @@ -35,6 +35,7 @@ struct ui_t { | ||||
|   void (*flush)(UI *ui); | ||||
|   void (*update_fg)(UI *ui, int fg); | ||||
|   void (*update_bg)(UI *ui, int bg); | ||||
|   void (*update_sp)(UI *ui, int sp); | ||||
|   void (*suspend)(UI *ui); | ||||
|   void (*set_title)(UI *ui, char *title); | ||||
|   void (*set_icon)(UI *ui, char *icon); | ||||
|   | ||||
| @@ -49,6 +49,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) | ||||
|   rv->bridge.visual_bell = ui_bridge_visual_bell; | ||||
|   rv->bridge.update_fg = ui_bridge_update_fg; | ||||
|   rv->bridge.update_bg = ui_bridge_update_bg; | ||||
|   rv->bridge.update_sp = ui_bridge_update_sp; | ||||
|   rv->bridge.flush = ui_bridge_flush; | ||||
|   rv->bridge.suspend = ui_bridge_suspend; | ||||
|   rv->bridge.set_title = ui_bridge_set_title; | ||||
| @@ -305,6 +306,16 @@ static void ui_bridge_update_bg_event(void **argv) | ||||
|   ui->update_bg(ui, PTR2INT(argv[1])); | ||||
| } | ||||
|  | ||||
| static void ui_bridge_update_sp(UI *b, int sp) | ||||
| { | ||||
|   UI_CALL(b, update_sp, 2, b, INT2PTR(sp)); | ||||
| } | ||||
| static void ui_bridge_update_sp_event(void **argv) | ||||
| { | ||||
|   UI *ui = UI(argv[0]); | ||||
|   ui->update_sp(ui, PTR2INT(argv[1])); | ||||
| } | ||||
|  | ||||
| static void ui_bridge_flush(UI *b) | ||||
| { | ||||
|   UI_CALL(b, flush, 1, b); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ describe('terminal window highlighting', function() | ||||
|       [5] = {background = 11}, | ||||
|       [6] = {foreground = 130}, | ||||
|       [7] = {reverse = true}, | ||||
|       [8] = {background = 11} | ||||
|       [8] = {background = 11}, | ||||
|     }) | ||||
|     screen:attach(false) | ||||
|     execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert') | ||||
| @@ -121,7 +121,7 @@ describe('terminal window highlighting with custom palette', function() | ||||
|     clear() | ||||
|     screen = Screen.new(50, 7) | ||||
|     screen:set_default_attr_ids({ | ||||
|       [1] = {foreground = 1193046} | ||||
|       [1] = {foreground = 1193046, special = Screen.colors.Black} | ||||
|     }) | ||||
|     screen:set_default_attr_ignore({ | ||||
|       [1] = {bold = true}, | ||||
| @@ -130,7 +130,7 @@ describe('terminal window highlighting with custom palette', function() | ||||
|       [5] = {background = 11}, | ||||
|       [6] = {foreground = 130}, | ||||
|       [7] = {reverse = true}, | ||||
|       [8] = {background = 11} | ||||
|       [8] = {background = 11}, | ||||
|     }) | ||||
|     screen:attach(true) | ||||
|     nvim('set_var', 'terminal_color_3', '#123456') | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| local helpers = require('test.functional.helpers') | ||||
| local Screen = require('test.functional.ui.screen') | ||||
| local os = require('os') | ||||
| local clear, feed = helpers.clear, helpers.feed | ||||
| local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert | ||||
| local execute, request, eq = helpers.execute, helpers.request, helpers.eq | ||||
|  | ||||
|  | ||||
| @@ -303,3 +303,61 @@ describe('Default highlight groups', function() | ||||
|     ]], {[1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}}) | ||||
|   end) | ||||
| end) | ||||
|  | ||||
| describe('guisp (special/undercurl)', function() | ||||
|   local screen | ||||
|  | ||||
|   before_each(function() | ||||
|     clear() | ||||
|     screen = Screen.new(25,10) | ||||
|     screen:attach() | ||||
|     screen:set_default_attr_ignore({ | ||||
|       [1] = {bold = true, foreground = Screen.colors.Blue}, | ||||
|       [2] = {bold = true} | ||||
|     }) | ||||
|   end) | ||||
|  | ||||
|   it('can be set and is applied like foreground or background', function() | ||||
|     execute('syntax on') | ||||
|     execute('syn keyword TmpKeyword neovim') | ||||
|     execute('syn keyword TmpKeyword1 special') | ||||
|     execute('syn keyword TmpKeyword2 specialwithbg') | ||||
|     execute('syn keyword TmpKeyword3 specialwithfg') | ||||
|     execute('hi! Awesome guifg=red guibg=yellow guisp=red') | ||||
|     execute('hi! Awesome1 guisp=red') | ||||
|     execute('hi! Awesome2 guibg=yellow guisp=red') | ||||
|     execute('hi! Awesome3 guifg=red guisp=red') | ||||
|     execute('hi link TmpKeyword Awesome') | ||||
|     execute('hi link TmpKeyword1 Awesome1') | ||||
|     execute('hi link TmpKeyword2 Awesome2') | ||||
|     execute('hi link TmpKeyword3 Awesome3') | ||||
|     insert([[ | ||||
|       neovim | ||||
|       awesome neovim | ||||
|       wordcontainingneovim | ||||
|       special | ||||
|       specialwithbg | ||||
|       specialwithfg | ||||
|       ]]) | ||||
|     feed('Go<tab>neovim tabbed') | ||||
|     screen:expect([[ | ||||
|       {1:neovim}                   | | ||||
|       awesome {1:neovim}           | | ||||
|       wordcontainingneovim     | | ||||
|       {2:special}                  | | ||||
|       {3:specialwithbg}            | | ||||
|       {4:specialwithfg}            | | ||||
|                                | | ||||
|               {1:neovim} tabbed^    | | ||||
|       ~                        | | ||||
|       -- INSERT --             | | ||||
|     ]],{ | ||||
|       [1] = {background = Screen.colors.Yellow, foreground = Screen.colors.Red, | ||||
|              special = Screen.colors.Red}, | ||||
|       [2] = {special = Screen.colors.Red}, | ||||
|       [3] = {special = Screen.colors.Red, background = Screen.colors.Yellow}, | ||||
|       [4] = {foreground = Screen.colors.Red, special = Screen.colors.Red}, | ||||
|     }) | ||||
|  | ||||
|   end) | ||||
| end) | ||||
|   | ||||
| @@ -425,6 +425,10 @@ function Screen:_handle_update_bg(bg) | ||||
|   self._bg = bg | ||||
| end | ||||
|  | ||||
| function Screen:_handle_update_sp(sp) | ||||
|   self._sp = sp | ||||
| end | ||||
|  | ||||
| function Screen:_handle_suspend() | ||||
|   self.suspended = true | ||||
| end | ||||
| @@ -573,7 +577,7 @@ function Screen:_pprint_attrs(attrs) | ||||
|     local items = {} | ||||
|     for f, v in pairs(attrs) do | ||||
|       local desc = tostring(v) | ||||
|       if f == "foreground" or f == "background" then | ||||
|       if f == "foreground" or f == "background" or f == "special" then | ||||
|         if Screen.colornames[v] ~= nil then | ||||
|           desc = "Screen.colors."..Screen.colornames[v] | ||||
|         end | ||||
| @@ -614,7 +618,8 @@ function Screen:_equal_attrs(a, b) | ||||
|        a.underline == b.underline and a.undercurl == b.undercurl and | ||||
|        a.italic == b.italic and a.reverse == b.reverse and | ||||
|        a.foreground == b.foreground and | ||||
|        a.background == b.background | ||||
|        a.background == b.background and | ||||
|        a.special == b.special | ||||
| end | ||||
|  | ||||
| function Screen:_attr_index(attrs, attr) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes