Add support for removing from options

`set -remove ...` will remove from the current option value, substracting
from int, removing elements from vectors and maps.
This commit is contained in:
Maxime Coste 2020-09-09 19:38:12 +10:00
parent 6f260c2ab2
commit ec3d7c3104
7 changed files with 128 additions and 17 deletions

View File

@ -3,6 +3,10 @@
This changelog contains major and/or breaking changes to Kakoune between This changelog contains major and/or breaking changes to Kakoune between
released versions. released versions.
== Development version
* `set-option -remove` support for substracting/removing from option values
== Kakoune 2020.09.01 == Kakoune 2020.09.01
* The `repl` and `send-text` aliases have been renamed respectively into * The `repl` and `send-text` aliases have been renamed respectively into

View File

@ -10,7 +10,7 @@ scripts.
Options can be modified using the `set-option` command: Options can be modified using the `set-option` command:
-------------------------------------------- --------------------------------------------
set-option [-add] <scope> <name> <values>... set-option [-add|-remove] <scope> <name> <values>...
-------------------------------------------- --------------------------------------------
<scope> can be *global*, *buffer*, *window* or *current* (See <scope> can be *global*, *buffer*, *window* or *current* (See
@ -20,8 +20,9 @@ which the option is already set.
Multiple <values> can be given as separate arguments when the option is a Multiple <values> can be given as separate arguments when the option is a
list or map. list or map.
If `-add` is specified, the new value is *added* to the current one If `-add` or `-remove` is specified, the new value is respectively *added*
instead of replacing it (the exact outcome depends on the type, see below). to or *removed* from the current one instead of replacing it (the exact
outcome depends on the type, see below).
[[unset-option]] [[unset-option]]
Options values can be unset in a specific scope with the `unset-option` Options values can be unset in a specific scope with the `unset-option`
@ -63,7 +64,9 @@ are exclusively available to built-in options.
*int*:: *int*::
an integer number. an integer number.
`set -add` performs a math addition
`set -add` performs a math addition. +
`set -remove` performs a math substraction. +
*bool*:: *bool*::
a boolean value, yes/true or no/false a boolean value, yes/true or no/false
@ -81,7 +84,10 @@ are exclusively available to built-in options.
*<type>-list*:: *<type>-list*::
a list, elements are specified as separate arguments to the command. a list, elements are specified as separate arguments to the command.
`set -add` appends the new element to the list
`set -add` appends the new element to the list. +
`set -remove` removes each given element from the list. +
Only `int-list` and `str-list` options can be created with Only `int-list` and `str-list` options can be created with
`declare-option`. `declare-option`.
@ -115,7 +121,8 @@ are exclusively available to built-in options.
its ranges get updated according to all the buffer modifications its ranges get updated according to all the buffer modifications
that happened since its timestamp. that happened since its timestamp.
`set -add` appends the new pair to the list `set -add` appends the new pairs to the list. +
`set -remove` removes the given pairs from the list. +
See <<highlighters#specs-highlighters,`:doc highlighters specs-highlighters`>>) See <<highlighters#specs-highlighters,`:doc highlighters specs-highlighters`>>)
@ -126,7 +133,9 @@ are exclusively available to built-in options.
type, its lines get updated according to all the buffer modifications type, its lines get updated according to all the buffer modifications
that happened since its timestamp. that happened since its timestamp.
See <<highlighters#specs-highlighters,`:doc highlighters specs-highlighters`>>) See <<highlighters#specs-highlighters,`:doc highlighters specs-highlighters`>>)
`set -add` appends the new spec to the list
`set -add` appends the new specs to the list. +
`set -remove` removes the given specs from the list. +
*completions*:: *completions*::
a list of `<text>|<select cmd>|<menu text>` candidates, a list of `<text>|<select cmd>|<menu text>` candidates,
@ -141,7 +150,8 @@ are exclusively available to built-in options.
Markup can be used in the menu text. Markup can be used in the menu text.
(see <<faces#markup-strings,`:doc faces markup-strings`>>) (see <<faces#markup-strings,`:doc faces markup-strings`>>)
`set -add` adds a new completion to the list `set -add` adds given completions to the list. +
`set -remove` removes given completions from the list. +
*enum(value1|value2|...)*:: *enum(value1|value2|...)*::
an enum, taking one of the given values an enum, taking one of the given values
@ -150,13 +160,21 @@ are exclusively available to built-in options.
*flags(value1|value2|...)*:: *flags(value1|value2|...)*::
a set of flags, taking a combination of the given values joined by a a set of flags, taking a combination of the given values joined by a
'|' character. '|' character.
`set -add` adds the new flag to the combination
`set -add` adds the given flags to the combination. +
`set -remove` removes the given flags to the combination. +
Cannot be used with `declare-option` Cannot be used with `declare-option`
*<type>-to-<type>-map*:: *<type>-to-<type>-map*::
a list of `key=value` pairs. a list of `key=value` pairs.
`set -add` adds the new pair to the hashmap or replace an already
existing key. `set -add` adds the given pair to the hashmap or replace an already
existing key. +
`set -remove` removes the given pair from the hashmap, if only the
key is provided it removes that entry regardless of the associated
value. +
Only `str-to-str-map` options can be created with `declare-option`. Only `str-to-str-map` options can be created with `declare-option`.
== Builtin options == Builtin options

View File

@ -1539,7 +1539,8 @@ const CommandDesc set_option_cmd = {
"<scope> can be global, buffer, window, or current which refers to the narrowest " "<scope> can be global, buffer, window, or current which refers to the narrowest "
"scope the option is set in", "scope the option is set in",
ParameterDesc{ 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 ParameterDesc::Flags::SwitchesOnlyAtStart, 2, (size_t)-1
}, },
CommandFlags::None, CommandFlags::None,
@ -1571,9 +1572,16 @@ const CommandDesc set_option_cmd = {
}, },
[](const ParametersParser& parser, Context& context, const ShellContext&) [](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]); 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)); opt.add_from_strings(parser.positionals_from(2));
else if (remove)
opt.remove_from_strings(parser.positionals_from(2));
else else
opt.set_from_strings(parser.positionals_from(2)); opt.set_from_strings(parser.positionals_from(2));
} }

