summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <mawww@kakoune.org>2019-04-23 16:44:30 +0100
committerMaxime Coste <mawww@kakoune.org>2019-04-23 23:15:23 +0100
commit4e24ba86cc95c51f78ebe9e77f1848b6760dbeb5 (patch)
tree6b7b855b249800bd40e23c2a9759847ad8194982 /src
parent17c5e7aa5fb020186378f7f738e617b5743839fc (diff)
Change faces alias to be a base that can be modified
Using <fg>,<bg>+<attr>@<base> will apply the given fg color, bg color and attributes on top of base dynamically. Simply giving <base> is a shorthand for default,default@<base>. Inspired by the discussion in #2862
Diffstat (limited to 'src')
-rw-r--r--src/face_registry.cc107
-rw-r--r--src/face_registry.hh8
-rw-r--r--src/window.cc2
3 files changed, 68 insertions, 49 deletions
diff --git a/src/face_registry.cc b/src/face_registry.cc
index 5a5187b8..9fe318a2 100644
--- a/src/face_registry.cc
+++ b/src/face_registry.cc
@@ -7,43 +7,51 @@
namespace Kakoune
{
-static Face parse_face(StringView facedesc)
+static FaceRegistry::FaceSpec parse_face(StringView facedesc)
{
- constexpr StringView invalid_face_error = "invalid face description, expected <fg>[,<bg>][+<attr>]";
+ constexpr StringView invalid_face_error = "invalid face description, expected <fg>[,<bg>][+<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 attr_it = find(facedesc, '+');
+ auto base_it = find(facedesc, '@');
if (bg_it != facedesc.end()
and (attr_it < bg_it or (bg_it + 1) == facedesc.end()))
throw runtime_error(invalid_face_error.str());
if (attr_it != facedesc.end()
and (attr_it + 1) == facedesc.end())
throw runtime_error(invalid_face_error.str());
- Face res;
- res.fg = attr_it != facedesc.begin() ?
+
+ FaceRegistry::FaceSpec spec;
+ auto& face = spec.face;
+ face.fg = attr_it != facedesc.begin() ?
str_to_color({facedesc.begin(), std::min(attr_it, bg_it)}) : Color::Default;
if (bg_it != facedesc.end())
- res.bg = bg_it+1 != attr_it ? str_to_color({bg_it+1, attr_it}) : Color::Default;
+ face.bg = bg_it+1 != attr_it ? str_to_color({bg_it+1, attr_it}) : Color::Default;
if (attr_it != facedesc.end())
{
- for (++attr_it; attr_it != facedesc.end(); ++attr_it)
+ for (++attr_it; attr_it != base_it; ++attr_it)
{
switch (*attr_it)
{
- case 'u': res.attributes |= Attribute::Underline; break;
- case 'r': res.attributes |= Attribute::Reverse; break;
- case 'b': res.attributes |= Attribute::Bold; break;
- case 'B': res.attributes |= Attribute::Blink; break;
- case 'd': res.attributes |= Attribute::Dim; 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;
+ case 'u': face.attributes |= Attribute::Underline; break;
+ case 'r': face.attributes |= Attribute::Reverse; break;
+ case 'b': face.attributes |= Attribute::Bold; break;
+ case 'B': face.attributes |= Attribute::Blink; break;
+ case 'd': face.attributes |= Attribute::Dim; break;
+ case 'i': face.attributes |= Attribute::Italic; break;
+ case 'f': face.attributes |= Attribute::FinalFg; break;
+ case 'g': face.attributes |= Attribute::FinalBg; break;
+ case 'a': face.attributes |= Attribute::FinalAttr; break;
+ case 'F': face.attributes |= Attribute::Final; break;
default: throw runtime_error(format("no such face attribute: '{}'", StringView{*attr_it}));
}
}
}
- return res;
+ if (base_it != facedesc.end())
+ spec.base = String{base_it+1, facedesc.end()};
+ return spec;
}
String to_string(Attribute attributes)
@@ -79,16 +87,33 @@ String to_string(Face face)
Face FaceRegistry::operator[](StringView facedesc) const
{
- auto it = m_faces.find(facedesc);
- if (it != m_faces.end())
+ return resolve_spec(parse_face(facedesc));
+}
+
+Face FaceRegistry::resolve_spec(const FaceSpec& spec) const
+{
+ if (spec.base.empty())
+ return spec.face;
+
+ StringView base = spec.base;
+ Face face = spec.face;
+ for (auto* reg = this; reg != nullptr; reg = reg->m_parent.get())
{
- if (it->value.alias.empty())
- return it->value.face;
- return operator[](it->value.alias);
+ auto it = reg->m_faces.find(base);
+ if (it == reg->m_faces.end())
+ continue;
+
+ if (it->value.base.empty())
+ return merge_faces(it->value.face, face);
+ if (it->value.base != it->key)
+ return merge_faces(reg->resolve_spec(it->value), face);
+ else
+ {
+ face = merge_faces(it->value.face, face);
+ base = it->value.base;
+ }
}
- if (m_parent)
- return (*m_parent)[facedesc];
- return parse_face(facedesc);
+ return face;
}
void FaceRegistry::add_face(StringView name, StringView facedesc, bool override)
@@ -97,33 +122,25 @@ void FaceRegistry::add_face(StringView name, StringView facedesc, bool override)
throw runtime_error(format("face '{}' already defined", name));
if (name.empty() or is_color_name(name) or
- std::any_of(name.begin(), name.end(),
- [](char c){ return not is_word(c); }))
+ any_of(name, [](char c){ return not is_word(c); }))
throw runtime_error(format("invalid face name: '{}'", name));
- if (name == facedesc)
- throw runtime_error(format("cannot alias face '{}' to itself", name));
-
- for (auto it = m_faces.find(facedesc);
- it != m_faces.end() and not it->value.alias.empty();
- it = m_faces.find(it->value.alias))
+ FaceSpec spec = parse_face(facedesc);
+ auto it = m_faces.find(spec.base);
+ if (spec.base == name and it != m_faces.end())
{
- if (it->value.alias == name)
- throw runtime_error("face cycle detected");
+ it->value.face = merge_faces(it->value.face, spec.face);
+ it->value.base = spec.base;
+ return;
}
- FaceOrAlias& face = m_faces[name];
-
- for (auto* registry = this; registry != nullptr; registry = registry->m_parent.get())
+ while (it != m_faces.end() and not it->value.base.empty())
{
- if (not registry->m_faces.contains(facedesc))
- continue;
- face.alias = facedesc.str(); // This is referencing another face
- return;
+ if (it->value.base == name)
+ throw runtime_error("face cycle detected");
+ it = m_faces.find(it->value.base);
}
-
- face.alias = "";
- face.face = parse_face(facedesc);
+ m_faces[name] = std::move(spec);
}
void FaceRegistry::remove_face(StringView name)
diff --git a/src/face_registry.hh b/src/face_registry.hh
index 1182a9d3..252256cf 100644
--- a/src/face_registry.hh
+++ b/src/face_registry.hh
@@ -20,12 +20,12 @@ public:
void add_face(StringView name, StringView facedesc, bool override = false);
void remove_face(StringView name);
- struct FaceOrAlias
+ struct FaceSpec
{
Face face = {};
- String alias = {};
+ String base = {};
};
- using FaceMap = HashMap<String, FaceOrAlias, MemoryDomain::Faces>;
+ using FaceMap = HashMap<String, FaceSpec, MemoryDomain::Faces>;
auto flatten_faces() const
{
@@ -41,6 +41,8 @@ public:
}
private:
+ Face resolve_spec(const FaceSpec& spec) const;
+
friend class Scope;
FaceRegistry();
diff --git a/src/window.cc b/src/window.cc
index 1b145d66..f44ea92b 100644
--- a/src/window.cc
+++ b/src/window.cc
@@ -78,7 +78,7 @@ static uint32_t compute_faces_hash(const FaceRegistry& faces)
{
uint32_t hash = 0;
for (auto&& face : faces.flatten_faces() | transform(&FaceRegistry::FaceMap::Item::value))
- hash = combine_hash(hash, face.alias.empty() ? hash_value(face.face) : hash_value(face.alias));
+ hash = combine_hash(hash, face.base.empty() ? hash_value(face.face) : hash_value(face.base));
return hash;
}