summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <mawww@kakoune.org>2021-09-07 08:21:26 +1000
committerMaxime Coste <mawww@kakoune.org>2021-09-07 08:21:26 +1000
commit3fc8e29d101b4f6eef2538cdbe799bab9859f4b3 (patch)
tree27d931e4bed0fb12c8d018f7302c89f634d81350 /src
parenta6644d3034eba2db1db6cd3832488897ffb78921 (diff)
Add support for curly underline and separate underline color
Add support for a third color in face definition that controls the underline and a 'c' attribute for curly underline (that takes precedence over 'u' if both are specified) Allow empty colors to mean default, so that `,,red+u` means the same as `default,default,red+u` Fixes #4138
Diffstat (limited to 'src')
-rw-r--r--src/commands.cc4
-rw-r--r--src/face.hh32
-rw-r--r--src/face_registry.cc20
-rw-r--r--src/json_ui.cc4
-rw-r--r--src/terminal_ui.cc20
5 files changed, 56 insertions, 24 deletions
diff --git a/src/commands.cc b/src/commands.cc
index e6fe6c92..1f5dcf7e 100644
--- a/src/commands.cc
+++ b/src/commands.cc
@@ -2384,8 +2384,8 @@ const CommandDesc set_face_cmd = {
"facespec format is <fg color>[,<bg color>][+<attributes>][@<base>]\n"
"colors are either a color name, rgb:######, or rgba:######## values.\n"
"attributes is a combination of:\n"
- " u: underline, i: italic, b: bold, r: reverse,\n"
- " s: strikethrough, B: blink, d: dim,\n"
+ " u: underline, c: curly underline, i: italic, b: bold,\n"
+ " r: reverse, s: strikethrough, B: blink, d: dim,\n"
" f: final foreground, g: final background,\n"
" a: final attributes, F: same as +fga\n"
"facespec can as well just be the name of another face.\n"
diff --git a/src/face.hh b/src/face.hh
index 798cd3f3..ce544116 100644
--- a/src/face.hh
+++ b/src/face.hh
@@ -9,18 +9,19 @@ namespace Kakoune
enum class Attribute : int
{
- Normal = 0,
- Underline = 1 << 1,
- Reverse = 1 << 2,
- Blink = 1 << 3,
- Bold = 1 << 4,
- Dim = 1 << 5,
- Italic = 1 << 6,
- Strikethrough = 1 << 7,
- FinalFg = 1 << 8,
- FinalBg = 1 << 9,
- FinalAttr = 1 << 10,
- Final = FinalFg | FinalBg | FinalAttr
+ Normal = 0,
+ Underline = 1 << 1,
+ CurlyUnderline = 1 << 2,
+ Reverse = 1 << 3,
+ Blink = 1 << 4,
+ Bold = 1 << 5,
+ Dim = 1 << 6,
+ Italic = 1 << 7,
+ Strikethrough = 1 << 8,
+ FinalFg = 1 << 9,
+ FinalBg = 1 << 10,
+ FinalAttr = 1 << 11,
+ Final = FinalFg | FinalBg | FinalAttr
};
constexpr bool with_bit_ops(Meta::Type<Attribute>) { return true; }
@@ -30,11 +31,13 @@ struct Face
Color fg = Color::Default;
Color bg = Color::Default;
Attribute attributes = Attribute::Normal;
+ Color underline = Color::Default;
friend constexpr bool operator==(const Face& lhs, const Face& rhs)
{
return lhs.fg == rhs.fg and
lhs.bg == rhs.bg and
+ lhs.underline == rhs.underline and
lhs.attributes == rhs.attributes;
}
@@ -45,7 +48,7 @@ struct Face
friend 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.underline, val.attributes);
}
};
@@ -77,7 +80,8 @@ inline Face merge_faces(const Face& base, const Face& face)
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,
+ choose(&Face::underline, Attribute{0}) };
}
}
diff --git a/src/face_registry.cc b/src/face_registry.cc
index 638059ac..87d638e7 100644
--- a/src/face_registry.cc
+++ b/src/face_registry.cc
@@ -9,11 +9,12 @@ namespace Kakoune
static FaceRegistry::FaceSpec parse_face(StringView facedesc)
{
- constexpr StringView invalid_face_error = "invalid face description, expected [<fg>][,<bg>][+<attr>][@base] or just [base]";
+ constexpr StringView invalid_face_error = "invalid face description, expected [<fg>][,<bg>[,<underline>]][+<attr>][@base] or just [base]";
if (all_of(facedesc, [](char c){ return is_word(c); }) and not is_color_name(facedesc))
return {Face{}, facedesc.str()};
auto bg_it = find(facedesc, ',');
+ auto underline_it = bg_it == facedesc.end() ? bg_it : std::find(bg_it+1, facedesc.end(), ',');
auto attr_it = find(facedesc, '+');
auto base_it = find(facedesc, '@');
if (bg_it != facedesc.end()
@@ -25,12 +26,19 @@ static FaceRegistry::FaceSpec parse_face(StringView facedesc)
auto colors_end = std::min(attr_it, base_it);
+ auto parse_color = [](StringView spec) {
+ return spec.empty() ? Color::Default : str_to_color(spec);
+ };
+
FaceRegistry::FaceSpec spec;
auto& face = spec.face;
- face.fg = colors_end != facedesc.begin() ?
- str_to_color({facedesc.begin(), std::min(bg_it, colors_end)}) : Color::Default;
+ face.fg = parse_color({facedesc.begin(), std::min(bg_it, colors_end)});
if (bg_it != facedesc.end())
- face.bg = bg_it+1 != attr_it ? str_to_color({bg_it+1, colors_end}) : Color::Default;
+ {
+ face.bg = parse_color({bg_it+1, std::min(underline_it, colors_end)});
+ if (underline_it != facedesc.end())
+ face.underline = parse_color({underline_it+1, colors_end});
+ }
if (attr_it != facedesc.end())
{
for (++attr_it; attr_it != base_it; ++attr_it)
@@ -38,6 +46,7 @@ static FaceRegistry::FaceSpec parse_face(StringView facedesc)
switch (*attr_it)
{
case 'u': face.attributes |= Attribute::Underline; break;
+ case 'c': face.attributes |= Attribute::CurlyUnderline; break;
case 'r': face.attributes |= Attribute::Reverse; break;
case 'b': face.attributes |= Attribute::Bold; break;
case 'B': face.attributes |= Attribute::Blink; break;
@@ -65,6 +74,7 @@ String to_string(Attribute attributes)
struct Attr { Attribute attr; StringView name; }
attrs[] {
{ Attribute::Underline, "u" },
+ { Attribute::CurlyUnderline, "c" },
{ Attribute::Reverse, "r" },
{ Attribute::Blink, "B" },
{ Attribute::Bold, "b" },
@@ -90,7 +100,7 @@ String to_string(Attribute attributes)
String to_string(Face face)
{
- return format("{},{}{}", face.fg, face.bg, face.attributes);
+ return format("{},{},{}{}", face.fg, face.bg, face.underline, face.attributes);
}
Face FaceRegistry::operator[](StringView facedesc) const
diff --git a/src/json_ui.cc b/src/json_ui.cc
index 36c547ef..f5ae1f1f 100644
--- a/src/json_ui.cc
+++ b/src/json_ui.cc
@@ -57,8 +57,8 @@ String to_json(Attribute attributes)
String to_json(Face face)
{
- return format(R"(\{ "fg": {}, "bg": {}, "attributes": {} })",
- to_json(face.fg), to_json(face.bg), to_json(face.attributes));
+ return format(R"(\{ "fg": {}, "bg": {}, "underline": {}, "attributes": {} })",
+ to_json(face.fg), to_json(face.bg), to_json(face.underline), to_json(face.attributes));
}
String to_json(const DisplayAtom& atom)
diff --git a/src/terminal_ui.cc b/src/terminal_ui.cc
index 58d786c9..67b21e7f 100644
--- a/src/terminal_ui.cc
+++ b/src/terminal_ui.cc
@@ -223,7 +223,8 @@ void TerminalUI::Screen::set_face(const Face& face, Writer& writer)
{
static constexpr int fg_table[]{ 39, 30, 31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97 };
static constexpr int bg_table[]{ 49, 40, 41, 42, 43, 44, 45, 46, 47, 100, 101, 102, 103, 104, 105, 106, 107 };
- static constexpr int attr_table[]{ 0, 4, 7, 5, 1, 2, 3, 9 };
+ static constexpr int ul_table[]{ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ static constexpr const char* attr_table[]{ "0", "4", "4:3", "7", "5", "1", "2", "3", "9" };
auto set_color = [&](bool fg, const Color& color, bool join) {
if (join)
@@ -255,7 +256,24 @@ void TerminalUI::Screen::set_face(const Face& face, Writer& writer)
join = true;
}
if (m_active_face.bg != face.bg)
+ {
set_color(false, face.bg, join);
+ join = true;
+ }
+ if (m_active_face.underline != face.underline)
+ {
+ if (join)
+ writer.write(";");
+ if (face.underline != Color::Default)
+ {
+ if (face.underline.isRGB())
+ format_with(writer, "58:2::{}:{}:{}", face.underline.r, face.underline.g, face.underline.b);
+ else
+ format_with(writer, "58:5:{}", ul_table[(int)(char)face.underline.color]);
+ }
+ else
+ format_with(writer, "59");
+ }
writer.write("m");
m_active_face = face;