kakoune/src/option_manager.cc
Maxime Coste 7ceb768a2e Use an HashMap to store options in option manager
Turns out looking for options can get pretty slow, so O(1) lookup
seems better.

This should improve the performance of the #1460 issue
2017-06-23 09:54:21 +01:00

130 lines
3.5 KiB
C++

#include "option_manager.hh"
#include "assert.hh"
#include "flags.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) const
{
kak_assert(not contains(m_watchers, &watcher));
m_watchers.push_back(&watcher);
}
void OptionManager::unregister_watcher(OptionManagerWatcher& watcher) const
{
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 = m_options.find(name);
if (it != m_options.end())
return *(it->value);
else if (m_parent)
{
auto* clone = (*m_parent)[name].clone(*this);
return *m_options.insert({clone->name(), std::unique_ptr<Option>{clone}});
}
else
throw option_not_found(name);
}
Option& OptionManager::operator[](StringView name)
{
auto it = m_options.find(name);
if (it != m_options.end())
return *it->value;
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
if (m_options.contains(name))
{
m_options.erase(name);
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_if(res, [&](const Option* opt) { return opt->name() == option.key; });
if (it != res.end())
*it = option.value.get();
else
res.emplace_back(option.value.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 m_options.contains(option.name()))
return;
// The watcher list might get mutated during calls to on_option_changed
auto watchers = m_watchers;
for (auto* watcher : watchers)
{
if (contains(m_watchers, watcher)) // make sure this watcher is still alive
watcher->on_option_changed(option);
}
}
CandidateList OptionsRegistry::complete_option_name(StringView prefix,
ByteCount cursor_pos) const
{
using OptionPtr = std::unique_ptr<const OptionDesc>;
return complete(prefix, cursor_pos, m_descs |
filter([](const OptionPtr& desc)
{ return not (desc->flags() & OptionFlags::Hidden); }) |
transform(std::mem_fn(&OptionDesc::name)));
}
}