2012-04-03 15:39:20 +02:00
|
|
|
#ifndef option_manager_hh_INCLUDED
|
|
|
|
#define option_manager_hh_INCLUDED
|
|
|
|
|
2012-04-03 20:25:27 +02:00
|
|
|
#include "completion.hh"
|
2013-04-09 20:05:40 +02:00
|
|
|
#include "exception.hh"
|
2014-10-23 19:55:45 +02:00
|
|
|
#include "flags.hh"
|
2013-03-26 13:47:14 +01:00
|
|
|
#include "option_types.hh"
|
2013-04-09 20:05:40 +02:00
|
|
|
#include "utils.hh"
|
2014-10-13 14:12:33 +02:00
|
|
|
#include "regex.hh"
|
2012-04-03 15:39:20 +02:00
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
|
|
|
struct option_not_found : public runtime_error
|
|
|
|
{
|
2014-11-15 19:45:56 +01:00
|
|
|
option_not_found(StringView name)
|
2012-04-03 15:39:20 +02:00
|
|
|
: runtime_error("option not found: " + name) {}
|
|
|
|
};
|
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
class OptionManager;
|
|
|
|
|
2014-04-12 21:03:26 +02:00
|
|
|
enum class OptionFlags
|
|
|
|
{
|
|
|
|
None = 0,
|
|
|
|
Hidden = 1,
|
|
|
|
};
|
|
|
|
|
2014-10-23 19:55:45 +02:00
|
|
|
template<> struct WithBitOps<OptionFlags> : std::true_type {};
|
2014-04-12 21:03:26 +02:00
|
|
|
|
|
|
|
class OptionDesc
|
2012-04-03 15:39:20 +02:00
|
|
|
{
|
|
|
|
public:
|
2014-04-12 21:03:26 +02:00
|
|
|
OptionDesc(String name, String docstring, OptionFlags flags);
|
|
|
|
|
|
|
|
const String& name() const { return m_name; }
|
|
|
|
const String& docstring() const { return m_docstring; }
|
2013-11-12 20:21:07 +01:00
|
|
|
|
2014-04-12 21:03:26 +02:00
|
|
|
OptionFlags flags() const { return m_flags; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
String m_name;
|
|
|
|
String m_docstring;
|
|
|
|
OptionFlags m_flags;
|
|
|
|
};
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2014-04-12 21:03:26 +02:00
|
|
|
class Option
|
|
|
|
{
|
|
|
|
public:
|
2014-04-30 20:40:23 +02:00
|
|
|
virtual ~Option() = default;
|
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
template<typename T> const T& get() const;
|
|
|
|
template<typename T> void set(const T& val);
|
2013-04-17 19:10:51 +02:00
|
|
|
template<typename T> bool is_of_type() const;
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
virtual String get_as_string() const = 0;
|
|
|
|
virtual void set_from_string(const String& str) = 0;
|
2013-03-29 19:34:57 +01:00
|
|
|
virtual void add_from_string(const String& str) = 0;
|
2012-06-14 15:16:44 +02:00
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
virtual Option* clone(OptionManager& manager) const = 0;
|
2014-04-12 21:03:26 +02:00
|
|
|
OptionManager& manager() const { return m_manager; }
|
2013-11-12 20:21:07 +01:00
|
|
|
|
2014-04-12 21:03:26 +02:00
|
|
|
const String& name() const { return m_desc.name(); }
|
|
|
|
const String& docstring() const { return m_desc.docstring(); }
|
|
|
|
OptionFlags flags() const { return m_desc.flags(); }
|
2013-11-12 20:21:07 +01:00
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
protected:
|
2014-04-12 21:03:26 +02:00
|
|
|
Option(const OptionDesc& desc, OptionManager& manager);
|
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
OptionManager& m_manager;
|
2014-04-12 21:03:26 +02:00
|
|
|
const OptionDesc& m_desc;
|
2012-04-03 15:39:20 +02:00
|
|
|
};
|
|
|
|
|
2012-06-14 15:16:44 +02:00
|
|
|
class OptionManagerWatcher
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual ~OptionManagerWatcher() {}
|
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
virtual void on_option_changed(const Option& option) = 0;
|
2012-06-14 15:16:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
class OptionManager : private OptionManagerWatcher
|
2012-04-03 15:39:20 +02:00
|
|
|
{
|
|
|
|
public:
|
2012-06-14 15:16:44 +02:00
|
|
|
OptionManager(OptionManager& parent);
|
|
|
|
~OptionManager();
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2014-05-25 18:36:12 +02:00
|
|
|
Option& operator[] (const String& name);
|
2012-04-14 03:17:09 +02:00
|
|
|
const Option& operator[] (const String& name) const;
|
2013-03-03 17:25:40 +01:00
|
|
|
Option& get_local_option(const String& name);
|
2012-06-14 15:16:44 +02:00
|
|
|
|
2014-04-18 15:02:14 +02:00
|
|
|
CandidateList complete_option_name(StringView prefix,
|
2012-10-11 00:41:48 +02:00
|
|
|
ByteCount cursor_pos);
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
using OptionList = std::vector<const Option*>;
|
|
|
|
OptionList flatten_options() const;
|
2012-06-14 15:16:44 +02:00
|
|
|
|
|
|
|
void register_watcher(OptionManagerWatcher& watcher);
|
|
|
|
void unregister_watcher(OptionManagerWatcher& watcher);
|
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
void on_option_changed(const Option& option) override;
|
2012-04-03 15:39:20 +02:00
|
|
|
private:
|
|
|
|
OptionManager()
|
|
|
|
: m_parent(nullptr) {}
|
|
|
|
// the only one allowed to construct a root option manager
|
2014-10-30 15:00:42 +01:00
|
|
|
friend class Scope;
|
|
|
|
friend class OptionsRegistry;
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2014-01-02 20:20:16 +01:00
|
|
|
template<typename MatchingFunc>
|
|
|
|
CandidateList get_matching_names(MatchingFunc func);
|
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
std::vector<std::unique_ptr<Option>> m_options;
|
2012-04-03 15:39:20 +02:00
|
|
|
OptionManager* m_parent;
|
2012-06-14 15:16:44 +02:00
|
|
|
|
|
|
|
std::vector<OptionManagerWatcher*> m_watchers;
|
2012-04-03 15:39:20 +02:00
|
|
|
};
|
|
|
|
|
2013-03-26 13:47:14 +01:00
|
|
|
template<typename T>
|
|
|
|
class TypedOption : public Option
|
|
|
|
{
|
|
|
|
public:
|
2014-08-19 19:56:11 +02:00
|
|
|
TypedOption(OptionManager& manager, const OptionDesc& desc, const T& value)
|
|
|
|
: Option(desc, manager), m_value(value) {}
|
2013-03-26 13:47:14 +01:00
|
|
|
|
2013-05-06 13:52:41 +02:00
|
|
|
void set(T value)
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
|
|
|
if (m_value != value)
|
|
|
|
{
|
2013-05-06 13:52:41 +02:00
|
|
|
m_value = std::move(value);
|
2014-04-12 21:03:26 +02:00
|
|
|
manager().on_option_changed(*this);
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
const T& get() const { return m_value; }
|
|
|
|
|
|
|
|
String get_as_string() const override
|
|
|
|
{
|
|
|
|
return option_to_string(m_value);
|
|
|
|
}
|
|
|
|
void set_from_string(const String& str) override
|
|
|
|
{
|
|
|
|
T val;
|
|
|
|
option_from_string(str, val);
|
2013-05-06 13:52:41 +02:00
|
|
|
set(std::move(val));
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
2013-03-29 19:34:57 +01:00
|
|
|
void add_from_string(const String& str) override
|
|
|
|
{
|
|
|
|
T val;
|
|
|
|
option_from_string(str, val);
|
|
|
|
if (option_add(m_value, val))
|
|
|
|
m_manager.on_option_changed(*this);
|
|
|
|
}
|
2013-03-26 13:47:14 +01:00
|
|
|
|
|
|
|
Option* clone(OptionManager& manager) const override
|
|
|
|
{
|
2014-08-19 19:56:11 +02:00
|
|
|
return new TypedOption{manager, m_desc, m_value};
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
|
|
|
private:
|
|
|
|
T m_value;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T> const T& Option::get() const
|
|
|
|
{
|
|
|
|
auto* typed_opt = dynamic_cast<const TypedOption<T>*>(this);
|
|
|
|
if (not typed_opt)
|
|
|
|
throw runtime_error("option " + name() + " is not of type " + typeid(T).name());
|
|
|
|
return typed_opt->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> void Option::set(const T& val)
|
|
|
|
{
|
|
|
|
auto* typed_opt = dynamic_cast<TypedOption<T>*>(this);
|
|
|
|
if (not typed_opt)
|
|
|
|
throw runtime_error("option " + name() + " is not of type " + typeid(T).name());
|
|
|
|
return typed_opt->set(val);
|
|
|
|
}
|
|
|
|
|
2013-04-17 19:10:51 +02:00
|
|
|
template<typename T> bool Option::is_of_type() const
|
|
|
|
{
|
|
|
|
return dynamic_cast<const TypedOption<T>*>(this) != nullptr;
|
|
|
|
}
|
|
|
|
|
2013-03-26 13:47:14 +01:00
|
|
|
template<typename T>
|
|
|
|
auto find_option(T& container, const String& name) -> decltype(container.begin())
|
|
|
|
{
|
|
|
|
using ptr_type = decltype(*container.begin());
|
|
|
|
return find_if(container, [&name](const ptr_type& opt) { return opt->name() == name; });
|
|
|
|
}
|
|
|
|
|
2014-10-30 15:00:42 +01:00
|
|
|
class OptionsRegistry
|
2012-04-03 15:39:20 +02:00
|
|
|
{
|
|
|
|
public:
|
2014-10-30 15:00:42 +01:00
|
|
|
OptionsRegistry(OptionManager& global_manager) : m_global_manager(global_manager) {}
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
template<typename T>
|
2014-04-09 21:14:04 +02:00
|
|
|
Option& declare_option(const String& name, const String& docstring,
|
|
|
|
const T& value,
|
2014-08-19 19:56:11 +02:00
|
|
|
OptionFlags flags = OptionFlags::None)
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
2014-10-30 15:00:42 +01:00
|
|
|
auto& opts = m_global_manager.m_options;
|
|
|
|
auto it = find_option(opts, name);
|
|
|
|
if (it != opts.end())
|
2013-04-17 19:10:51 +02:00
|
|
|
{
|
2013-11-12 20:21:07 +01:00
|
|
|
if ((*it)->is_of_type<T>() and (*it)->flags() == flags)
|
2013-04-17 19:10:51 +02:00
|
|
|
return **it;
|
2013-11-12 20:21:07 +01:00
|
|
|
throw runtime_error("option " + name + " already declared with different type or flags");
|
2013-04-17 19:10:51 +02:00
|
|
|
}
|
2014-04-12 21:03:26 +02:00
|
|
|
m_descs.emplace_back(new OptionDesc{name, docstring, flags});
|
2014-10-30 15:00:42 +01:00
|
|
|
opts.emplace_back(new TypedOption<T>{m_global_manager, *m_descs.back(), value});
|
|
|
|
return *opts.back();
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
2014-05-05 13:55:04 +02:00
|
|
|
|
2014-11-15 19:45:56 +01:00
|
|
|
bool option_exists(StringView name) const
|
2014-05-05 13:55:04 +02:00
|
|
|
{
|
2014-10-30 15:00:42 +01:00
|
|
|
return find_if(m_descs, [&name](const std::unique_ptr<OptionDesc>& opt) {
|
|
|
|
return opt->name() == name;
|
|
|
|
}) != m_descs.end();
|
2014-05-05 13:55:04 +02:00
|
|
|
}
|
2014-04-12 21:03:26 +02:00
|
|
|
private:
|
2014-10-30 15:00:42 +01:00
|
|
|
OptionManager& m_global_manager;
|
2014-04-12 21:03:26 +02:00
|
|
|
std::vector<std::unique_ptr<OptionDesc>> m_descs;
|
2013-03-03 17:25:40 +01:00
|
|
|
};
|
2012-04-03 15:39:20 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // option_manager_hh_INCLUDED
|