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"
|
2017-06-23 10:54:21 +02:00
|
|
|
#include "hash_map.hh"
|
2017-08-29 10:23:03 +02:00
|
|
|
#include "option.hh"
|
|
|
|
#include "ranges.hh"
|
2017-06-23 10:54:21 +02:00
|
|
|
#include "utils.hh"
|
2017-08-29 10:23:03 +02:00
|
|
|
#include "vector.hh"
|
2017-10-09 16:12:42 +02:00
|
|
|
#include "string_utils.hh"
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2016-03-14 10:31:13 +01:00
|
|
|
#include <memory>
|
2016-08-06 10:05:50 +02:00
|
|
|
#include <type_traits>
|
2016-03-14 10:31:13 +01:00
|
|
|
|
2012-04-03 15:39:20 +02:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2013-03-03 17:25:40 +01:00
|
|
|
class OptionManager;
|
2017-05-25 09:38:11 +02:00
|
|
|
class Context;
|
2013-03-03 17:25:40 +01:00
|
|
|
|
2014-04-12 21:03:26 +02:00
|
|
|
enum class OptionFlags
|
|
|
|
{
|
|
|
|
None = 0,
|
|
|
|
Hidden = 1,
|
|
|
|
};
|
|
|
|
|
2017-03-15 18:55:34 +01:00
|
|
|
constexpr bool with_bit_ops(Meta::Type<OptionFlags>) { return true; }
|
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
|
|
|
|
2017-03-07 17:11:40 +01:00
|
|
|
class Option : public UseMemoryDomain<MemoryDomain::Options>
|
2014-04-12 21:03:26 +02:00
|
|
|
{
|
|
|
|
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;
|
2015-12-12 12:45:45 +01:00
|
|
|
template<typename T> T& get_mutable();
|
2015-07-24 00:42:48 +02:00
|
|
|
template<typename T> void set(const T& val, bool notify=true);
|
2013-04-17 19:10:51 +02:00
|
|
|
template<typename T> bool is_of_type() const;
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2018-08-27 13:52:57 +02:00
|
|
|
virtual String get_as_string(Quoting quoting) const = 0;
|
2018-05-30 15:23:38 +02:00
|
|
|
virtual Vector<String> get_as_strings() const = 0;
|
|
|
|
virtual void set_from_strings(ConstArrayView<String> strs) = 0;
|
|
|
|
virtual void add_from_strings(ConstArrayView<String> strs) = 0;
|
|
|
|
virtual void update(const Context& context) = 0;
|
2012-06-14 15:16:44 +02:00
|
|
|
|
2018-11-28 11:45:40 +01:00
|
|
|
virtual bool has_same_value(const Option& other) const = 0;
|
|
|
|
|
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:
|
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
|
|
|
};
|
|
|
|
|
2018-05-08 14:07:43 +02:00
|
|
|
class OptionManager final : private OptionManagerWatcher
|
2012-04-03 15:39:20 +02:00
|
|
|
{
|
|
|
|
public:
|
2012-06-14 15:16:44 +02:00
|
|
|
OptionManager(OptionManager& parent);
|
2017-05-22 11:31:56 +02:00
|
|
|
~OptionManager();
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2014-11-16 21:55:36 +01:00
|
|
|
Option& operator[] (StringView name);
|
|
|
|
const Option& operator[] (StringView name) const;
|
|
|
|
Option& get_local_option(StringView name);
|
2012-06-14 15:16:44 +02:00
|
|
|
|
2015-08-10 14:54:52 +02:00
|
|
|
void unset_option(StringView name);
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2018-02-20 13:32:51 +01:00
|
|
|
auto flatten_options() const
|
|
|
|
{
|
|
|
|
auto merge = [](auto&& first, const OptionMap& second) {
|
|
|
|
return concatenated(std::forward<decltype(first)>(first)
|
|
|
|
| filter([&second](auto& i) { return not second.contains(i.key); }),
|
|
|
|
second);
|
|
|
|
};
|
|
|
|
static const OptionMap empty;
|
|
|
|
auto& parent = m_parent ? m_parent->m_options : empty;
|
|
|
|
auto& grand_parent = (m_parent and m_parent->m_parent) ? m_parent->m_parent->m_options : empty;
|
2018-03-13 04:24:03 +01:00
|
|
|
return merge(merge(grand_parent, parent), m_options) | transform(&OptionMap::Item::value);
|
2018-02-20 13:32:51 +01:00
|
|
|
}
|
2012-06-14 15:16:44 +02:00
|
|
|
|
2016-08-21 21:24:18 +02:00
|
|
|
void register_watcher(OptionManagerWatcher& watcher) const;
|
|
|
|
void unregister_watcher(OptionManagerWatcher& watcher) const;
|
2012-06-14 15:16:44 +02:00
|
|
|
|
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;
|
2018-02-20 13:32:51 +01:00
|
|
|
using OptionMap = HashMap<StringView, std::unique_ptr<Option>, MemoryDomain::Options>;
|
2012-04-03 15:39:20 +02:00
|
|
|
|
2018-02-20 13:32:51 +01:00
|
|
|
OptionMap m_options;
|
2012-04-03 15:39:20 +02:00
|
|
|
OptionManager* m_parent;
|
2012-06-14 15:16:44 +02:00
|
|
|
|
2016-08-21 21:24:18 +02:00
|
|
|
mutable Vector<OptionManagerWatcher*, MemoryDomain::Options> 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
|
|
|
|
2015-07-24 00:42:48 +02:00
|
|
|
void set(T value, bool notify = true)
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
2016-02-11 23:07:18 +01:00
|
|
|
validate(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);
|
2015-07-24 00:42:48 +02:00
|
|
|
if (notify)
|
|
|
|
manager().on_option_changed(*this);
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
const T& get() const { return m_value; }
|
2015-12-12 12:45:45 +01:00
|
|
|
T& get_mutable() { return m_value; }
|
2013-03-26 13:47:14 +01:00
|
|
|
|
2018-05-30 15:23:38 +02:00
|
|
|
Vector<String> get_as_strings() const override
|
|
|
|
{
|
|
|
|
return option_to_strings(m_value);
|
|
|
|
}
|
|
|
|
|
2018-08-27 13:52:57 +02:00
|
|
|
String get_as_string(Quoting quoting) const override
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
2018-08-27 13:52:57 +02:00
|
|
|
return option_to_string(m_value, quoting);
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
2018-05-30 15:23:38 +02:00
|
|
|
|
|
|
|
void set_from_strings(ConstArrayView<String> strs) override
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
2018-05-30 15:23:38 +02:00
|
|
|
set(option_from_strings(Meta::Type<T>{}, strs));
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
2018-05-30 15:23:38 +02:00
|
|
|
|
|
|
|
void add_from_strings(ConstArrayView<String> strs) override
|
2013-03-29 19:34:57 +01:00
|
|
|
{
|
2018-05-30 15:23:38 +02:00
|
|
|
if (option_add_from_strings(m_value, strs))
|
2013-03-29 19:34:57 +01:00
|
|
|
m_manager.on_option_changed(*this);
|
|
|
|
}
|
2018-05-30 15:23:38 +02:00
|
|
|
|
2017-05-25 09:38:11 +02:00
|
|
|
void update(const Context& context) override
|
|
|
|
{
|
|
|
|
option_update(m_value, context);
|
|
|
|
}
|
2018-11-28 11:45:40 +01:00
|
|
|
|
|
|
|
bool has_same_value(const Option& other) const override
|
|
|
|
{
|
|
|
|
return other.is_of_type<T>() and other.get<T>() == m_value;
|
|
|
|
}
|
2013-03-26 13:47:14 +01:00
|
|
|
private:
|
2016-02-11 23:07:18 +01:00
|
|
|
virtual void validate(const T& value) const {}
|
2013-03-26 13:47:14 +01:00
|
|
|
T m_value;
|
|
|
|
};
|
|
|
|
|
2016-02-11 23:07:18 +01:00
|
|
|
template<typename T, void (*validator)(const T&)>
|
|
|
|
class TypedCheckedOption : public TypedOption<T>
|
|
|
|
{
|
|
|
|
using TypedOption<T>::TypedOption;
|
|
|
|
|
|
|
|
Option* clone(OptionManager& manager) const override
|
|
|
|
{
|
|
|
|
return new TypedCheckedOption{manager, this->m_desc, this->get()};
|
|
|
|
}
|
|
|
|
|
|
|
|
void validate(const T& value) const override { if (validator != nullptr) validator(value); }
|
|
|
|
};
|
|
|
|
|
2013-03-26 13:47:14 +01:00
|
|
|
template<typename T> const T& Option::get() const
|
|
|
|
{
|
|
|
|
auto* typed_opt = dynamic_cast<const TypedOption<T>*>(this);
|
|
|
|
if (not typed_opt)
|
2018-05-26 02:01:26 +02:00
|
|
|
throw runtime_error(format("option '{}' is not of type '{}'", name(),
|
|
|
|
option_type_name(Meta::Type<T>{})));
|
2013-03-26 13:47:14 +01:00
|
|
|
return typed_opt->get();
|
|
|
|
}
|
|
|
|
|
2015-12-12 12:45:45 +01:00
|
|
|
template<typename T> T& Option::get_mutable()
|
|
|
|
{
|
|
|
|
return const_cast<T&>(get<T>());
|
|
|
|
}
|
|
|
|
|
2015-07-24 00:42:48 +02:00
|
|
|
template<typename T> void Option::set(const T& val, bool notify)
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
|
|
|
auto* typed_opt = dynamic_cast<TypedOption<T>*>(this);
|
|
|
|
if (not typed_opt)
|
2018-05-26 02:01:26 +02:00
|
|
|
throw runtime_error(format("option '{}' is not of type '{}'", name(),
|
|
|
|
option_type_name(Meta::Type<T>{})));
|
2015-07-24 00:42:48 +02:00
|
|
|
return typed_opt->set(val, notify);
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2016-02-11 23:07:18 +01:00
|
|
|
template<typename T, void (*validator)(const T&) = nullptr>
|
2016-08-06 07:52:11 +02:00
|
|
|
Option& declare_option(StringView name, StringView docstring,
|
2014-04-09 21:14:04 +02:00
|
|
|
const T& value,
|
2014-08-19 19:56:11 +02:00
|
|
|
OptionFlags flags = OptionFlags::None)
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
2018-03-25 07:35:33 +02:00
|
|
|
auto is_option_identifier = [](char c) {
|
|
|
|
return is_basic_alpha(c) or is_basic_digit(c) or c == '_';
|
2016-09-27 10:54:55 +02:00
|
|
|
};
|
|
|
|
|
2018-03-25 07:35:33 +02:00
|
|
|
if (not all_of(name, is_option_identifier))
|
2016-09-27 10:54:55 +02:00
|
|
|
throw runtime_error{format("name '{}' contains char out of [a-zA-Z0-9_]", name)};
|
|
|
|
|
2014-10-30 15:00:42 +01:00
|
|
|
auto& opts = m_global_manager.m_options;
|
2017-06-23 10:54:21 +02:00
|
|
|
auto it = opts.find(name);
|
2014-10-30 15:00:42 +01:00
|
|
|
if (it != opts.end())
|
2013-04-17 19:10:51 +02:00
|
|
|
{
|
2017-06-23 10:54:21 +02:00
|
|
|
if (it->value->is_of_type<T>() and it->value->flags() == flags)
|
|
|
|
return *it->value;
|
2016-09-27 10:54:55 +02:00
|
|
|
throw runtime_error{format("option '{}' already declared with different type or flags", name)};
|
2013-04-17 19:10:51 +02:00
|
|
|
}
|
2017-03-15 18:42:02 +01:00
|
|
|
String doc = docstring.empty() ? format("[{}]", option_type_name(Meta::Type<T>{}))
|
|
|
|
: format("[{}] - {}", option_type_name(Meta::Type<T>{}), docstring);
|
2016-08-07 11:47:26 +02:00
|
|
|
m_descs.emplace_back(new OptionDesc{name.str(), std::move(doc), flags});
|
2017-06-23 10:54:21 +02:00
|
|
|
return *opts.insert({m_descs.back()->name(),
|
2016-03-12 16:27:54 +01:00
|
|
|
std::make_unique<TypedCheckedOption<T, validator>>(m_global_manager, *m_descs.back(), value)});
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
2014-05-05 13:55:04 +02:00
|
|
|
|
2015-08-10 14:53:30 +02:00
|
|
|
const OptionDesc* option_desc(StringView name) const
|
2014-05-05 13:55:04 +02:00
|
|
|
{
|
2015-08-10 14:53:30 +02:00
|
|
|
auto it = find_if(m_descs,
|
2016-09-26 23:59:02 +02:00
|
|
|
[&name](const std::unique_ptr<const OptionDesc>& opt)
|
2015-08-10 14:53:30 +02:00
|
|
|
{ return opt->name() == name; });
|
|
|
|
return it != m_descs.end() ? it->get() : nullptr;
|
2014-05-05 13:55:04 +02:00
|
|
|
}
|
2015-08-10 14:38:06 +02:00
|
|
|
|
2015-08-10 14:53:30 +02:00
|
|
|
bool option_exists(StringView name) const { return option_desc(name) != nullptr; }
|
|
|
|
|
2015-08-10 14:38:06 +02:00
|
|
|
CandidateList complete_option_name(StringView prefix, ByteCount cursor_pos) const;
|
2019-01-20 12:46:40 +01:00
|
|
|
|
|
|
|
void clear_option_trash() { m_option_trash.clear(); }
|
|
|
|
void move_to_trash(std::unique_ptr<Option>&& option) { m_option_trash.push_back(std::move(option)); }
|
2014-04-12 21:03:26 +02:00
|
|
|
private:
|
2014-10-30 15:00:42 +01:00
|
|
|
OptionManager& m_global_manager;
|
2016-09-26 23:59:02 +02:00
|
|
|
Vector<std::unique_ptr<const OptionDesc>, MemoryDomain::Options> m_descs;
|
2019-01-20 12:46:40 +01:00
|
|
|
Vector<std::unique_ptr<Option>> m_option_trash;
|
2013-03-03 17:25:40 +01:00
|
|
|
};
|
2012-04-03 15:39:20 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // option_manager_hh_INCLUDED
|