#ifndef option_types_hh_INCLUDED #define option_types_hh_INCLUDED #include "array_view.hh" #include "coord.hh" #include "exception.hh" #include "flags.hh" #include "hash_map.hh" #include "option.hh" #include "ranges.hh" #include "string.hh" #include "string_utils.hh" #include "units.hh" #include #include namespace Kakoune { template String option_to_string(const T& value, Quoting) requires std::is_same_v())), String> { return option_to_string(value); } template constexpr decltype(T::option_type_name) option_type_name(Meta::Type) { return T::option_type_name; } template requires std::is_enum_v String option_type_name(Meta::Type) { return format("{}({})", with_bit_ops(Meta::Type{}) ? "flags" : "enum", join(enum_desc(Meta::Type{}) | transform(&EnumDesc::name), '|')); } inline String option_to_string(int opt) { return to_string(opt); } inline int option_from_string(Meta::Type, StringView str) { return str_to_int(str); } inline bool option_add(int& opt, StringView str) { auto val = str_to_int(str); opt += val; return val != 0; } inline bool option_remove(int& opt, StringView str) { auto val = str_to_int(str); opt -= val; return val != 0; } constexpr StringView option_type_name(Meta::Type) { return "int"; } inline String option_to_string(size_t opt) { return to_string(opt); } inline size_t option_from_string(Meta::Type, StringView str) { return str_to_int(str); } inline String option_to_string(bool opt) { return opt ? "true" : "false"; } inline bool option_from_string(Meta::Type, StringView str) { if (str == "true" or str == "yes") return true; else if (str == "false" or str == "no") return false; else throw runtime_error("boolean values are either true, yes, false or no"); } constexpr StringView option_type_name(Meta::Type) { return "bool"; } inline String option_to_string(Codepoint opt, Quoting quoting) { return quoter(quoting)(to_string(opt)); } inline Codepoint option_from_string(Meta::Type, StringView str) { if (str.char_length() != 1) throw runtime_error{format("'{}' is not a single codepoint", str)}; return str[0_char]; } constexpr StringView option_type_name(Meta::Type) { return "codepoint"; } template Vector option_to_strings(const Vector& opt) { return opt | transform([](const T& t) { return option_to_string(t, Quoting::Raw); }) | gather>(); } template String option_to_string(const Vector& opt, Quoting quoting) { return join(opt | transform([=](const T& t) { return option_to_string(t, quoting); }), ' ', false); } template void option_list_postprocess(Vector& opt) {} template Vector option_from_strings(Meta::Type>, ConstArrayView strs) { auto res = strs | transform([](auto&& s) { return option_from_string(Meta::Type{}, s); }) | gather>(); option_list_postprocess(res); return res; } template bool option_add_from_strings(Vector& opt, ConstArrayView strs) { auto vec = option_from_strings(Meta::Type>{}, strs); opt.insert(opt.end(), std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end())); option_list_postprocess(opt); return not vec.empty(); } template bool option_remove_from_strings(Vector& opt, ConstArrayView strs) { bool did_remove = false; for (auto&& val : strs | transform([](auto&& s) { return option_from_string(Meta::Type{}, s); })) { auto it = find(opt, val); if (it == opt.end()) continue; opt.erase(it); did_remove = true; } return did_remove; } template String option_type_name(Meta::Type>) { return option_type_name(Meta::Type{}) + "-list"_sv; } template Vector option_to_strings(const HashMap& opt) { return opt | transform([](auto&& item) { return format("{}={}", escape(option_to_string(item.key, Quoting::Raw), '=', '\\'), escape(option_to_string(item.value, Quoting::Raw), '=', '\\')); }) | gather>(); } template String option_to_string(const HashMap& opt, Quoting quoting) { return join(opt | transform([=](auto&& item) { return quoter(quoting)( format("{}={}", escape(option_to_string(item.key, Quoting::Raw), '=', '\\'), escape(option_to_string(item.value, Quoting::Raw), '=', '\\'))); }), ' ', false); } template bool option_add_from_strings(HashMap& opt, ConstArrayView strs) { struct error : runtime_error { error(size_t) : runtime_error{"map option expects key=value"} {} }; bool changed = false; for (auto&& str : strs) { auto key_value = str | split('=', '\\') | transform(unescape<'=', '\\'>) | static_gather(); opt[option_from_string(Meta::Type{}, key_value[0])] = option_from_string(Meta::Type{}, key_value[1]); changed = true; } return changed; } template bool option_remove_from_strings(HashMap& opt, ConstArrayView strs) { struct error : runtime_error { error(size_t) : runtime_error{"map option expects key=value"} {} }; bool changed = false; for (auto&& str : strs) { auto key_value = str | split('=', '\\') | transform(unescape<'=', '\\'>) | static_gather(); if (auto it = opt.find(key_value[0]); it != opt.end() and (key_value[1].empty() or key_value[1] == it->value)) { opt.remove(it->key); changed = true; } } return changed; } template HashMap option_from_strings(Meta::Type>, ConstArrayView str) { HashMap res; option_add_from_strings(res, str); return res; } template String option_type_name(Meta::Type>) { return format("{}-to-{}-map", option_type_name(Meta::Type{}), option_type_name(Meta::Type{})); } constexpr char tuple_separator = '|'; template String option_to_string_impl(Quoting quoting, const std::tuple& opt, std::index_sequence) { return quoter(quoting)(join(make_array({option_to_string(std::get(opt), Quoting::Raw)...}), tuple_separator)); } template String option_to_string(const std::tuple& opt, Quoting quoting) { return option_to_string_impl(quoting, opt, std::make_index_sequence()); } template std::tuple option_from_string_impl(Meta::Type>, StringView str, std::index_sequence) { struct error : runtime_error { error(size_t i) : runtime_error{i < sizeof...(Types) ? "not enough elements in tuple" : "too many elements in tuple"} {} }; auto elems = str | split(tuple_separator, '\\') | transform(unescape) | static_gather(); return std::tuple{option_from_string(Meta::Type{}, elems[I])...}; } template std::tuple option_from_string(Meta::Type>, StringView str) { return option_from_string_impl(Meta::Type>{}, str, std::make_index_sequence()); } template inline String option_to_string(const StronglyTypedNumber& opt) { return to_string(opt); } template requires std::is_base_of_v, Number> Number option_from_string(Meta::Type, StringView str) { return Number{str_to_int(str)}; } template inline bool option_add(StronglyTypedNumber& opt, StringView str) { int val = str_to_int(str); opt += val; return val != 0; } struct WorstMatch { template WorstMatch(T&&) {} }; inline bool option_add(WorstMatch, StringView) { throw runtime_error("no add operation supported for this option type"); } inline bool option_remove(WorstMatch, StringView) { throw runtime_error("no remove operation supported for this option type"); } class Context; inline void option_update(WorstMatch, const Context&) { throw runtime_error("no update operation supported for this option type"); } template requires std::is_base_of_v, Coord> Coord option_from_string(Meta::Type, StringView str) { struct error : runtime_error { error(size_t) : runtime_error{"expected ,"} {} }; auto vals = str | split(',') | static_gather(); return {str_to_int(vals[0]), str_to_int(vals[1])}; } template inline String option_to_string(const LineAndColumn& opt) { return format("{},{}", opt.line, opt.column); } template requires WithBitOps String option_to_string(Flags flags) { constexpr auto desc = enum_desc(Meta::Type{}); String res; for (int i = 0; i < desc.size(); ++i) { if (not (flags & desc[i].value)) continue; if (not res.empty()) res += "|"; res += desc[i].name; } return res; } template requires (not WithBitOps) String option_to_string(Enum e) { constexpr auto desc = enum_desc(Meta::Type{}); auto it = find_if(desc, [e](const EnumDesc& d) { return d.value == e; }); if (it != desc.end()) return it->name.str(); kak_assert(false); return {}; } template requires WithBitOps Flags option_from_string(Meta::Type, StringView str) { constexpr auto desc = enum_desc(Meta::Type{}); Flags flags{}; for (auto s : str | split('|')) { auto it = find_if(desc, [s](const EnumDesc& d) { return d.name == s; }); if (it == desc.end()) throw runtime_error(format("invalid flag value '{}'", s)); flags |= it->value; } return flags; } template requires (not WithBitOps) Enum option_from_string(Meta::Type, StringView str) { constexpr auto desc = enum_desc(Meta::Type{}); auto it = find_if(desc, [str](const EnumDesc& d) { return d.name == str; }); if (it == desc.end()) throw runtime_error(format("invalid enum value '{}'", str)); return it->value; } template requires WithBitOps bool option_add(Flags& opt, StringView str) { const Flags old = opt; opt |= option_from_string(Meta::Type{}, str); return opt != old; } template requires WithBitOps bool option_remove(Flags& opt, StringView str) { const Flags old = opt; opt &= ~option_from_string(Meta::Type{}, str); return opt != old; } template inline Vector option_to_strings(const PrefixedList& opt) { Vector res{option_to_string(opt.prefix, Quoting::Raw)}; auto list = option_to_strings(opt.list); res.insert(res.end(), std::make_move_iterator(list.begin()), std::make_move_iterator(list.end())); return res; } template inline String option_to_string(const PrefixedList& opt, Quoting quoting) { return option_to_string(opt.prefix, quoting) + " " + option_to_string(opt.list, quoting); } template inline PrefixedList option_from_strings(Meta::Type>, ConstArrayView strs) { if (strs.empty()) return {{}, {}}; return {option_from_string(Meta::Type

{}, strs[0]), option_from_strings(Meta::Type>{}, strs.subrange(1))}; } template inline bool option_add_from_strings(PrefixedList& opt, ConstArrayView str) { return option_add_from_strings(opt.list, str); } template inline bool option_remove_from_strings(PrefixedList& opt, ConstArrayView str) { return option_remove_from_strings(opt.list, str); } } #endif // option_types_hh_INCLUDED