summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command_manager.cc56
-rw-r--r--src/commands.cc18
-rw-r--r--src/completion.hh7
-rw-r--r--src/option.hh27
-rw-r--r--src/option_manager.hh23
-rw-r--r--src/option_types.cc19
-rw-r--r--src/option_types.hh94
-rw-r--r--src/parameters_parser.hh7
8 files changed, 167 insertions, 84 deletions
diff --git a/src/command_manager.cc b/src/command_manager.cc
index dd904bf4..c3dfc7c4 100644
--- a/src/command_manager.cc
+++ b/src/command_manager.cc
@@ -253,9 +253,31 @@ Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
}
}
-String expand_token(const Token& token, const Context& context,
- const ShellContext& shell_context)
+auto expand_option(Option& opt, std::true_type)
{
+ return opt.get_as_string();
+}
+
+auto expand_option(Option& opt, std::false_type)
+{
+ return opt.get_as_strings();
+}
+
+String expand_arobase(ConstArrayView<String> params, std::true_type)
+{
+ return join(params, ' ', false);
+}
+
+Vector<String> expand_arobase(ConstArrayView<String> params, std::false_type)
+{
+ return {params.begin(), params.end()};
+}
+
+template<bool single>
+std::conditional_t<single, String, Vector<String>>
+expand_token(const Token& token, const Context& context, const ShellContext& shell_context)
+{
+ using IsSingle = std::integral_constant<bool, single>;
auto& content = token.content;
switch (token.type)
{
@@ -273,35 +295,35 @@ String expand_token(const Token& token, const Context& context,
++trailing_eol_count;
}
str.resize(str.length() - trailing_eol_count, 0);
- return str;
+ return {str};
}
case Token::Type::RegisterExpand:
- return context.main_sel_register_value(content).str();
+ return {context.main_sel_register_value(content).str()};
case Token::Type::OptionExpand:
- return context.options()[content].get_as_string();
+ return expand_option(context.options()[content], IsSingle{});
case Token::Type::ValExpand:
{
auto it = shell_context.env_vars.find(content);
if (it != shell_context.env_vars.end())
- return it->value;
- return ShellManager::instance().get_val(content, context);
+ return {it->value};
+ return {ShellManager::instance().get_val(content, context)};
}
case Token::Type::ArgExpand:
{
auto& params = shell_context.params;
if (content == '@')
- return join(params, ' ');
+ return expand_arobase(params, IsSingle{});
const int arg = str_to_int(content)-1;
if (arg < 0)
throw runtime_error("invalid argument index");
- return arg < params.size() ? params[arg] : String{};
+ return {arg < params.size() ? params[arg] : String{}};
}
case Token::Type::RawEval:
- return expand(content, context, shell_context);
+ return {expand(content, context, shell_context)};
case Token::Type::Raw:
case Token::Type::RawQuoted:
- return content;
+ return {content};
default: kak_assert(false);
}
return {};
@@ -378,8 +400,7 @@ String expand_impl(StringView str, const Context& context,
else
{
res += reader.substr_from(beg);
- res += postprocess(expand_token(parse_percent_token(reader, true),
- context, shell_context));
+ res += postprocess(expand_token<true>(parse_percent_token(reader, true), context, shell_context));
beg = reader.pos;
}
}
@@ -483,7 +504,12 @@ void CommandManager::execute(StringView command_line,
params.insert(params.end(), shell_context.params.begin(),
shell_context.params.end());
else
- params.push_back(expand_token(token, context, shell_context));
+ {
+ auto tokens = expand_token<false>(token, context, shell_context);
+ params.insert(params.end(),
+ std::make_move_iterator(tokens.begin()),
+ std::make_move_iterator(tokens.end()));
+ }
}
execute_single_command(params, context, shell_context, command_coord);
}
@@ -640,7 +666,7 @@ Completions CommandManager::complete(const Context& context,
context, flags, params, tokens.size() - 2,
cursor_pos_in_token), start);
- if (token.type != Token::Type::RawQuoted)
+ if (not completions.quoted and token.type != Token::Type::RawQuoted)
{
StringView to_escape = token.type == Token::Type::Raw ? "% \t;" : "%";
for (auto& candidate : completions.candidates)
diff --git a/src/commands.cc b/src/commands.cc
index 8323aa56..7c840fb7 100644
--- a/src/commands.cc
+++ b/src/commands.cc
@@ -1318,7 +1318,7 @@ const CommandDesc set_option_cmd = {
"scope the option is set in",
ParameterDesc{
{ { "add", { false, "add to option rather than replacing it" } } },
- ParameterDesc::Flags::SwitchesOnlyAtStart, 3, 3
+ ParameterDesc::Flags::SwitchesOnlyAtStart, 2, (size_t)-1
},
CommandFlags::None,
option_doc_helper,
@@ -1337,13 +1337,11 @@ const CommandDesc set_option_cmd = {
else if (token_to_complete == start + 1)
return { 0_byte, params[start + 1].length(),
GlobalScope::instance().option_registry().complete_option_name(params[start + 1], pos_in_token) };
- else if (not add and token_to_complete == start + 2 and
+ else if (not add and token_to_complete == start + 2 and params[start + 2].empty() and
GlobalScope::instance().option_registry().option_exists(params[start + 1]))
{
OptionManager& options = get_scope(params[start], context).options();
- String val = options[params[start + 1]].get_as_string();
- if (prefix_match(val, params[start + 2]))
- return { 0_byte, params[start + 2].length(), { std::move(val) } };
+ return { 0_byte, params[start + 2].length(), { options[params[start + 1]].get_as_string() }, true };
}
return Completions{};
},
@@ -1351,9 +1349,9 @@ const CommandDesc set_option_cmd = {
{
Option& opt = get_options(parser[0], context, parser[1]).get_local_option(parser[1]);
if (parser.get_switch("add"))
- opt.add_from_string(parser[2]);
+ opt.add_from_strings(parser.positionals_from(2));
else
- opt.set_from_string(parser[2]);
+ opt.set_from_strings(parser.positionals_from(2));
}
};
@@ -1427,7 +1425,7 @@ const CommandDesc declare_option_cmd = {
ParameterDesc{
{ { "hidden", { false, "do not display option name when completing" } },
{ "docstring", { true, "specify option description" } } },
- ParameterDesc::Flags::SwitchesOnlyAtStart, 2, 3
+ ParameterDesc::Flags::SwitchesOnlyAtStart, 2, (size_t)-1
},
CommandFlags::None,
CommandHelper{},
@@ -1470,8 +1468,8 @@ const CommandDesc declare_option_cmd = {
else
throw runtime_error(format("no such option type: '{}'", parser[0]));
- if (parser.positional_count() == 3)
- opt->set_from_string(parser[2]);
+ if (parser.positional_count() > 2)
+ opt->set_from_strings(parser.positionals_from(2));
}
};
diff --git a/src/completion.hh b/src/completion.hh
index 19275824..553b37e7 100644
--- a/src/completion.hh
+++ b/src/completion.hh
@@ -21,6 +21,7 @@ struct Completions
CandidateList candidates;
ByteCount start;
ByteCount end;
+ bool quoted = false;
Completions()
: start(0), end(0) {}
@@ -28,8 +29,8 @@ struct Completions
Completions(ByteCount start, ByteCount end)
: start(start), end(end) {}
- Completions(ByteCount start, ByteCount end, CandidateList candidates)
- : candidates(std::move(candidates)), start(start), end(end) {}
+ Completions(ByteCount start, ByteCount end, CandidateList candidates, bool quoted = false)
+ : candidates(std::move(candidates)), start(start), end(end), quoted{quoted} {}
};
enum class CompletionFlags
@@ -56,7 +57,7 @@ Completions shell_complete(const Context& context, CompletionFlags,
inline Completions offset_pos(Completions completion, ByteCount offset)
{
return { completion.start + offset, completion.end + offset,
- std::move(completion.candidates) };
+ std::move(completion.candidates), completion.quoted };
}
template<typename Container>
diff --git a/src/option.hh b/src/option.hh
index f4aafe3a..19ca709f 100644
--- a/src/option.hh
+++ b/src/option.hh
@@ -16,6 +16,33 @@ inline String option_to_string(int opt);
inline String option_to_string(size_t opt);
inline String option_to_string(bool opt);
+// Default fallback to single value functions
+template<typename T>
+decltype(option_from_string(Meta::Type<T>{}, StringView{}))
+option_from_strings(Meta::Type<T>, ConstArrayView<String> strs)
+{
+ if (strs.size() != 1)
+ throw runtime_error("expected a single value for option");
+ return option_from_string(Meta::Type<T>{}, strs[0]);
+}
+
+template<typename T>
+Vector<decltype(option_to_string(std::declval<T>()))>
+option_to_strings(const T& opt)
+{
+ return Vector<String>{option_to_string(opt)};
+}
+
+template<typename T>
+decltype(option_add(std::declval<T>(), std::declval<String>()))
+option_add_from_strings(T& opt, ConstArrayView<String> strs)
+{
+ if (strs.size() != 1)
+ throw runtime_error("expected a single value for option");
+ return option_add(opt, strs[0]);
+}
+
+
template<typename P, typename T>
struct PrefixedList
{
diff --git a/src/option_manager.hh b/src/option_manager.hh
index b1ecf4ef..b7de0e7e 100644
--- a/src/option_manager.hh
+++ b/src/option_manager.hh
@@ -54,9 +54,10 @@ public:
template<typename T> bool is_of_type() const;
virtual String get_as_string() const = 0;
- virtual void set_from_string(StringView str) = 0;
- virtual void add_from_string(StringView str) = 0;
- virtual void update(const Context& context) = 0;
+ virtual Vector<String> get_as_strings() const = 0;
+ virtual void set_from_strings(ConstArrayView<String> strs) = 0;
+ virtual void add_from_strings(ConstArrayView<String> strs) = 0;
+ virtual void update(const Context& context) = 0;
virtual Option* clone(OptionManager& manager) const = 0;
OptionManager& manager() const { return m_manager; }
@@ -141,19 +142,27 @@ public:
const T& get() const { return m_value; }
T& get_mutable() { return m_value; }
+ Vector<String> get_as_strings() const override
+ {
+ return option_to_strings(m_value);
+ }
+
String get_as_string() const override
{
return option_to_string(m_value);
}
- void set_from_string(StringView str) override
+
+ void set_from_strings(ConstArrayView<String> strs) override
{
- set(option_from_string(Meta::Type<T>{}, str));
+ set(option_from_strings(Meta::Type<T>{}, strs));
}
- void add_from_string(StringView str) override
+
+ void add_from_strings(ConstArrayView<String> strs) override
{
- if (option_add(m_value, str))
+ if (option_add_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.cc b/src/option_types.cc
index f32e361f..5cd4371c 100644
--- a/src/option_types.cc
+++ b/src/option_types.cc
@@ -5,19 +5,20 @@ namespace Kakoune
{
UnitTest test_option_parsing{[]{
- auto check = [](auto&& value, StringView str)
+ auto check = [](auto&& value, ConstArrayView<String> strs)
{
- auto repr = option_to_string(value);
- kak_assert(repr == str);
- auto parsed = option_from_string(Meta::Type<std::decay_t<decltype(value)>>{}, str);
+ auto repr = option_to_strings(value);
+ kak_assert(strs == ConstArrayView<String>{repr});
+ auto parsed = option_from_strings(Meta::Type<std::decay_t<decltype(value)>>{}, strs);
kak_assert(parsed == value);
};
- check(123, "123");
- check(true, "true");
- check(Vector<String>{"foo", "bar:", "baz"}, "foo:bar\\::baz");
- check(HashMap<String, int>{{"foo", 10}, {"b=r", 20}, {"b:z", 30}}, "foo=10:b\\=r=20:b\\:z=30");
- check(DebugFlags::Keys | DebugFlags::Hooks, "hooks|keys");
+ check(123, {"123"});
+ check(true, {"true"});
+ check(Vector<String>{"foo", "bar:", "baz"}, {"foo", "bar:", "baz"});
+ check(Vector<int>{10, 20, 30}, {"10", "20", "30"});
+ check(HashMap<String, int>{{"foo", 10}, {"b=r", 20}, {"b:z", 30}}, {"foo=10", "b\\=r=20", "b:z=30"});
+ check(DebugFlags::Keys | DebugFlags::Hooks, {"hooks|keys"});
}};
}
diff --git a/src/option_types.hh b/src/option_types.hh
index 889dedcc..886008b7 100644
--- a/src/option_types.hh
+++ b/src/option_types.hh
@@ -18,6 +18,12 @@
namespace Kakoune
{
+
+inline String quote(StringView s)
+{
+ return format("'{}'", replace(s, "'", "''"));
+}
+
template<typename T>
constexpr decltype(T::option_type_name) option_type_name(Meta::Type<T>)
{
@@ -67,13 +73,16 @@ inline Codepoint option_from_string(Meta::Type<Codepoint>, StringView str)
}
constexpr StringView option_type_name(Meta::Type<Codepoint>) { return "codepoint"; }
-constexpr char list_separator = ':';
+template<typename T, MemoryDomain domain>
+Vector<String> option_to_strings(const Vector<T, domain>& opt)
+{
+ return opt | transform([](const T& t) { return option_to_string(t); }) | gather<Vector<String>>();
+}
template<typename T, MemoryDomain domain>
String option_to_string(const Vector<T, domain>& opt)
{
- return join(opt | transform([](const T& t) { return option_to_string(t); }),
- list_separator);
+ return join(opt | transform([](const T& t) { return quote(option_to_string(t)); }), ' ', false);
}
template<typename T, MemoryDomain domain>
@@ -81,20 +90,18 @@ void option_list_postprocess(Vector<T, domain>& opt)
{}
template<typename T, MemoryDomain domain>
-Vector<T, domain> option_from_string(Meta::Type<Vector<T, domain>>, StringView str)
+Vector<T, domain> option_from_strings(Meta::Type<Vector<T, domain>>, ConstArrayView<String> strs)
{
- 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>>();
+ auto res = strs | transform([](auto&& s) { return option_from_string(Meta::Type<T>{}, s); })
+ | gather<Vector<T, domain>>();
option_list_postprocess(res);
return res;
}
template<typename T, MemoryDomain domain>
-bool option_add(Vector<T, domain>& opt, StringView str)
+bool option_add_from_strings(Vector<T, domain>& opt, ConstArrayView<String> strs)
{
- auto vec = option_from_string(Meta::Type<Vector<T, domain>>{}, str);
+ auto vec = option_from_strings(Meta::Type<Vector<T, domain>>{}, strs);
opt.insert(opt.end(),
std::make_move_iterator(vec.begin()),
std::make_move_iterator(vec.end()));
@@ -109,31 +116,35 @@ String option_type_name(Meta::Type<Vector<T, D>>)
}
template<typename Key, typename Value, MemoryDomain domain>
+Vector<String> option_to_strings(const HashMap<Key, Value, domain>& opt)
+{
+ return opt | transform([](auto&& item) {
+ return format("{}={}",
+ escape(option_to_string(item.key), '=', '\\'),
+ escape(option_to_string(item.value), '=', '\\'));
+ }) | gather<Vector<String>>();
+}
+
+template<typename Key, typename Value, MemoryDomain domain>
String option_to_string(const HashMap<Key, Value, domain>& opt)
{
- String res;
- for (auto it = opt.begin(); it != opt.end(); ++it)
- {
- if (it != opt.begin())
- res += list_separator;
- String elem = escape(option_to_string(it->key), '=', '\\') + "=" +
- escape(option_to_string(it->value), '=', '\\');
- res += escape(elem, list_separator, '\\');
- }
- return res;
+ return join(opt | transform([](auto&& item) {
+ return quote(format("{}={}",
+ escape(option_to_string(item.key), '=', '\\'),
+ escape(option_to_string(item.value), '=', '\\')));
+ }), ' ', false);
}
template<typename Key, typename Value, MemoryDomain domain>
-bool option_add(HashMap<Key, Value, domain>& opt, StringView str)
+bool option_add_from_strings(HashMap<Key, Value, domain>& opt, ConstArrayView<String> strs)
{
bool changed = false;
- for (auto&& elem : str | split<StringView>(list_separator, '\\')
- | transform(unescape<list_separator, '\\'>))
+ for (auto&& str : strs)
{
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>();
+ auto key_value = str | split<StringView>('=', '\\')
+ | transform(unescape<'=', '\\'>)
+ | static_gather<error, 2>();
opt[option_from_string(Meta::Type<Key>{}, key_value[0])] = option_from_string(Meta::Type<Value>{}, key_value[1]);
changed = true;
@@ -142,10 +153,10 @@ bool option_add(HashMap<Key, Value, domain>& opt, StringView str)
}
template<typename Key, typename Value, MemoryDomain domain>
-HashMap<Key, Value, domain> option_from_string(Meta::Type<HashMap<Key, Value, domain>>, StringView str)
+HashMap<Key, Value, domain> option_from_strings(Meta::Type<HashMap<Key, Value, domain>>, ConstArrayView<String> str)
{
HashMap<Key, Value, domain> res;
- option_add(res, str);
+ option_add_from_strings(res, str);
return res;
}
@@ -305,28 +316,31 @@ EnableIfWithBitOps<Flags, bool> option_add(Flags& opt, StringView str)
}
template<typename P, typename T>
+inline Vector<String> option_to_strings(const PrefixedList<P, T>& opt)
+{
+ Vector<String> res{option_to_string(opt.prefix)};
+ 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<typename P, typename T>
inline String option_to_string(const PrefixedList<P, T>& opt)
{
- 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));
+ return option_to_string(opt.prefix) + " " + option_to_string(opt.list);
}
template<typename P, typename T>
-inline PrefixedList<P, T> option_from_string(Meta::Type<PrefixedList<P, T>>, StringView str)
+inline PrefixedList<P, T> option_from_strings(Meta::Type<PrefixedList<P, T>>, ConstArrayView<String> strs)
{
- using VecType = Vector<T, MemoryDomain::Options>;
- auto it = find(str, list_separator);
- 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{}};
+ return {option_from_string(Meta::Type<P>{}, strs[0]),
+ option_from_strings(Meta::Type<Vector<T, MemoryDomain::Options>>{}, strs.subrange(1))};
}
template<typename P, typename T>
-inline bool option_add(PrefixedList<P, T>& opt, StringView str)
+inline bool option_add_from_strings(PrefixedList<P, T>& opt, ConstArrayView<String> str)
{
- return option_add(opt.list, str);
+ return option_add_from_strings(opt.list, str);
}
}
diff --git a/src/parameters_parser.hh b/src/parameters_parser.hh
index 8cbaf0fd..0f9f83dc 100644
--- a/src/parameters_parser.hh
+++ b/src/parameters_parser.hh
@@ -6,6 +6,7 @@
#include "meta.hh"
#include "array_view.hh"
#include "optional.hh"
+#include "flags.hh"
#include "string.hh"
#include "string_utils.hh"
@@ -115,6 +116,12 @@ struct ParametersParser
return m_params[m_positional_indices[index]];
}
+ ConstArrayView<String> positionals_from(size_t first) const
+ {
+ kak_assert(m_desc.flags & (ParameterDesc::Flags::SwitchesOnlyAtStart | ParameterDesc::Flags::SwitchesAsPositional));
+ return m_params.subrange(m_positional_indices[first]);
+ }
+
iterator begin() const { return iterator(*this, 0); }
iterator end() const { return iterator(*this, m_positional_indices.size()); }