Refactor OptionManager, add OptionManagerWatcher

- use set_option to set an option, instead of operator[] (no-const)
- keeps a list of OptionManagerWatcher to notify when an option change
  it also notifies when an option changes in his parent and the option
  is not overridden.
This commit is contained in:
Maxime Coste 2012-06-14 13:16:44 +00:00
parent 109c11f29c
commit a943e08dc7
3 changed files with 89 additions and 32 deletions

View File

@ -640,13 +640,12 @@ void exec_commands_in_runtime_file(const CommandParameters& params,
} }
void set_option(OptionManager& option_manager, const CommandParameters& params, void set_option(OptionManager& option_manager, const CommandParameters& params,
HookManager& hook_manager, const Context& context) const Context& context)
{ {
if (params.size() != 2) if (params.size() != 2)
throw wrong_argument_count(); throw wrong_argument_count();
option_manager[params[0]] = params[1]; option_manager.set_option(params[0], Option(params[1]));
hook_manager.run_hook("SetOption", params[0] + "=" + params[1], context);
} }
class RegisterRestorer class RegisterRestorer
@ -920,7 +919,7 @@ void register_commands()
cm.register_commands({ "setg", "setglobal" }, cm.register_commands({ "setg", "setglobal" },
[](const CommandParameters& params, const Context& context) [](const CommandParameters& params, const Context& context)
{ set_option(GlobalOptionManager::instance(), params, GlobalHookManager::instance(), context); }, { set_option(GlobalOptionManager::instance(), params, context); },
CommandManager::None, CommandManager::None,
PerArgumentCommandCompleter({ PerArgumentCommandCompleter({
[](const String& prefix, size_t cursor_pos) [](const String& prefix, size_t cursor_pos)
@ -928,7 +927,7 @@ void register_commands()
})); }));
cm.register_commands({ "setb", "setbuffer" }, cm.register_commands({ "setb", "setbuffer" },
[](const CommandParameters& params, const Context& context) [](const CommandParameters& params, const Context& context)
{ set_option(context.buffer().option_manager(), params, context.buffer().hook_manager(), context); }, { set_option(context.buffer().option_manager(), params, context); },
CommandManager::None, CommandManager::None,
PerArgumentCommandCompleter({ PerArgumentCommandCompleter({
[](const String& prefix, size_t cursor_pos) [](const String& prefix, size_t cursor_pos)
@ -936,7 +935,7 @@ void register_commands()
})); }));
cm.register_commands({ "setw", "setwindow" }, cm.register_commands({ "setw", "setwindow" },
[](const CommandParameters& params, const Context& context) [](const CommandParameters& params, const Context& context)
{ set_option(context.window().option_manager(), params, context.window().hook_manager(), context); }, { set_option(context.window().option_manager(), params, context); },
CommandManager::None, CommandManager::None,
PerArgumentCommandCompleter({ PerArgumentCommandCompleter({
[](const String& prefix, size_t cursor_pos) [](const String& prefix, size_t cursor_pos)

View File

@ -1,31 +1,47 @@
#include "option_manager.hh" #include "option_manager.hh"
#include "assert.hh"
#include <sstream> #include <sstream>
namespace Kakoune namespace Kakoune
{ {
Option& OptionManager::operator[] (const String& name) OptionManager::OptionManager(OptionManager& parent)
: m_parent(&parent)
{ {
auto it = m_options.find(name); parent.register_watcher(*this);
if (it != m_options.end())
return it->second;
else
{
Option& res = m_options[name];
OptionManager* parent = m_parent;
while (parent)
{
auto parent_it = parent->m_options.find(name);
if (parent_it != parent->m_options.end())
{
res = parent_it->second;
break;
} }
else
parent = parent->m_parent; OptionManager::~OptionManager()
{
if (m_parent)
m_parent->unregister_watcher(*this);
assert(m_watchers.empty());
} }
return res;
void OptionManager::register_watcher(OptionManagerWatcher& watcher)
{
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);
assert(it != m_watchers.end());
m_watchers.erase(it);
}
void OptionManager::set_option(const String& name, const Option& value)
{
Option old_value = m_options[name];
m_options[name] = value;
if (old_value != value)
{
for (auto watcher : m_watchers)
watcher->on_option_changed(name, value);
} }
} }
@ -56,10 +72,28 @@ CandidateList OptionManager::complete_option_name(const String& prefix,
return result; return result;
} }
OptionManager::OptionMap OptionManager::flatten_options() const
{
OptionMap res = m_parent ? m_parent->flatten_options() : OptionMap();
for (auto& option : m_options)
res.insert(option);
return res;
}
void OptionManager::on_option_changed(const String& name, const Option& value)
{
// if parent option changed, but we overrided it, it's like nothing happened
if (m_options.find(name) != m_options.end())
return;
for (auto watcher : m_watchers)
watcher->on_option_changed(name, value);
}
GlobalOptionManager::GlobalOptionManager() GlobalOptionManager::GlobalOptionManager()
: OptionManager() : OptionManager()
{ {
(*this)["tabstop"] = 8; set_option("tabstop", Option(8));
} }
} }

View File

@ -28,32 +28,56 @@ public:
Option& operator=(int value) { m_value = int_to_str(value); return *this; } Option& operator=(int value) { m_value = int_to_str(value); return *this; }
Option& operator=(const String& value) { m_value = value; return *this; } Option& operator=(const String& value) { m_value = value; return *this; }
bool operator==(const Option& other) const { return m_value == other.m_value; }
bool operator!=(const Option& other) const { return m_value != other.m_value; }
int as_int() const { return atoi(m_value.c_str()); } int as_int() const { return atoi(m_value.c_str()); }
String as_string() const { return m_value; } String as_string() const { return m_value; }
private: private:
String m_value; String m_value;
}; };
class OptionManager class OptionManagerWatcher
{ {
public: public:
OptionManager(OptionManager& parent) virtual ~OptionManagerWatcher() {}
: m_parent(&parent) {}
virtual void on_option_changed(const String& name,
const Option& option) = 0;
};
class OptionManager : private OptionManagerWatcher
{
public:
OptionManager(OptionManager& parent);
~OptionManager();
Option& operator[] (const String& name);
const Option& operator[] (const String& name) const; const Option& operator[] (const String& name) const;
void set_option(const String& name, const Option& value);
CandidateList complete_option_name(const String& prefix, CandidateList complete_option_name(const String& prefix,
size_t cursor_pos); size_t cursor_pos);
typedef std::unordered_map<String, Option> OptionMap;
OptionMap flatten_options() const;
void register_watcher(OptionManagerWatcher& watcher);
void unregister_watcher(OptionManagerWatcher& watcher);
private: private:
OptionManager() OptionManager()
: m_parent(nullptr) {} : m_parent(nullptr) {}
// the only one allowed to construct a root option manager // the only one allowed to construct a root option manager
friend class GlobalOptionManager; friend class GlobalOptionManager;
std::unordered_map<String, Option> m_options; OptionMap m_options;
OptionManager* m_parent; OptionManager* m_parent;
void on_option_changed(const String& name,
const Option& value);
std::vector<OptionManagerWatcher*> m_watchers;
}; };
class GlobalOptionManager : public OptionManager, class GlobalOptionManager : public OptionManager,