Merge #4633: support "special" highlight (undercurl)

Closes #2040
Closes #3370
This commit is contained in:
Justin M. Keyes 2016-05-19 22:16:59 -04:00
commit 74f6460181
12 changed files with 161 additions and 26 deletions

View File

@ -504,6 +504,7 @@ EXTERN int cterm_normal_fg_bold INIT(= 0);
EXTERN int cterm_normal_bg_color INIT(= 0); EXTERN int cterm_normal_bg_color INIT(= 0);
EXTERN RgbValue normal_fg INIT(= -1); EXTERN RgbValue normal_fg INIT(= -1);
EXTERN RgbValue normal_bg 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_busy INIT(= FALSE); /* Is apply_autocmds() busy? */
EXTERN int autocmd_no_enter INIT(= FALSE); /* *Enter autocmds disabled */ EXTERN int autocmd_no_enter INIT(= FALSE); /* *Enter autocmds disabled */

View File

@ -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->visual_bell = remote_ui_visual_bell;
ui->update_fg = remote_ui_update_fg; ui->update_fg = remote_ui_update_fg;
ui->update_bg = remote_ui_update_bg; ui->update_bg = remote_ui_update_bg;
ui->update_sp = remote_ui_update_sp;
ui->flush = remote_ui_flush; ui->flush = remote_ui_flush;
ui->suspend = remote_ui_suspend; ui->suspend = remote_ui_suspend;
ui->set_title = remote_ui_set_title; 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)); PUT(hl, "background", INTEGER_OBJ(attrs.background));
} }
if (attrs.special != -1) {
PUT(hl, "special", INTEGER_OBJ(attrs.special));
}
ADD(args, DICTIONARY_OBJ(hl)); ADD(args, DICTIONARY_OBJ(hl));
push_call(ui, "highlight_set", args); 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); 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) static void remote_ui_flush(UI *ui)
{ {
UIData *data = ui->data; UIData *data = ui->data;

View File

@ -62,8 +62,10 @@ struct hl_group {
int sg_gui; // "gui=" highlighting attributes int sg_gui; // "gui=" highlighting attributes
RgbValue sg_rgb_fg; // RGB foreground color RgbValue sg_rgb_fg; // RGB foreground color
RgbValue sg_rgb_bg; // RGB background 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_fg_name; // RGB foreground color name
uint8_t *sg_rgb_bg_name; // RGB background 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 #define SG_CTERM 2 // cterm has been set
@ -6169,12 +6171,11 @@ do_highlight (
break; break;
} }
/* // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg",
* Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or // "guibg" or "guisp").
* "guibg"). while (*linep && !ascii_iswhite(*linep) && *linep != '=') {
*/ linep++;
while (*linep && !ascii_iswhite(*linep) && *linep != '=') }
++linep;
xfree(key); xfree(key);
key = vim_strnsave_up(key_start, (int)(linep - key_start)); key = vim_strnsave_up(key_start, (int)(linep - key_start));
linep = skipwhite(linep); linep = skipwhite(linep);
@ -6452,7 +6453,23 @@ do_highlight (
normal_bg = HL_TABLE()[idx].sg_rgb_bg; normal_bg = HL_TABLE()[idx].sg_rgb_bg;
} }
} else if (STRCMP(key, "GUISP") == 0) { } 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) { } else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) {
// Ignored for now // Ignored for now
} else { } else {
@ -6516,6 +6533,7 @@ void restore_cterm_colors(void)
{ {
normal_fg = -1; normal_fg = -1;
normal_bg = -1; normal_bg = -1;
normal_sp = -1;
cterm_normal_fg_color = 0; cterm_normal_fg_color = 0;
cterm_normal_fg_bold = 0; cterm_normal_fg_bold = 0;
cterm_normal_bg_color = 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_cterm_bg != 0
|| HL_TABLE()[idx].sg_rgb_fg_name != NULL || HL_TABLE()[idx].sg_rgb_fg_name != NULL
|| HL_TABLE()[idx].sg_rgb_bg_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)); || (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_gui = 0;
HL_TABLE()[idx].sg_rgb_fg = -1; HL_TABLE()[idx].sg_rgb_fg = -1;
HL_TABLE()[idx].sg_rgb_bg = -1; HL_TABLE()[idx].sg_rgb_bg = -1;
HL_TABLE()[idx].sg_rgb_sp = -1;
xfree(HL_TABLE()[idx].sg_rgb_fg_name); xfree(HL_TABLE()[idx].sg_rgb_fg_name);
HL_TABLE()[idx].sg_rgb_fg_name = NULL; HL_TABLE()[idx].sg_rgb_fg_name = NULL;
xfree(HL_TABLE()[idx].sg_rgb_bg_name); xfree(HL_TABLE()[idx].sg_rgb_bg_name);
HL_TABLE()[idx].sg_rgb_bg_name = NULL; HL_TABLE()[idx].sg_rgb_bg_name = NULL;
/* Clear the script ID only when there is no link, since that is not xfree(HL_TABLE()[idx].sg_rgb_sp_name);
* cleared. */ HL_TABLE()[idx].sg_rgb_sp_name = NULL;
if (HL_TABLE()[idx].sg_link == 0) // 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; 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->cterm_bg_color == taep->cterm_bg_color
&& aep->rgb_ae_attr == taep->rgb_ae_attr && aep->rgb_ae_attr == taep->rgb_ae_attr
&& aep->rgb_fg_color == taep->rgb_fg_color && 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; 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_ae_attr = aep->rgb_ae_attr;
taep->rgb_fg_color = aep->rgb_fg_color; taep->rgb_fg_color = aep->rgb_fg_color;
taep->rgb_bg_color = aep->rgb_bg_color; taep->rgb_bg_color = aep->rgb_bg_color;
taep->rgb_sp_color = aep->rgb_sp_color;
return table->ga_len - 1 + ATTR_OFF; 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) { if (spell_aep->rgb_bg_color >= 0) {
new_en.rgb_bg_color = spell_aep->rgb_bg_color; 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); 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, didh = highlight_list_arg(id, didh, LIST_STRING,
0, sgp->sg_rgb_bg_name, "guibg"); 0, sgp->sg_rgb_bg_name, "guibg");
didh = highlight_list_arg(id, didh, LIST_STRING, didh = highlight_list_arg(id, didh, LIST_STRING,
0, NULL, "guisp"); 0, sgp->sg_rgb_sp_name, "guisp");
if (sgp->sg_link && !got_int) { if (sgp->sg_link && !got_int) {
(void)syn_list_header(didh, 9999, id); (void)syn_list_header(didh, 9999, id);
@ -6847,8 +6876,9 @@ highlight_color (
if (modec == 'g') { if (modec == 'g') {
if (fg) if (fg)
return HL_TABLE()[id - 1].sg_rgb_fg_name; return HL_TABLE()[id - 1].sg_rgb_fg_name;
if (sp) if (sp) {
return NULL; return HL_TABLE()[id - 1].sg_rgb_sp_name;
}
return HL_TABLE()[id - 1].sg_rgb_bg_name; return HL_TABLE()[id - 1].sg_rgb_bg_name;
} }
if (font || sp) if (font || sp)
@ -6935,10 +6965,12 @@ set_hl_attr (
// before setting attr_entry->{f,g}g_color to a other than -1 // 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_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_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 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.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); 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; 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; highlight_ga.ga_len = hlcnt + i + 1;
set_hl_attr(hlcnt + i); /* At long last we can apply */ set_hl_attr(hlcnt + i); /* At long last we can apply */
highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1); highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);

View File

@ -69,8 +69,8 @@ struct syn_state {
// Structure shared between syntax.c, screen.c // Structure shared between syntax.c, screen.c
typedef struct attr_entry { typedef struct attr_entry {
short rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc. int16_t rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc.
RgbValue rgb_fg_color, rgb_bg_color; RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color;
int cterm_fg_color, cterm_bg_color; int cterm_fg_color, cterm_bg_color;
} attrentry_T; } attrentry_T;

View File

@ -100,6 +100,7 @@ UI *tui_start(void)
ui->visual_bell = tui_visual_bell; ui->visual_bell = tui_visual_bell;
ui->update_fg = tui_update_fg; ui->update_fg = tui_update_fg;
ui->update_bg = tui_update_bg; ui->update_bg = tui_update_bg;
ui->update_sp = tui_update_sp;
ui->flush = tui_flush; ui->flush = tui_flush;
ui->suspend = tui_suspend; ui->suspend = tui_suspend;
ui->set_title = tui_set_title; 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; ((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) static void tui_flush(UI *ui)
{ {
TUIData *data = ui->data; TUIData *data = ui->data;

View File

@ -21,7 +21,7 @@ struct ugrid {
UCell **cells; 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) \ #define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \
do { \ do { \

View File

@ -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_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_bg, (ui->rgb ? normal_bg : cterm_normal_bg_color - 1));
UI_CALL(update_sp, (ui->rgb ? normal_sp : -1));
sr.top = 0; sr.top = 0;
sr.bot = height - 1; sr.bot = height - 1;
@ -388,7 +389,7 @@ static void parse_control_character(uint8_t c)
static void set_highlight_args(int attr_code) 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; HlAttrs cterm_attrs = rgb_attrs;
if (attr_code == HL_NORMAL) { if (attr_code == HL_NORMAL) {
@ -425,6 +426,10 @@ static void set_highlight_args(int attr_code)
rgb_attrs.background = aep->rgb_bg_color; 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) { if (cterm_normal_fg_color != aep->cterm_fg_color) {
cterm_attrs.foreground = aep->cterm_fg_color - 1; cterm_attrs.foreground = aep->cterm_fg_color - 1;
} }

View File

@ -7,7 +7,7 @@
typedef struct { typedef struct {
bool bold, underline, undercurl, italic, reverse; bool bold, underline, undercurl, italic, reverse;
int foreground, background; int foreground, background, special;
} HlAttrs; } HlAttrs;
typedef struct ui_t UI; typedef struct ui_t UI;
@ -35,6 +35,7 @@ struct ui_t {
void (*flush)(UI *ui); void (*flush)(UI *ui);
void (*update_fg)(UI *ui, int fg); void (*update_fg)(UI *ui, int fg);
void (*update_bg)(UI *ui, int bg); void (*update_bg)(UI *ui, int bg);
void (*update_sp)(UI *ui, int sp);
void (*suspend)(UI *ui); void (*suspend)(UI *ui);
void (*set_title)(UI *ui, char *title); void (*set_title)(UI *ui, char *title);
void (*set_icon)(UI *ui, char *icon); void (*set_icon)(UI *ui, char *icon);

View File

@ -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.visual_bell = ui_bridge_visual_bell;
rv->bridge.update_fg = ui_bridge_update_fg; rv->bridge.update_fg = ui_bridge_update_fg;
rv->bridge.update_bg = ui_bridge_update_bg; rv->bridge.update_bg = ui_bridge_update_bg;
rv->bridge.update_sp = ui_bridge_update_sp;
rv->bridge.flush = ui_bridge_flush; rv->bridge.flush = ui_bridge_flush;
rv->bridge.suspend = ui_bridge_suspend; rv->bridge.suspend = ui_bridge_suspend;
rv->bridge.set_title = ui_bridge_set_title; 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])); 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) static void ui_bridge_flush(UI *b)
{ {
UI_CALL(b, flush, 1, b); UI_CALL(b, flush, 1, b);

View File

@ -25,7 +25,7 @@ describe('terminal window highlighting', function()
[5] = {background = 11}, [5] = {background = 11},
[6] = {foreground = 130}, [6] = {foreground = 130},
[7] = {reverse = true}, [7] = {reverse = true},
[8] = {background = 11} [8] = {background = 11},
}) })
screen:attach(false) screen:attach(false)
execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert') execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert')
@ -121,7 +121,7 @@ describe('terminal window highlighting with custom palette', function()
clear() clear()
screen = Screen.new(50, 7) screen = Screen.new(50, 7)
screen:set_default_attr_ids({ screen:set_default_attr_ids({
[1] = {foreground = 1193046} [1] = {foreground = 1193046, special = Screen.colors.Black}
}) })
screen:set_default_attr_ignore({ screen:set_default_attr_ignore({
[1] = {bold = true}, [1] = {bold = true},
@ -130,7 +130,7 @@ describe('terminal window highlighting with custom palette', function()
[5] = {background = 11}, [5] = {background = 11},
[6] = {foreground = 130}, [6] = {foreground = 130},
[7] = {reverse = true}, [7] = {reverse = true},
[8] = {background = 11} [8] = {background = 11},
}) })
screen:attach(true) screen:attach(true)
nvim('set_var', 'terminal_color_3', '#123456') nvim('set_var', 'terminal_color_3', '#123456')

View File

@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers') local helpers = require('test.functional.helpers')
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local os = require('os') 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 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}}) ]], {[1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}})
end) end)
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)

View File

@ -425,6 +425,10 @@ function Screen:_handle_update_bg(bg)
self._bg = bg self._bg = bg
end end
function Screen:_handle_update_sp(sp)
self._sp = sp
end
function Screen:_handle_suspend() function Screen:_handle_suspend()
self.suspended = true self.suspended = true
end end
@ -573,7 +577,7 @@ function Screen:_pprint_attrs(attrs)
local items = {} local items = {}
for f, v in pairs(attrs) do for f, v in pairs(attrs) do
local desc = tostring(v) 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 if Screen.colornames[v] ~= nil then
desc = "Screen.colors."..Screen.colornames[v] desc = "Screen.colors."..Screen.colornames[v]
end end
@ -614,7 +618,8 @@ function Screen:_equal_attrs(a, b)
a.underline == b.underline and a.undercurl == b.undercurl and a.underline == b.underline and a.undercurl == b.undercurl and
a.italic == b.italic and a.reverse == b.reverse and a.italic == b.italic and a.reverse == b.reverse and
a.foreground == b.foreground and a.foreground == b.foreground and
a.background == b.background a.background == b.background and
a.special == b.special
end end
function Screen:_attr_index(attrs, attr) function Screen:_attr_index(attrs, attr)