From a943e08dc7b93c065d83639862fbcf1dd2fcb9c1 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Thu, 14 Jun 2012 13:16:44 +0000 Subject: [PATCH] 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. --- src/commands.cc | 11 +++---- src/option_manager.cc | 76 +++++++++++++++++++++++++++++++------------ src/option_manager.hh | 34 ++++++++++++++++--- 3 files changed, 89 insertions(+), 32 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index f5ab6c9d..71043d28 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -640,13 +640,12 @@ void exec_commands_in_runtime_file(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) throw wrong_argument_count(); - option_manager[params[0]] = params[1]; - hook_manager.run_hook("SetOption", params[0] + "=" + params[1], context); + option_manager.set_option(params[0], Option(params[1])); } class RegisterRestorer @@ -920,7 +919,7 @@ void register_commands() cm.register_commands({ "setg", "setglobal" }, [](const CommandParameters& params, const Context& context) - { set_option(GlobalOptionManager::instance(), params, GlobalHookManager::instance(), context); }, + { set_option(GlobalOptionManager::instance(), params, context); }, CommandManager::None, PerArgumentCommandCompleter({ [](const String& prefix, size_t cursor_pos) @@ -928,7 +927,7 @@ void register_commands() })); cm.register_commands({ "setb", "setbuffer" }, [](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, PerArgumentCommandCompleter({ [](const String& prefix, size_t cursor_pos) @@ -936,7 +935,7 @@ void register_commands() })); cm.register_commands({ "setw", "setwindow" }, [](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, PerArgumentCommandCompleter({ [](const String& prefix, size_t cursor_pos) diff --git a/src/option_manager.cc b/src/option_manager.cc index a0533f8f..c7d63d55 100644 --- a/src/option_manager.cc +++ b/src/option_manager.cc @@ -1,35 +1,51 @@ #include "option_manager.hh" +#include "assert.hh" #include namespace Kakoune { -Option& OptionManager::operator[] (const String& name) +OptionManager::OptionManager(OptionManager& parent) + : m_parent(&parent) { - auto it = m_options.find(name); - if (it != m_options.end()) - return it->second; - else + parent.register_watcher(*this); +} + +OptionManager::~OptionManager() +{ + if (m_parent) + m_parent->unregister_watcher(*this); + + assert(m_watchers.empty()); +} + +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) { - 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; - } - return res; + for (auto watcher : m_watchers) + watcher->on_option_changed(name, value); } } -const Option& OptionManager::operator[] (const String& name) const +const Option& OptionManager::operator[](const String& name) const { auto it = m_options.find(name); if (it != m_options.end()) @@ -56,10 +72,28 @@ CandidateList OptionManager::complete_option_name(const String& prefix, 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() : OptionManager() { - (*this)["tabstop"] = 8; + set_option("tabstop", Option(8)); } } diff --git a/src/option_manager.hh b/src/option_manager.hh index 91f35c08..49b8c9d5 100644 --- a/src/option_manager.hh +++ b/src/option_manager.hh @@ -28,32 +28,56 @@ public: Option& operator=(int value) { m_value = int_to_str(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()); } String as_string() const { return m_value; } private: String m_value; }; -class OptionManager +class OptionManagerWatcher { public: - OptionManager(OptionManager& parent) - : m_parent(&parent) {} + virtual ~OptionManagerWatcher() {} + + 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; + void set_option(const String& name, const Option& value); + CandidateList complete_option_name(const String& prefix, size_t cursor_pos); + typedef std::unordered_map OptionMap; + OptionMap flatten_options() const; + + void register_watcher(OptionManagerWatcher& watcher); + void unregister_watcher(OptionManagerWatcher& watcher); + private: OptionManager() : m_parent(nullptr) {} // the only one allowed to construct a root option manager friend class GlobalOptionManager; - std::unordered_map m_options; + OptionMap m_options; OptionManager* m_parent; + + void on_option_changed(const String& name, + const Option& value); + + std::vector m_watchers; }; class GlobalOptionManager : public OptionManager,