summaryrefslogtreecommitdiff
path: root/src/option_manager.cc
blob: 04fb3e842b380f765ba4b1107d2c058c31772476 (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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include "option_manager.hh"

#include "assert.hh"

namespace Kakoune
{

OptionDesc::OptionDesc(String name, String docstring, OptionFlags flags)
    : m_name(std::move(name)), m_docstring(std::move(docstring)),
    m_flags(flags) {}

Option::Option(const OptionDesc& desc, OptionManager& manager)
    : m_manager(manager), m_desc(desc) {}

OptionManager::OptionManager(OptionManager& parent)
    : m_parent(&parent)
{
    parent.register_watcher(*this);
}

OptionManager::~OptionManager()
{
    if (m_parent)
        m_parent->unregister_watcher(*this);

    kak_assert(m_watchers.empty());
}

void OptionManager::register_watcher(OptionManagerWatcher& watcher)
{
    kak_assert(not contains(m_watchers, &watcher));
    m_watchers.push_back(&watcher);
}

void OptionManager::unregister_watcher(OptionManagerWatcher& watcher)
{
    auto it = find(m_watchers.begin(), m_watchers.end(), &watcher);
    kak_assert(it != m_watchers.end());
    m_watchers.erase(it);
}

struct option_not_found : public runtime_error
{
    option_not_found(StringView name)
        : runtime_error("option not found: " + name) {}
};

Option& OptionManager::get_local_option(StringView name)
{
    auto it = find_option(m_options, name);
    if (it != m_options.end())
        return **it;
    else if (m_parent)
    {
        m_options.emplace_back((*m_parent)[name].clone(*this));
        return *m_options.back();
    }
    else
        throw option_not_found(name);

}

Option& OptionManager::operator[](StringView name)
{
    auto it = find_option(m_options, name);
    if (it != m_options.end())
        return **it;
    else if (m_parent)
        return (*m_parent)[name];
    else
        throw option_not_found(name);
}

const Option& OptionManager::operator[](StringView name) const
{
    return const_cast<OptionManager&>(*this)[name];
}

void OptionManager::unset_option(StringView name)
{
    kak_assert(m_parent); // cannot unset option on global manager
    auto it = find_option(m_options, name);
    if (it != m_options.end())
    {
        m_options.erase(it);
        on_option_changed((*m_parent)[name]);
    }
}

OptionManager::OptionList OptionManager::flatten_options() const
{
    OptionList res = m_parent ? m_parent->flatten_options() : OptionList{};
    for (auto& option : m_options)
    {
        auto it = find_option(res, option->name());
        if (it != res.end())
            *it = option.get();
        else
            res.emplace_back(option.get());
    }
    return res;
}

void OptionManager::on_option_changed(const Option& option)
{
    // if parent option changed, but we overrided it, it's like nothing happened
    if (&option.manager() != this and
        find_option(m_options, option.name()) != m_options.end())
        return;

    for (auto watcher : m_watchers)
        watcher->on_option_changed(option);
}

template<typename Container, typename MatchingFunc>
static CandidateList get_matching_names(const Container& options, MatchingFunc func)
{
    CandidateList result;
    for (auto& option : options)
    {
        if (option->flags() & OptionFlags::Hidden)
            continue;

        const auto& name = option->name();
        if (func(name))
            result.push_back(name);
    }
    return result;
}

CandidateList OptionsRegistry::complete_option_name(StringView prefix,
                                                    ByteCount cursor_pos) const
{
    using namespace std::placeholders;
    auto real_prefix = prefix.substr(0, cursor_pos);
    auto result = get_matching_names(m_descs, std::bind(prefix_match, _1, real_prefix));
    if (result.empty())
        result = get_matching_names(m_descs, std::bind(subsequence_match, _1, real_prefix));
    return result;
}

}