diff options
| author | Maxime Coste <mawww@kakoune.org> | 2020-09-09 19:38:12 +1000 |
|---|---|---|
| committer | Maxime Coste <mawww@kakoune.org> | 2020-09-09 21:00:30 +1000 |
| commit | ec3d7c31040afaee65afde15832d9b0ed6b2224d (patch) | |
| tree | 3deb13723ff798c114a67315755fa2f42b155e95 /src | |
| parent | 6f260c2ab2f118e7eaa3a9b8334ee63dad27bf2c (diff) | |
Add support for removing from options
`set -remove ...` will remove from the current option value, substracting
from int, removing elements from vectors and maps.
Diffstat (limited to 'src')
| -rw-r--r-- | src/commands.cc | 12 | ||||
| -rw-r--r-- | src/main.cc | 3 | ||||
| -rw-r--r-- | src/option.hh | 9 | ||||
| -rw-r--r-- | src/option_manager.hh | 7 | ||||
| -rw-r--r-- | src/option_types.hh | 70 |
5 files changed, 95 insertions, 6 deletions
diff --git a/src/commands.cc b/src/commands.cc index 1e421aa4..7ca5a8ab 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -1539,7 +1539,8 @@ const CommandDesc set_option_cmd = { "<scope> can be global, buffer, window, or current which refers to the narrowest " "scope the option is set in", ParameterDesc{ - { { "add", { false, "add to option rather than replacing it" } } }, + { { "add", { false, "add to option rather than replacing it" } }, + { "remove", { false, "remove from option rather than replacing it" } } }, ParameterDesc::Flags::SwitchesOnlyAtStart, 2, (size_t)-1 }, CommandFlags::None, @@ -1571,9 +1572,16 @@ const CommandDesc set_option_cmd = { }, [](const ParametersParser& parser, Context& context, const ShellContext&) { + bool add = (bool)parser.get_switch("add"); + bool remove = (bool)parser.get_switch("remove"); + if (add and remove) + throw runtime_error("cannot add and remove at the same time"); + Option& opt = get_options(parser[0], context, parser[1]).get_local_option(parser[1]); - if (parser.get_switch("add")) + if (add) opt.add_from_strings(parser.positionals_from(2)); + else if (remove) + opt.remove_from_strings(parser.positionals_from(2)); else opt.set_from_strings(parser.positionals_from(2)); } diff --git a/src/main.cc b/src/main.cc index e822daa7..af3d68ba 100644 --- a/src/main.cc +++ b/src/main.cc @@ -44,6 +44,9 @@ struct { unsigned int version; StringView notes; } constexpr version_notes[] = { { + 0, + "» {+u}set-option -remove{} support\n" + }, { 20200901, "» daemon mode does not fork anymore\n" }, { diff --git a/src/option.hh b/src/option.hh index db8be775..a04b235c 100644 --- a/src/option.hh +++ b/src/option.hh @@ -43,6 +43,15 @@ option_add_from_strings(T& opt, ConstArrayView<String> strs) return option_add(opt, strs[0]); } +template<typename T> +decltype(option_add(std::declval<T>(), std::declval<String>())) +option_remove_from_strings(T& opt, ConstArrayView<String> strs) +{ + if (strs.size() != 1) + throw runtime_error("expected a single value for option"); + return option_remove(opt, strs[0]); +} + template<typename P, typename T> struct PrefixedList { diff --git a/src/option_manager.hh b/src/option_manager.hh index 739ec6bd..e4dc230f 100644 --- a/src/option_manager.hh +++ b/src/option_manager.hh @@ -58,6 +58,7 @@ public: virtual String get_desc_string() const = 0; virtual void set_from_strings(ConstArrayView<String> strs) = 0; virtual void add_from_strings(ConstArrayView<String> strs) = 0; + virtual void remove_from_strings(ConstArrayView<String> strs) = 0; virtual void update(const Context& context) = 0; virtual bool has_same_value(const Option& other) const = 0; @@ -174,6 +175,12 @@ public: m_manager.on_option_changed(*this); } + void remove_from_strings(ConstArrayView<String> strs) override + { + if (option_remove_from_strings(m_value, strs)) + m_manager.on_option_changed(*this); + } + void update(const Context& context) override { option_update(m_value, context); diff --git a/src/option_types.hh b/src/option_types.hh index c0f138ea..e576b295 100644 --- a/src/option_types.hh +++ b/src/option_types.hh @@ -48,6 +48,12 @@ inline bool option_add(int& opt, StringView 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<int>) { return "int"; } inline String option_to_string(size_t opt) { return to_string(opt); } @@ -110,6 +116,21 @@ bool option_add_from_strings(Vector<T, domain>& opt, ConstArrayView<String> strs return not vec.empty(); } +template<typename T, MemoryDomain domain> +bool option_remove_from_strings(Vector<T, domain>& opt, ConstArrayView<String> strs) +{ + bool did_remove = false; + for (auto&& val : strs | transform([](auto&& s) { return option_from_string(Meta::Type<T>{}, s); })) + { + auto it = find(opt, val); + if (it == opt.end()) + continue; + opt.erase(it); + did_remove = true; + } + return did_remove; +} + template<typename T, MemoryDomain D> String option_type_name(Meta::Type<Vector<T, D>>) { @@ -140,10 +161,11 @@ String option_to_string(const HashMap<Key, Value, domain>& opt, Quoting quoting) template<typename Key, typename Value, MemoryDomain domain> bool option_add_from_strings(HashMap<Key, Value, domain>& opt, ConstArrayView<String> strs) { + struct error : runtime_error { error(size_t) : runtime_error{"map option expects key=value"} {} }; + bool changed = false; for (auto&& str : strs) { - struct error : runtime_error { error(size_t) : runtime_error{"map option expects key=value"} {} }; auto key_value = str | split<StringView>('=', '\\') | transform(unescape<'=', '\\'>) | static_gather<error, 2>(); @@ -155,6 +177,27 @@ bool option_add_from_strings(HashMap<Key, Value, domain>& opt, ConstArrayView<St } template<typename Key, typename Value, MemoryDomain domain> +bool option_remove_from_strings(HashMap<Key, Value, domain>& opt, ConstArrayView<String> 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<StringView>('=', '\\') + | transform(unescape<'=', '\\'>) + | static_gather<error, 2>(); + + 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<typename Key, typename Value, MemoryDomain domain> HashMap<Key, Value, domain> option_from_strings(Meta::Type<HashMap<Key, Value, domain>>, ConstArrayView<String> str) { HashMap<Key, Value, domain> res; @@ -234,6 +277,11 @@ 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&) @@ -312,9 +360,17 @@ EnableIfWithoutBitOps<Enum, Enum> option_from_string(Meta::Type<Enum>, StringVie template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))> EnableIfWithBitOps<Flags, bool> option_add(Flags& opt, StringView str) { - const Flags res = option_from_string(Meta::Type<Flags>{}, str); - opt |= res; - return res != (Flags)0; + const Flags old = opt; + opt |= option_from_string(Meta::Type<Flags>{}, str); + return opt != old; +} + +template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))> +EnableIfWithBitOps<Flags, bool> option_remove(Flags& opt, StringView str) +{ + const Flags old = opt; + opt &= ~option_from_string(Meta::Type<Flags>{}, str); + return opt != old; } template<typename P, typename T> @@ -348,6 +404,12 @@ inline bool option_add_from_strings(PrefixedList<P, T>& opt, ConstArrayView<Stri return option_add_from_strings(opt.list, str); } +template<typename P, typename T> +inline bool option_remove_from_strings(PrefixedList<P, T>& opt, ConstArrayView<String> str) +{ + return option_remove_from_strings(opt.list, str); +} + } #endif // option_types_hh_INCLUDED |
