diff --git a/src/buffer.hh b/src/buffer.hh index 9c648625..17de0f07 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -6,6 +6,7 @@ #include "option_manager.hh" #include "keymap_manager.hh" #include "string.hh" +#include "value.hh" #include #include @@ -166,6 +167,8 @@ public: KeymapManager& keymaps() { return m_keymaps; } const KeymapManager& keymaps() const { return m_keymaps; } + ValueMap& values() const { return m_values; } + void run_hook_in_own_context(const String& hook_name, const String& param); std::unordered_set& change_listeners() const { return m_change_listeners; } @@ -224,6 +227,10 @@ private: HookManager m_hooks; KeymapManager m_keymaps; + // Values are just data holding by the buffer, so it is part of its + // observable state + mutable ValueMap m_values; + friend constexpr Flags operator|(Flags lhs, Flags rhs) { return (Flags)((int) lhs | (int) rhs); diff --git a/src/value.hh b/src/value.hh new file mode 100644 index 00000000..9122255a --- /dev/null +++ b/src/value.hh @@ -0,0 +1,118 @@ +#ifndef value_hh_INCLUDED +#define value_hh_INCLUDED + +#include +#include + +#include "units.hh" + +namespace Kakoune +{ + +struct bad_value_cast {}; + +struct Value +{ + Value() = default; + + template + Value(const T& val) : m_value{new Model{val}} {} + + template + Value(T&& val) : m_value{new Model{std::move(val)}} {} + + Value(const Value& val) + { + if (val.m_value) + m_value.reset(val.m_value->clone()); + } + + Value(Value&&) = default; + + Value& operator=(const Value& val) + { + if (val.m_value) + m_value.reset(val.m_value->clone()); + else + m_value.reset(); + return *this; + } + + Value& operator=(Value&& val) = default; + + explicit operator bool() const { return (bool)m_value; } + + template + bool is_a() const + { + return m_value and m_value->type() == typeid(T); + } + + template + T& as() + { + if (not is_a()) + throw bad_value_cast{}; + return static_cast*>(m_value.get())->m_content; + } + + template + const T& as() const + { + return const_cast(this)->as(); + } + +private: + struct Concept + { + virtual ~Concept() {} + virtual const std::type_info& type() const = 0; + virtual Concept* clone() const = 0; + }; + + template + struct Model : public Concept + { + Model(const T& val) : m_content(val) {} + Model(T&& val) : m_content(std::move(val)) {} + + const std::type_info& type() const override { return typeid(T); } + Concept* clone() const { return new Model(m_content); } + + T m_content; + }; + + std::unique_ptr m_value; +}; + +struct ValueId : public StronglyTypedNumber +{ + constexpr ValueId(int value = 0) : StronglyTypedNumber(value) {} + + static ValueId get_free_id() + { + static ValueId next; + return next++; + } +}; + +using ValueMap = std::unordered_map; + +} + +namespace std +{ + +template<> +struct hash +{ + size_t operator()(Kakoune::ValueId val) const + { + return std::hash()((int)val); + } +}; + +} + + +#endif // value_hh_INCLUDED