#ifndef option_types_hh_INCLUDED #define option_types_hh_INCLUDED #include "exception.hh" #include "string.hh" #include "units.hh" #include "coord.hh" #include "array_view.hh" #include "hash_map.hh" #include "flags.hh" #include "enum.hh" #include #include namespace Kakoune { template constexpr decltype(T::option_type_name) option_type_name(Meta::Type) { return T::option_type_name; } template typename std::enable_if::value, String>::type option_type_name(Meta::Type) { constexpr StringView type = with_bit_ops(Meta::Type{}) ? "flags" : "enum"; auto name = enum_desc(Meta::Type{}); return type + "(" + join(name | transform(std::mem_fn(&EnumDesc::name)), '|') + ")"; } inline String option_to_string(int opt) { return to_string(opt); } inline void option_from_string(StringView str, int& opt) { opt = str_to_int(str); } inline bool option_add(int& opt, StringView str) { auto val = str_to_int(str); opt += val; return val != 0; } inline StringView option_type_name(Meta::Type) { return "int"; } inline String option_to_string(size_t opt) { return to_string(opt); } inline void option_from_string(StringView str, size_t& opt) { opt = str_to_int(str); } inline String option_to_string(bool opt) { return opt ? "true" : "false"; } inline void option_from_string(StringView str, bool& opt) { if (str == "true" or str == "yes") opt = true; else if (str == "false" or str == "no") opt = false; else throw runtime_error("boolean values are either true, yes, false or no"); } inline StringView option_type_name(Meta::Type) { return "bool"; } constexpr char list_separator = ':'; template String option_to_string(const Vector& opt) { String res; for (size_t i = 0; i < opt.size(); ++i) { res += escape(option_to_string(opt[i]), list_separator, '\\'); if (i != opt.size() - 1) res += list_separator; } return res; } template void option_from_string(StringView str, Vector& opt) { opt.clear(); Vector elems = split(str, list_separator, '\\'); for (auto& elem: elems) { T opt_elem; option_from_string(elem, opt_elem); opt.push_back(opt_elem); } } template bool option_add(Vector& opt, StringView str) { Vector vec; option_from_string(str, vec); std::copy(std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end()), back_inserter(opt)); return not vec.empty(); } template String option_type_name(Meta::Type>) { return option_type_name(Meta::Type{}) + StringView{"-list"}; } template String option_to_string(const HashMap& opt) { String res; for (auto it = begin(opt); it != end(opt); ++it) { if (it != begin(opt)) res += list_separator; String elem = escape(option_to_string(it->key), '=', '\\') + "=" + escape(option_to_string(it->value), '=', '\\'); res += escape(elem, list_separator, '\\'); } return res; } template void option_from_string(StringView str, HashMap& opt) { opt.clear(); for (auto& elem : split(str, list_separator, '\\')) { Vector pair_str = split(elem, '=', '\\'); if (pair_str.size() != 2) throw runtime_error("map option expects key=value"); Key key; Value value; option_from_string(pair_str[0], key); option_from_string(pair_str[1], value); opt.insert({ std::move(key), std::move(value) }); } } 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 struct TupleOptionDetail { static String to_string(const std::tuple& opt) { return TupleOptionDetail::to_string(opt) + tuple_separator + escape(option_to_string(std::get(opt)), tuple_separator, '\\'); } static void from_string(ConstArrayView elems, std::tuple& opt) { option_from_string(elems[I], std::get(opt)); TupleOptionDetail::from_string(elems, opt); } }; template struct TupleOptionDetail<0, Types...> { static String to_string(const std::tuple& opt) { return option_to_string(std::get<0>(opt)); } static void from_string(ConstArrayView elems, std::tuple& opt) { option_from_string(elems[0], std::get<0>(opt)); } }; template String option_to_string(const std::tuple& opt) { return TupleOptionDetail::to_string(opt); } template void option_from_string(StringView str, std::tuple& opt) { auto elems = split(str, tuple_separator, '\\'); if (elems.size() != sizeof...(Types)) throw runtime_error(elems.size() < sizeof...(Types) ? "not enough elements in tuple" : "to many elements in tuple"); TupleOptionDetail::from_string(elems, opt); } template inline String option_to_string(const StronglyTypedNumber& opt) { return to_string(opt); } template inline void option_from_string(StringView str, StronglyTypedNumber& opt) { opt = StronglyTypedNumber{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 str) { throw runtime_error("no add operation supported for this option type"); } template inline void option_from_string(StringView str, LineAndColumn& opt) { auto vals = split(str, ','); if (vals.size() != 2) throw runtime_error("expected ,"); opt.line = str_to_int(vals[0]); opt.column = str_to_int(vals[1]); } template inline String option_to_string(const LineAndColumn& opt) { return format("{},{}", opt.line, opt.column); } enum class DebugFlags { None = 0, Hooks = 1 << 0, Shell = 1 << 1, Profile = 1 << 2, Keys = 1 << 3, }; constexpr bool with_bit_ops(Meta::Type) { return true; } constexpr Array, 4> enum_desc(Meta::Type) { return { { { DebugFlags::Hooks, "hooks" }, { DebugFlags::Shell, "shell" }, { DebugFlags::Profile, "profile" }, { DebugFlags::Keys, "keys" } } }; } template{}))> EnableIfWithBitOps 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{}))> EnableIfWithoutBitOps 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{}))> EnableIfWithBitOps option_from_string(StringView str, Flags& flags) { 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; } } template{}))> EnableIfWithoutBitOps option_from_string(StringView str, Enum& e) { 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)); e = it->value; } template{}))> EnableIfWithBitOps option_add(Flags& opt, StringView str) { Flags res = Flags{}; option_from_string(str, res); opt |= res; return res != (Flags)0; } template struct PrefixedList { P prefix; Vector list; }; template inline bool operator==(const PrefixedList& lhs, const PrefixedList& rhs) { return lhs.prefix == rhs.prefix and lhs.list == rhs.list; } template inline bool operator!=(const PrefixedList& lhs, const PrefixedList& rhs) { return not (lhs == rhs); } template inline String option_to_string(const PrefixedList& opt) { return format("{}:{}", opt.prefix, option_to_string(opt.list)); } template inline void option_from_string(StringView str, PrefixedList& opt) { auto it = find(str, ':'); option_from_string(StringView{str.begin(), it}, opt.prefix); if (it != str.end()) option_from_string({it+1, str.end()}, opt.list); } template inline bool option_add(PrefixedList& opt, StringView str) { return option_add(opt.list, str); } template using TimestampedList = PrefixedList; } #endif // option_types_hh_INCLUDED