Replace the Exclusive face attribute with Final

Final is more granular, it consists of FinalFg (f), FinalBg (g)
and FinalAttr (a) which control if a face's fg, bg, or attributes
fully overwrite the previous face (instead of merging) and if
following faces apply on top of this face or not.

Fixes #2388 if the Whitespace face has the FinalFg flag.
This commit is contained in:
Maxime Coste 2018-09-23 23:17:12 +10:00
parent 09546a950e
commit 1631a7d8d9
9 changed files with 63 additions and 24 deletions

View File

@ -71,6 +71,6 @@ evaluate-commands %sh{
face global Prompt ${yellow} face global Prompt ${yellow}
face global MatchingChar default+b face global MatchingChar default+b
face global BufferPadding ${bg2},${bg} face global BufferPadding ${bg2},${bg}
face global Whitespace ${bg2} face global Whitespace ${bg2}+f
" "
} }

View File

@ -14,7 +14,7 @@ Kakoune won't be able to parse named parameters in requests.
Here are the data structures used: Here are the data structures used:
* Color: a string, either a named color, or #rrggbb, or 'default' * Color: a string, either a named color, or #rrggbb, or 'default'
* Attribute: one of {exclusive, underline, reverse, blink, bold, dim, italic} * Attribute: one of {underline, reverse, blink, bold, dim, italic, final_fg, final_bg, final_attr}
* Face { Color fg; Color bg; Array<Attribute> attributes; } * Face { Color fg; Color bg; Array<Attribute> attributes; }
* Atom { Face face; String contents; } * Atom { Face face; String contents; }
* Line : Array of Atom * Line : Array of Atom

View File

@ -8,6 +8,13 @@ released versions.
* `remove-hooks` <group> argument is now a regex and removes all * `remove-hooks` <group> argument is now a regex and removes all
hooks whose group matches it. hooks whose group matches it.
* `exclusive` face attribute (e) has been replaced with more
granular `final foreground` (f), `final background` (g), and `final
attributes` (a), or the three combined as `final` (F). Semantics
changed slightly as those attributes apply to the existing face as
well (a final face will not get modified by a following face if that
following face does not have the final attribute itself.
== Kakoune 2018.09.04 == Kakoune 2018.09.04
This version contains a significant overhaul of various Kakoune This version contains a significant overhaul of various Kakoune

View File

@ -33,9 +33,19 @@ fg_color[,bg_color][+attributes]
dim dim
*i*::: *i*:::
italic italic
*e*::: *F*:::
exclusive, override previous faces instead of merging final, override the previous face instead of merging with it
with them an will only be replaced if another face with the final
attribute is applied
*f*:::
final foreground, as final but only applies to face's
foreground color
*g*:::
final background, as final but only applies to face's
background color
*a*:::
final attributes, as final but only applies to face's
attributes
== Builtin faces == Builtin faces

View File

@ -10,13 +10,16 @@ namespace Kakoune
enum class Attribute : int enum class Attribute : int
{ {
Normal = 0, Normal = 0,
Exclusive = 1 << 1, Underline = 1 << 1,
Underline = 1 << 2, Reverse = 1 << 2,
Reverse = 1 << 3, Blink = 1 << 3,
Blink = 1 << 4, Bold = 1 << 4,
Bold = 1 << 5, Dim = 1 << 5,
Dim = 1 << 6, Italic = 1 << 6,
Italic = 1 << 7, FinalFg = 1 << 7,
FinalBg = 1 << 8,
FinalAttr = 1 << 8,
Final = FinalFg | FinalBg | FinalAttr
}; };
constexpr bool with_bit_ops(Meta::Type<Attribute>) { return true; } constexpr bool with_bit_ops(Meta::Type<Attribute>) { return true; }
@ -49,11 +52,22 @@ constexpr size_t hash_value(const Face& val)
return hash_values(val.fg, val.bg, val.attributes); return hash_values(val.fg, val.bg, val.attributes);
} }
constexpr Face merge_faces(const Face& base, const Face& face) inline Face merge_faces(const Face& base, const Face& face)
{ {
return face.attributes & Attribute::Exclusive ? auto choose = [&](Color Face::*color, Attribute final_attr) {
face : Face{ face.fg == Color::Default ? base.fg : face.fg, if (face.attributes & final_attr)
face.bg == Color::Default ? base.bg : face.bg, return face.*color;
if (base.attributes & final_attr)
return base.*color;
if (face.*color == Color::Default)
return base.*color;
return face.*color;
};
return Face{ choose(&Face::fg, Attribute::FinalFg),
choose(&Face::bg, Attribute::FinalBg),
face.attributes & Attribute::FinalAttr ? face.attributes :
base.attributes & Attribute::FinalAttr ? base.attributes :
face.attributes | base.attributes }; face.attributes | base.attributes };
} }

View File