View File

@ -44,6 +44,9 @@ struct {
unsigned int version; unsigned int version;
StringView notes; StringView notes;
} constexpr version_notes[] = { { } constexpr version_notes[] = { {
0,
"» {+u}set-option -remove{} support\n"
}, {
20200901, 20200901,
"» daemon mode does not fork anymore\n" "» daemon mode does not fork anymore\n"
}, { }, {

View File

@ -43,6 +43,15 @@ option_add_from_strings(T& opt, ConstArrayView<String> strs)
return option_add(opt, strs[0]); 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> template<typename P, typename T>
struct PrefixedList struct PrefixedList
{ {

View File

@ -58,6 +58,7 @@ public:
virtual String get_desc_string() const = 0; virtual String get_desc_string() const = 0;
virtual void set_from_strings(ConstArrayView<String> strs) = 0; virtual void set_from_strings(ConstArrayView<String> strs) = 0;
virtual void add_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 void update(const Context& context) = 0;
virtual bool has_same_value(const Option& other) const = 0; virtual bool has_same_value(const Option& other) const = 0;
@ -174,6 +175,12 @@ public:
m_manager.on_option_changed(*this); 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 void update(const Context& context) override
{ {
option_update(m_value, context); option_update(m_value, context);

View File

@ -48,6 +48,12 @@ inline bool option_add(int& opt, StringView str)
opt += val; opt += val;
return val != 0; 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"; } constexpr StringView option_type_name(Meta::Type<int>) { return "int"; }
inline String option_to_string(size_t opt) { return to_string(opt); } 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(); 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> template<typename T, MemoryDomain D>
String option_type_name(Meta::Type<Vector<T, 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> template<typename Key, typename Value, MemoryDomain domain>
bool option_add_from_strings(HashMap<Key, Value, domain>& opt, ConstArrayView<String> strs) 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; bool changed = false;
for (auto&& str : strs) for (auto&& str : strs)
{ {
struct error : runtime_error { error(size_t) : runtime_error{"map option expects key=value"} {} };
auto key_value = str | split<StringView>('=', '\\') auto key_value = str | split<StringView>('=', '\\')
| transform(unescape<'=', '\\'>) | transform(unescape<'=', '\\'>)
| static_gather<error, 2>(); | static_gather<error, 2>();
@ -154,6 +176,27 @@ bool option_add_from_strings(HashMap<Key, Value, domain>& opt, ConstArrayView<St
return changed; return changed;
} }
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> 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> option_from_strings(Meta::Type<HashMap<Key, Value, domain>>, ConstArrayView<String> str)
{ {
@ -234,6 +277,11 @@ inline bool option_add(WorstMatch, StringView)
throw runtime_error("no add operation supported for this option type"); 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; class Context;
inline void option_update(WorstMatch, const 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>{}))> template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))>
EnableIfWithBitOps<Flags, bool> option_add(Flags& opt, StringView str) EnableIfWithBitOps<Flags, bool> option_add(Flags& opt, StringView str)
{ {
const Flags res = option_from_string(Meta::Type<Flags>{}, str); const Flags old = opt;
opt |= res; opt |= option_from_string(Meta::Type<Flags>{}, str);
return res != (Flags)0; 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> 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); 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 #endif // option_types_hh_INCLUDED