summaryrefslogtreecommitdiff
path: root/src/face_registry.cc
blob: 93e9c9f097dad62838413934688ab58bfe8034ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include "face_registry.hh"

#include "exception.hh"

namespace Kakoune
{

static Face parse_face(StringView facedesc)
{
    auto bg_it = std::find(facedesc.begin(), facedesc.end(), ',');
    auto attr_it = std::find(facedesc.begin(), facedesc.end(), '+');
    if (bg_it != facedesc.end() and attr_it < bg_it)
        throw runtime_error("invalid face description, expected <fg>[,<bg>][+<attr>]");
    Face res;
    res.fg = str_to_color({facedesc.begin(), std::min(attr_it, bg_it)});
    if (bg_it != facedesc.end())
        res.bg = str_to_color({bg_it+1, attr_it});
    if (attr_it != facedesc.end())
    {
        for (++attr_it; attr_it != facedesc.end(); ++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;
                default: throw runtime_error("unknown face attribute '" + String(*attr_it) + "'");
            }
        }
    }
    return res;
}

Face FaceRegistry::operator[](const String& facedesc)
{
    auto it = m_aliases.find(facedesc);
    while (it != m_aliases.end())
    {
        if (it->second.alias.empty())
            return it->second.face;
        it = m_aliases.find(it->second.alias);
    }
    return parse_face(facedesc);
}

void FaceRegistry::register_alias(const String& name, const String& facedesc,
                                  bool override)
{
    if (not override and m_aliases.find(name) != m_aliases.end())
        throw runtime_error("alias '" + name + "' already defined");

    if (name.empty() or is_color_name(name) or
        std::any_of(name.begin(), name.end(),
                    [](char c){ return not isalnum(c); }))
        throw runtime_error("invalid alias name");

    FaceOrAlias& alias = m_aliases[name];
    auto it = m_aliases.find(facedesc);
    if (it != m_aliases.end())
    {
        while (it != m_aliases.end())
        {
            if (it->second.alias.empty())
                break;
            if (it->second.alias == name)
                throw runtime_error("face cycle detected");
            it = m_aliases.find(it->second.alias);
        }

        alias.alias = facedesc;
    }
    else
    {
        alias.alias = "";
        alias.face = parse_face(facedesc);
    }
}

CandidateList FaceRegistry::complete_alias_name(StringView prefix,
                                                 ByteCount cursor_pos) const
{
    CandidateList res;
    auto real_prefix = prefix.substr(0, cursor_pos);
    for (auto& alias : m_aliases)
    {
        if (prefix_match(alias.first, real_prefix))
            res.push_back(alias.first);
    }
    return res;
}

FaceRegistry::FaceRegistry()
    : m_aliases{
        { "PrimarySelection", Face{ Colors::Cyan, Colors::Blue } },
        { "SecondarySelection", Face{ Colors::Black, Colors::Blue } },
        { "PrimaryCursor", Face{ Colors::Black, Colors::White } },
        { "SecondaryCursor", Face{ Colors::Black, Colors::White } },
        { "LineNumbers", Face{ Colors::Default, Colors::Default } },
        { "MenuForeground", Face{ Colors::White, Colors::Blue } },
        { "MenuBackground", Face{ Colors::Blue, Colors::White } },
        { "Information", Face{ Colors::Black, Colors::Yellow } },
        { "Error", Face{ Colors::Black, Colors::Red } },
        { "StatusLine", Face{ Colors::Cyan, Colors::Default } },
        { "StatusCursor", Face{ Colors::Black, Colors::Cyan } },
        { "Prompt", Face{ Colors::Yellow, Colors::Default } },
        { "MatchingChar", Face{ Colors::Default, Colors::Default, Attribute::Underline } },
        { "Search", Face{ Colors::Default, Colors::Default, Attribute::Underline } },
      }
{}

}