@ -29,13 +29,16 @@ static Face parse_face(StringView facedesc)
{ {
switch (*attr_it) switch (*attr_it)
{ {
case 'e': res.attributes |= Attribute::Exclusive; break;
case 'u': res.attributes |= Attribute::Underline; break; case 'u': res.attributes |= Attribute::Underline; break;
case 'r': res.attributes |= Attribute::Reverse; break; case 'r': res.attributes |= Attribute::Reverse; break;
case 'b': res.attributes |= Attribute::Bold; break; case 'b': res.attributes |= Attribute::Bold; break;
case 'B': res.attributes |= Attribute::Blink; break; case 'B': res.attributes |= Attribute::Blink; break;
case 'd': res.attributes |= Attribute::Dim; break; case 'd': res.attributes |= Attribute::Dim; break;
case 'i': res.attributes |= Attribute::Italic; break; case 'i': res.attributes |= Attribute::Italic; break;
case 'f': res.attributes |= Attribute::FinalFg; break;
case 'g': res.attributes |= Attribute::FinalBg; break;
case 'a': res.attributes |= Attribute::FinalAttr; break;
case 'F': res.attributes |= Attribute::Final; break;
default: throw runtime_error(format("no such face attribute: '{}'", StringView{*attr_it})); default: throw runtime_error(format("no such face attribute: '{}'", StringView{*attr_it}));
} }
} }
@ -50,13 +53,16 @@ String to_string(Attribute attributes)
struct Attr { Attribute attr; StringView name; } struct Attr { Attribute attr; StringView name; }
attrs[] { attrs[] {
{ Attribute::Exclusive, "e" },
{ Attribute::Underline, "u" }, { Attribute::Underline, "u" },
{ Attribute::Reverse, "r" }, { Attribute::Reverse, "r" },
{ Attribute::Blink, "B" }, { Attribute::Blink, "B" },
{ Attribute::Bold, "b" }, { Attribute::Bold, "b" },
{ Attribute::Dim, "d" }, { Attribute::Dim, "d" },
{ Attribute::Italic, "i" }, { Attribute::Italic, "i" },
{ Attribute::Final, "F" },
{ Attribute::FinalFg, "f" },
{ Attribute::FinalBg, "b" },
{ Attribute::FinalAttr, "a" },
}; };
auto filteredAttrs = attrs | auto filteredAttrs = attrs |
@ -150,7 +156,7 @@ FaceRegistry::FaceRegistry()
{ "Prompt", {Face{ Color::Yellow, Color::Default }} }, { "Prompt", {Face{ Color::Yellow, Color::Default }} },
{ "MatchingChar", {Face{ Color::Default, Color::Default, Attribute::Bold }} }, { "MatchingChar", {Face{ Color::Default, Color::Default, Attribute::Bold }} },
{ "BufferPadding", {Face{ Color::Blue, Color::Default }} }, { "BufferPadding", {Face{ Color::Blue, Color::Default }} },
{ "Whitespace", {Face{ Color::Default, Color::Default }} }, { "Whitespace", {Face{ Color::Default, Color::Default, Attribute::FinalFg }} },
} }
{} {}

View File

@ -81,13 +81,15 @@ String to_json(Attribute attributes)
{ {
struct Attr { Attribute attr; StringView name; } struct Attr { Attribute attr; StringView name; }
attrs[] { attrs[] {
{ Attribute::Exclusive, "exclusive" },
{ Attribute::Underline, "underline" }, { Attribute::Underline, "underline" },
{ Attribute::Reverse, "reverse" }, { Attribute::Reverse, "reverse" },
{ Attribute::Blink, "blink" }, { Attribute::Blink, "blink" },
{ Attribute::Bold, "bold" }, { Attribute::Bold, "bold" },
{ Attribute::Dim, "dim" }, { Attribute::Dim, "dim" },
{ Attribute::Italic, "italic" }, { Attribute::Italic, "italic" },
{ Attribute::FinalFg, "final_fg" },
{ Attribute::FinalBg, "final_bg" },
{ Attribute::FinalAttr, "final_attr" },
}; };
return "[" + join(attrs | return "[" + join(attrs |

View File

@ -1,11 +1,11 @@
{ "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] } { "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] }
{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "·" }, { "face": { "fg": "black", "bg": "cyan", "attributes": [] }, "contents": "¬" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "¬" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] } { "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": ["final_fg"] }, "contents": "·" }, { "face": { "fg": "default", "bg": "cyan", "attributes": ["final_fg"] }, "contents": "¬" }], [{ "face": { "fg": "default", "bg": "default", "attributes": ["final_fg"] }, "contents": "¬" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] } { "jsonrpc": "2.0", "method": "menu_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "info_hide", "params": [] } { "jsonrpc": "2.0", "method": "info_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:2 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[+]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] } { "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:2 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[+]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 0, "column": 1 }] } { "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 0, "column": 1 }] }
{ "jsonrpc": "2.0", "method": "refresh", "params": [true] } { "jsonrpc": "2.0", "method": "refresh", "params": [true] }
{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "·" }, { "face": { "fg": "black", "bg": "cyan", "attributes": [] }, "contents": "¬" }], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "¬" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] } { "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": ["final_fg"] }, "contents": "·" }, { "face": { "fg": "default", "bg": "cyan", "attributes": ["final_fg"] }, "contents": "¬" }], [{ "face": { "fg": "default", "bg": "default", "attributes": ["final_fg"] }, "contents": "¬" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] } { "jsonrpc": "2.0", "method": "menu_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "info_hide", "params": [] } { "jsonrpc": "2.0", "method": "info_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:2 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[+]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] } { "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:2 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[+]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }

View File

@ -1,5 +1,5 @@
{ "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] } { "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] }
{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "this" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "→ " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "is" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "→ " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "a" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "→ " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "test" }, { "face": { "fg": "black", "bg": "cyan", "attributes": [] }, "contents": "¬" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] } { "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "this" }, { "face": { "fg": "default", "bg": "default", "attributes": ["final_fg"] }, "contents": "→ " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "is" }, { "face": { "fg": "default", "bg": "default", "attributes": ["final_fg"] }, "contents": "→ " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "a" }, { "face": { "fg": "default", "bg": "default", "attributes": ["final_fg"] }, "contents": "→ " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "test" }, { "face": { "fg": "default", "bg": "cyan", "attributes": ["final_fg"] }, "contents": "¬" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] } { "jsonrpc": "2.0", "method": "menu_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "info_hide", "params": [] } { "jsonrpc": "2.0", "method": "info_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:15 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[+]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] } { "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:15 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[+]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }