diff options
| author | Maxime Coste <mawww@kakoune.org> | 2019-04-23 16:44:30 +0100 |
|---|---|---|
| committer | Maxime Coste <mawww@kakoune.org> | 2019-04-23 23:15:23 +0100 |
| commit | 4e24ba86cc95c51f78ebe9e77f1848b6760dbeb5 (patch) | |
| tree | 6b7b855b249800bd40e23c2a9759847ad8194982 /src | |
| parent | 17c5e7aa5fb020186378f7f738e617b5743839fc (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.cc | 107 | ||||
| -rw-r--r-- | src/face_registry.hh | 8 | ||||
| -rw-r--r-- | src/window.cc | 2 |
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; } |
