2013-03-26 00:14:38 +01:00
|
|
|
#ifndef option_types_hh_INCLUDED
|
|
|
|
#define option_types_hh_INCLUDED
|
|
|
|
|
2017-03-17 00:08:10 +01:00
|
|
|
#include "array_view.hh"
|
|
|
|
#include "coord.hh"
|
2013-04-09 20:05:40 +02:00
|
|
|
#include "exception.hh"
|
2017-03-17 00:08:10 +01:00
|
|
|
#include "flags.hh"
|
|
|
|
#include "hash_map.hh"
|
|
|
|
#include "option.hh"
|
2017-08-29 10:23:03 +02:00
|
|
|
#include "ranges.hh"
|
2013-03-26 00:14:38 +01:00
|
|
|
#include "string.hh"
|
2017-11-13 04:27:55 +01:00
|
|
|
#include "string_utils.hh"
|
2013-03-29 19:31:06 +01:00
|
|
|
#include "units.hh"
|
2013-03-26 00:14:38 +01:00
|
|
|
|
2013-03-29 19:31:06 +01:00
|
|
|
#include <tuple>
|
|
|
|
#include <vector>
|
|
|
|
|
2013-03-26 00:14:38 +01:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2016-08-06 10:05:50 +02:00
|
|
|
template<typename T>
|
2017-03-15 18:42:02 +01:00
|
|
|
constexpr decltype(T::option_type_name) option_type_name(Meta::Type<T>)
|
2016-08-06 10:05:50 +02:00
|
|
|
{
|
2017-03-15 18:42:02 +01:00
|
|
|
return T::option_type_name;
|
|
|
|
}
|
2016-08-06 10:05:50 +02:00
|
|
|
|
|
|
|
template<typename Enum>
|
2017-05-11 00:22:24 +02:00
|
|
|
std::enable_if_t<std::is_enum<Enum>::value, String>
|
2017-03-15 18:42:02 +01:00
|
|
|
option_type_name(Meta::Type<Enum>)
|
2016-08-06 10:05:50 +02:00
|
|
|
{
|
2017-05-24 16:40:16 +02:00
|
|
|
return format("{}({})", with_bit_ops(Meta::Type<Enum>{}) ? "flags" : "enum",
|
|
|
|
join(enum_desc(Meta::Type<Enum>{}) |
|
2018-03-13 04:24:03 +01:00
|
|
|
transform(&EnumDesc<Enum>::name), '|'));
|
2017-03-15 18:42:02 +01:00
|
|
|
}
|
2013-03-26 13:47:14 +01:00
|
|
|
|
2013-05-13 14:23:07 +02:00
|
|
|
inline String option_to_string(int opt) { return to_string(opt); }
|
2018-05-27 05:00:04 +02:00
|
|
|
inline int option_from_string(Meta::Type<int>, StringView str) { return str_to_int(str); }
|
2015-12-12 12:26:34 +01:00
|
|
|
inline bool option_add(int& opt, StringView str)
|
|
|
|
{
|
|
|
|
auto val = str_to_int(str);
|
|
|
|
opt += val;
|
|
|
|
return val != 0;
|
|
|
|
}
|
2017-03-16 10:57:39 +01:00
|
|
|
constexpr StringView option_type_name(Meta::Type<int>) { return "int"; }
|
2013-03-26 13:47:14 +01:00
|
|
|
|
2016-04-01 02:27:23 +02:00
|
|
|
inline String option_to_string(size_t opt) { return to_string(opt); }
|
2018-05-27 05:00:04 +02:00
|
|
|
inline size_t option_from_string(Meta::Type<size_t>, StringView str) { return str_to_int(str); }
|
2016-04-01 02:27:23 +02:00
|
|
|
|
2013-03-26 13:47:14 +01:00
|
|
|
inline String option_to_string(bool opt) { return opt ? "true" : "false"; }
|
2018-05-27 05:00:04 +02:00
|
|
|
inline bool option_from_string(Meta::Type<bool>, StringView str)
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
|
|
|
if (str == "true" or str == "yes")
|
2018-05-27 05:00:04 +02:00
|
|
|
return true;
|
2013-03-26 13:47:14 +01:00
|
|
|
else if (str == "false" or str == "no")
|
2018-05-27 05:00:04 +02:00
|
|
|
return false;
|
2013-03-26 13:47:14 +01:00
|
|
|
else
|
|
|
|
throw runtime_error("boolean values are either true, yes, false or no");
|
|
|
|
}
|
2017-03-16 10:57:39 +01:00
|
|
|
constexpr StringView option_type_name(Meta::Type<bool>) { return "bool"; }
|
2013-03-26 13:47:14 +01:00
|
|
|
|
2017-06-26 15:39:17 +02:00
|
|
|
inline String option_to_string(Codepoint opt) { return to_string(opt); }
|
2018-05-27 05:00:04 +02:00
|
|
|
inline Codepoint option_from_string(Meta::Type<Codepoint>, StringView str)
|
2017-06-26 15:39:17 +02:00
|
|
|
{
|
|
|
|
if (str.char_length() != 1)
|
|
|
|
throw runtime_error{format("'{}' is not a single codepoint", str)};
|
2018-05-27 05:00:04 +02:00
|
|
|
return str[0_char];
|
2017-06-26 15:39:17 +02:00
|
|
|
}
|
|
|
|
constexpr StringView option_type_name(Meta::Type<Codepoint>) { return "codepoint"; }
|
|
|
|
|
2015-03-30 14:33:46 +02:00
|
|
|
constexpr char list_separator = ':';
|
2013-04-02 18:56:09 +02:00
|
|
|
|
2015-01-12 14:58:41 +01:00
|
|
|
template<typename T, MemoryDomain domain>
|
|
|
|
String option_to_string(const Vector<T, domain>& opt)
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
2017-06-24 13:24:24 +02:00
|
|
|
return join(opt | transform([](const T& t) { return option_to_string(t); }),
|
|
|
|
list_separator);
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
|
|
|
|
2017-11-02 02:51:15 +01:00
|
|
|
template<typename T, MemoryDomain domain>
|
|
|
|
void option_list_postprocess(Vector<T, domain>& opt)
|
|
|
|
{}
|
|
|
|
|
2015-01-12 14:58:41 +01:00
|
|
|
template<typename T, MemoryDomain domain>
|
2018-05-27 05:00:04 +02:00
|
|
|
Vector<T, domain> option_from_string(Meta::Type<Vector<T, domain>>, StringView str)
|
2013-03-26 13:47:14 +01:00
|
|
|
{
|
2018-05-27 05:00:04 +02:00
|
|
|
auto res = str | split<StringView>(list_separator, '\\')
|
|
|
|
| transform(unescape<list_separator, '\\'>)
|
|
|
|
| transform([](auto&& s) { return option_from_string(Meta::Type<T>{}, s); })
|
|
|
|
| gather<Vector<T, domain>>();
|
|
|
|
option_list_postprocess(res);
|
|
|
|
return res;
|
2013-03-26 13:47:14 +01:00
|
|
|
}
|
|
|
|
|
2015-01-12 14:58:41 +01:00
|
|
|
template<typename T, MemoryDomain domain>
|
2015-12-12 12:26:34 +01:00
|
|
|
bool option_add(Vector<T, domain>& opt, StringView str)
|
2013-03-29 19:34:57 +01:00
|
|
|
{
|
2018-05-27 05:00:04 +02:00
|
|
|
auto vec = option_from_string(Meta::Type<Vector<T, domain>>{}, str);
|
2017-06-24 13:24:24 +02:00
|
|
|
opt.insert(opt.end(),
|
|
|
|
std::make_move_iterator(vec.begin()),
|
|
|
|
std::make_move_iterator(vec.end()));
|
2017-11-02 02:51:15 +01:00
|
|
|
option_list_postprocess(opt);
|
2013-03-29 19:34:57 +01:00
|
|
|
return not vec.empty();
|
|
|
|
}
|
|
|
|
|
2016-08-06 10:05:50 +02:00
|
|
|
template<typename T, MemoryDomain D>
|
2017-03-15 18:42:02 +01:00
|
|
|
String option_type_name(Meta::Type<Vector<T, D>>)
|
2016-08-06 10:05:50 +02:00
|
|
|
{
|
2018-01-17 23:00:54 +01:00
|
|
|
return option_type_name(Meta::Type<T>{}) + "-list"_sv;
|
2017-03-15 18:42:02 +01:00
|
|
|
}
|
2016-08-06 10:05:50 +02:00
|
|
|
|
2017-03-06 20:57:18 +01:00
|
|
|
template<typename Key, typename Value, MemoryDomain domain>
|
|
|
|
String option_to_string(const HashMap<Key, Value, domain>& opt)
|
|
|
|
{
|
|
|
|
String res;
|
2016-03-14 10:56:08 +01:00
|
|
|
for (auto it = opt.begin(); it != opt.end(); ++it)
|
2017-03-06 20:57:18 +01:00
|
|
|
{
|
2016-03-14 10:56:08 +01:00
|
|
|
if (it != opt.begin())
|
2017-03-06 20:57:18 +01:00
|
|
|
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<typename Key, typename Value, MemoryDomain domain>
|
2017-06-03 19:01:38 +02:00
|
|
|
bool option_add(HashMap<Key, Value, domain>& opt, StringView str)
|
2017-03-06 20:57:18 +01:00
|
|
|
{
|
2017-06-03 19:01:38 +02:00
|
|
|
bool changed = false;
|
2017-12-06 10:18:44 +01:00
|
|
|
for (auto&& elem : str | split<StringView>(list_separator, '\\')
|
|
|
|
| transform(unescape<list_separator, '\\'>))
|
2017-03-06 20:57:18 +01:00
|
|
|
{
|
2017-12-06 10:18:44 +01:00
|
|
|
struct error : runtime_error { error(size_t) : runtime_error{"map option expects key=value"} {} };
|
|
|
|
auto key_value = elem | split<StringView>('=', '\\')
|
|
|
|
| transform(unescape<'=', '\\'>)
|
|
|
|
| static_gather<error, 2>();
|
|
|
|
|
2018-05-27 05:00:04 +02:00
|
|
|
opt[option_from_string(Meta::Type<Key>{}, key_value[0])] = option_from_string(Meta::Type<Value>{}, key_value[1]);
|
2017-06-03 19:01:38 +02:00
|
|
|
changed = true;
|
2017-03-06 20:57:18 +01:00
|
|
|
}
|
2017-06-03 19:01:38 +02:00
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Key, typename Value, MemoryDomain domain>
|
2018-05-27 05:00:04 +02:00
|
|
|
HashMap<Key, Value, domain> option_from_string(Meta::Type<HashMap<Key, Value, domain>>, StringView str)
|
2017-06-03 19:01:38 +02:00
|
|
|
{
|
2018-05-27 05:00:04 +02:00
|
|
|
HashMap<Key, Value, domain> res;
|
|
|
|
option_add(res, str);
|
|
|
|
return res;
|
2017-03-06 20:57:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename K, typename V, MemoryDomain D>
|
2017-03-15 18:42:02 +01:00
|
|
|
String option_type_name(Meta::Type<HashMap<K, V, D>>)
|
2017-03-06 20:57:18 +01:00
|
|
|
{
|
2017-03-15 18:42:02 +01:00
|
|
|
return format("{}-to-{}-map", option_type_name(Meta::Type<K>{}),
|
|
|
|
option_type_name(Meta::Type<V>{}));
|
|
|
|
}
|
2017-03-06 20:57:18 +01:00
|
|
|
|
2015-08-23 13:13:14 +02:00
|
|
|
constexpr char tuple_separator = '|';
|
2013-03-26 13:47:14 +01:00
|
|
|
|
2018-05-27 05:00:04 +02:00
|
|
|
template<typename... Types, size_t... I>
|
|
|
|
String option_to_string_impl(const std::tuple<Types...>& opt, std::index_sequence<I...>)
|
2013-03-26 00:14:38 +01:00
|
|
|
{
|
2018-05-27 05:00:04 +02:00
|
|
|
return join(make_array({option_to_string(std::get<I>(opt))...}), tuple_separator);
|
|
|
|
}
|
2013-03-26 00:14:38 +01:00
|
|
|
|
2013-03-29 19:31:06 +01:00
|
|
|
template<typename... Types>
|
|
|
|
String option_to_string(const std::tuple<Types...>& opt)
|
|
|
|
{
|
2018-05-27 05:00:04 +02:00
|
|
|
return option_to_string_impl(opt, std::make_index_sequence<sizeof...(Types)>());
|
2013-03-29 19:31:06 +01:00
|
|
|
}
|
|
|
|
|
2018-05-27 05:00:04 +02:00
|
|
|
template<typename... Types, size_t... I>
|
|
|
|
std::tuple<Types...> option_from_string_impl(Meta::Type<std::tuple<Types...>>, StringView str,
|
|
|
|
std::index_sequence<I...>)
|
2013-03-29 19:31:06 +01:00
|
|
|
{
|
2017-12-06 10:18:44 +01:00
|
|
|
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<StringView>(tuple_separator, '\\')
|
|
|
|
| transform(unescape<tuple_separator, '\\'>)
|
|
|
|
| static_gather<error, sizeof...(Types)>();
|
2018-05-28 00:26:19 +02:00
|
|
|
return std::tuple<Types...>{option_from_string(Meta::Type<Types>{}, elems[I])...};
|
2018-05-27 05:00:04 +02:00
|
|
|
}
|
2017-12-06 10:18:44 +01:00
|
|
|
|
2018-05-27 05:00:04 +02:00
|
|
|
template<typename... Types>
|
|
|
|
std::tuple<Types...> option_from_string(Meta::Type<std::tuple<Types...>>, StringView str)
|
|
|
|
{
|
|
|
|
return option_from_string_impl(Meta::Type<std::tuple<Types...>>{}, str,
|
|
|
|
std::make_index_sequence<sizeof...(Types)>());
|
2013-03-29 19:31:06 +01:00
|
|
|
}
|
|
|
|
|
2013-05-13 14:23:07 +02:00
|
|
|
template<typename RealType, typename ValueType>
|
|
|
|
inline String option_to_string(const StronglyTypedNumber<RealType, ValueType>& opt)
|
|
|
|
{
|
|
|
|
return to_string(opt);
|
|
|
|
}
|
2013-03-29 19:31:06 +01:00
|
|
|
|
2018-05-27 05:00:04 +02:00
|
|
|
template<typename Number>
|
|
|
|
std::enable_if_t<std::is_base_of<StronglyTypedNumber<Number, int>, Number>::value, Number>
|
|
|
|
option_from_string(Meta::Type<Number>, StringView str)
|
2013-03-29 19:31:06 +01:00
|
|
|
{
|
2018-05-27 05:00:04 +02:00
|
|
|
return Number{str_to_int(str)};
|
2013-03-29 19:31:06 +01:00
|
|
|
}
|
2013-03-26 00:14:38 +01:00
|
|
|
|
2013-05-13 14:23:07 +02:00
|
|
|
template<typename RealType, typename ValueType>
|
2018-05-27 05:00:04 +02:00
|
|
|
inline bool option_add(StronglyTypedNumber<RealType, ValueType>& opt, StringView str)
|
2013-03-29 19:34:57 +01:00
|
|
|
{
|
2015-12-12 12:26:34 +01:00
|
|
|
int val = str_to_int(str);
|
2013-05-06 13:52:20 +02:00
|
|
|
opt += val;
|
|
|
|
return val != 0;
|
2013-03-29 19:34:57 +01:00
|
|
|
}
|
|
|
|
|
2016-12-30 07:43:32 +01:00
|
|
|
struct WorstMatch { template<typename T> WorstMatch(T&&) {} };
|
|
|
|
|
2017-03-17 00:08:10 +01:00
|
|
|
inline bool option_add(WorstMatch, StringView)
|
2013-03-29 19:34:57 +01:00
|
|
|
{
|
|
|
|
throw runtime_error("no add operation supported for this option type");
|
|
|
|
}
|
|
|
|
|
2017-11-13 04:27:55 +01:00
|
|
|
class Context;
|
|
|
|
|
2017-05-25 09:38:11 +02:00
|
|
|
inline void option_update(WorstMatch, const Context&)
|
|
|
|
{
|
|
|
|
throw runtime_error("no update operation supported for this option type");
|
|
|
|
}
|
|
|
|
|
2018-05-27 05:00:04 +02:00
|
|
|
template<typename Coord>
|
|
|
|
std::enable_if_t<std::is_base_of<LineAndColumn<Coord, decltype(Coord::line), decltype(Coord::column)>, Coord>::value, Coord>
|
|
|
|
option_from_string(Meta::Type<Coord>, StringView str)
|
2014-10-06 20:21:32 +02:00
|
|
|
{
|
2017-12-06 10:18:44 +01:00
|
|
|
struct error : runtime_error { error(size_t) : runtime_error{"expected <line>,<column>"} {} };
|
|
|
|
auto vals = str | split<StringView>(',')
|
|
|
|
| static_gather<error, 2>();
|
2018-05-27 05:00:04 +02:00
|
|
|
return {str_to_int(vals[0]), str_to_int(vals[1])};
|
2014-10-06 20:21:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename EffectiveType, typename LineType, typename ColumnType>
|
|
|
|
inline String option_to_string(const LineAndColumn<EffectiveType, LineType, ColumnType>& opt)
|
|
|
|
{
|
2015-08-23 13:13:14 +02:00
|
|
|
return format("{},{}", opt.line, opt.column);
|
2014-10-06 20:21:32 +02:00
|
|
|
}
|
|
|
|
|
2017-03-15 19:28:32 +01:00
|
|
|
template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))>
|
|
|
|
EnableIfWithBitOps<Flags, String> option_to_string(Flags flags)
|
|
|
|
{
|
|
|
|
constexpr auto desc = enum_desc(Meta::Type<Flags>{});
|
|
|
|
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<typename Enum, typename = decltype(enum_desc(Meta::Type<Enum>{}))>
|
|
|
|
EnableIfWithoutBitOps<Enum, String> option_to_string(Enum e)
|
|
|
|
{
|
|
|
|
constexpr auto desc = enum_desc(Meta::Type<Enum>{});
|
|
|
|
auto it = find_if(desc, [e](const EnumDesc<Enum>& d) { return d.value == e; });
|
|
|
|
if (it != desc.end())
|
|
|
|
return it->name.str();
|
|
|
|
kak_assert(false);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))>
|
2018-05-27 05:00:04 +02:00
|
|
|
EnableIfWithBitOps<Flags, Flags> option_from_string(Meta::Type<Flags>, StringView str)
|
2017-03-15 19:28:32 +01:00
|
|
|
{
|
|
|
|
constexpr auto desc = enum_desc(Meta::Type<Flags>{});
|
2018-05-27 05:00:04 +02:00
|
|
|
Flags flags{};
|
2017-03-15 19:28:32 +01:00
|
|
|
for (auto s : str | split<StringView>('|'))
|
|
|
|
{
|
|
|
|
auto it = find_if(desc, [s](const EnumDesc<Flags>& d) { return d.name == s; });
|
|
|
|
if (it == desc.end())
|
|
|
|
throw runtime_error(format("invalid flag value '{}'", s));
|
|
|
|
flags |= it->value;
|
|
|
|
}
|
2018-05-27 05:00:04 +02:00
|
|
|
return flags;
|
2017-03-15 19:28:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Enum, typename = decltype(enum_desc(Meta::Type<Enum>{}))>
|
2018-05-27 05:00:04 +02:00
|
|
|
EnableIfWithoutBitOps<Enum, Enum> option_from_string(Meta::Type<Enum>, StringView str)
|
2017-03-15 19:28:32 +01:00
|
|
|
{
|
|
|
|
constexpr auto desc = enum_desc(Meta::Type<Enum>{});
|
|
|
|
auto it = find_if(desc, [str](const EnumDesc<Enum>& d) { return d.name == str; });
|
|
|
|
if (it == desc.end())
|
|
|
|
throw runtime_error(format("invalid enum value '{}'", str));
|
2018-05-27 05:00:04 +02:00
|
|
|
return it->value;
|
2017-03-15 19:28:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))>
|
|
|
|
EnableIfWithBitOps<Flags, bool> option_add(Flags& opt, StringView str)
|
|
|
|
{
|
2018-05-27 05:00:04 +02:00
|
|
|
const Flags res = option_from_string(Meta::Type<Flags>{}, str);
|
2017-03-15 19:28:32 +01:00
|
|
|
opt |= res;
|
|
|
|
return res != (Flags)0;
|
|
|
|
}
|
|
|
|
|
2016-04-01 02:27:23 +02:00
|
|
|
template<typename P, typename T>
|
|
|
|
inline String option_to_string(const PrefixedList<P, T>& opt)
|
2015-12-12 12:45:45 +01:00
|
|
|
{
|
2017-11-13 04:28:22 +01:00
|
|
|
if (opt.list.empty())
|
|
|
|
return format("{}", escape(option_to_string(opt.prefix), list_separator, '\\'));
|
|
|
|
else
|
|
|
|
return format("{}{}{}", escape(option_to_string(opt.prefix), list_separator, '\\'),
|
|
|
|
list_separator, option_to_string(opt.list));
|
2015-12-12 12:45:45 +01:00
|
|
|
}
|
|
|
|
|
2016-04-01 02:27:23 +02:00
|
|
|
template<typename P, typename T>
|
2018-05-27 05:00:04 +02:00
|
|
|
inline PrefixedList<P, T> option_from_string(Meta::Type<PrefixedList<P, T>>, StringView str)
|
2015-12-12 12:45:45 +01:00
|
|
|
{
|
2018-05-27 05:00:04 +02:00
|
|
|
using VecType = Vector<T, MemoryDomain::Options>;
|
2017-11-13 04:28:22 +01:00
|
|
|
auto it = find(str, list_separator);
|
2018-05-27 05:00:04 +02:00
|
|
|
return {option_from_string(Meta::Type<P>{}, StringView{str.begin(), it}),
|
|
|
|
it != str.end() ? option_from_string(Meta::Type<VecType>{}, {it+1, str.end()}) : VecType{}};
|
2015-12-12 12:45:45 +01:00
|
|
|
}
|
|
|
|
|
2016-04-01 02:27:23 +02:00
|
|
|
template<typename P, typename T>
|
|
|
|
inline bool option_add(PrefixedList<P, T>& opt, StringView str)
|
2015-12-12 12:45:45 +01:00
|
|
|
{
|
|
|
|
return option_add(opt.list, str);
|
|
|
|
}
|
|
|
|
|
2013-03-26 00:14:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif // option_types_hh_INCLUDED
|