Unify command parameter parsing with a ParametersParser class
This commit is contained in:
parent
05442857e6
commit
393b9b24e2
246
src/commands.cc
246
src/commands.cc
|
@ -31,6 +31,169 @@ extern GetKeyFunc get_key_func;
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct unknown_option : public runtime_error
|
||||||
|
{
|
||||||
|
unknown_option(const String& name)
|
||||||
|
: runtime_error("unknown option '" + name + "'") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct missing_option_value: public runtime_error
|
||||||
|
{
|
||||||
|
missing_option_value(const String& name)
|
||||||
|
: runtime_error("missing value for option '" + name + "'") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ParameterParser provides tools to parse command parameters.
|
||||||
|
// There are 3 types of parameters:
|
||||||
|
// * unnamed options, which are accessed by position (ignoring named ones)
|
||||||
|
// * named boolean options, which are enabled using '-name' syntax
|
||||||
|
// * named string options, which are defined using '-name value' syntax
|
||||||
|
struct ParametersParser
|
||||||
|
{
|
||||||
|
// the options defines named options, if they map to true, then
|
||||||
|
// they are understood as string options, else they are understood as
|
||||||
|
// boolean option.
|
||||||
|
ParametersParser(const CommandParameters& params,
|
||||||
|
std::unordered_map<String, bool> options)
|
||||||
|
: m_params(params), m_positional(params.size(), true), m_options(options)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < params.size(); ++i)
|
||||||
|
{
|
||||||
|
if (params[i][0] == '-')
|
||||||
|
{
|
||||||
|
auto it = options.find(params[i].substr(1));
|
||||||
|
if (it == options.end())
|
||||||
|
throw unknown_option(params[i]);
|
||||||
|
|
||||||
|
if (it->second)
|
||||||
|
{
|
||||||
|
if (i + 1 == params.size() or params[i+1][0] == '-')
|
||||||
|
throw missing_option_value(params[i]);
|
||||||
|
|
||||||
|
m_positional[i+1] = false;
|
||||||
|
}
|
||||||
|
m_positional[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all options following -- are positional
|
||||||
|
if (params[i] == "--")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a named option (either string or boolean) is specified
|
||||||
|
bool has_option(const String& name) const
|
||||||
|
{
|
||||||
|
assert(m_options.find(name) != m_options.end());
|
||||||
|
for (auto param : m_params)
|
||||||
|
{
|
||||||
|
if (param[0] == '-' and param.substr(1) == name)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (param == "--")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a string option value, returns an empty string if the option
|
||||||
|
// is not defined
|
||||||
|
const String& option_value(const String& name) const
|
||||||
|
{
|
||||||
|
auto it = m_options.find(name);
|
||||||
|
assert(it != m_options.end());
|
||||||
|
assert(it->second == true);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_params.size(); ++i)
|
||||||
|
{
|
||||||
|
if (m_params[i][0] == '-' and m_params[i].substr(1) == name)
|
||||||
|
return m_params[i+1];
|
||||||
|
|
||||||
|
if (m_params[i] == "--")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
static String empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t positional_count() const
|
||||||
|
{
|
||||||
|
size_t res = 0;
|
||||||
|
for (bool positional : m_positional)
|
||||||
|
{
|
||||||
|
if (positional)
|
||||||
|
++res;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef String value_type;
|
||||||
|
typedef const value_type* pointer;
|
||||||
|
typedef const value_type& reference;
|
||||||
|
typedef size_t difference_type;
|
||||||
|
typedef std::forward_iterator_tag iterator_category;
|
||||||
|
|
||||||
|
iterator(const ParametersParser& parser, size_t index)
|
||||||
|
: m_parser(parser), m_index(index) {}
|
||||||
|
|
||||||
|
const String& operator*() const
|
||||||
|
{
|
||||||
|
assert(m_parser.m_positional[m_index]);
|
||||||
|
return m_parser.m_params[m_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator& operator++()
|
||||||
|
{
|
||||||
|
while (m_index < m_parser.m_positional.size() and
|
||||||
|
not m_parser.m_positional[++m_index]) {}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const iterator& other) const
|
||||||
|
{
|
||||||
|
return &m_parser == &other.m_parser and m_index == other.m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const iterator& other) const
|
||||||
|
{
|
||||||
|
return &m_parser != &other.m_parser or m_index != other.m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const iterator& other) const
|
||||||
|
{
|
||||||
|
assert(&m_parser == &other.m_parser);
|
||||||
|
return m_index < other.m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const ParametersParser& m_parser;
|
||||||
|
size_t m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// positional parameter begin
|
||||||
|
iterator begin() const
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
while (index < m_positional.size() and not m_positional[index])
|
||||||
|
++index;
|
||||||
|
return iterator(*this, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// positional parameter end
|
||||||
|
iterator end() const
|
||||||
|
{
|
||||||
|
return iterator(*this, m_params.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const CommandParameters& m_params;
|
||||||
|
std::vector<bool> m_positional;
|
||||||
|
std::unordered_map<String, bool> m_options;
|
||||||
|
};
|
||||||
|
|
||||||
Buffer* open_or_create(const String& filename)
|
Buffer* open_or_create(const String& filename)
|
||||||
{
|
{
|
||||||
Buffer* buffer = NULL;
|
Buffer* buffer = NULL;
|
||||||
|
@ -139,28 +302,24 @@ void show_buffer(const CommandParameters& params, const Context& context)
|
||||||
|
|
||||||
void add_highlighter(const CommandParameters& params, const Context& context)
|
void add_highlighter(const CommandParameters& params, const Context& context)
|
||||||
{
|
{
|
||||||
if (params.size() < 1)
|
ParametersParser parser(params, { { "group", true } });
|
||||||
|
if (parser.positional_count() < 1)
|
||||||
throw wrong_argument_count();
|
throw wrong_argument_count();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HighlighterRegistry& registry = HighlighterRegistry::instance();
|
HighlighterRegistry& registry = HighlighterRegistry::instance();
|
||||||
|
|
||||||
if (params[0] == "-group")
|
auto begin = parser.begin();
|
||||||
{
|
const String& name = *begin;
|
||||||
if (params.size() < 3)
|
std::vector<String> highlighter_params(++begin, parser.end());
|
||||||
throw wrong_argument_count();
|
|
||||||
HighlighterParameters highlighter_params(params.begin()+3, params.end());
|
|
||||||
HighlighterGroup& group = context.window().highlighters().get_group(params[1]);
|
|
||||||
registry.add_highlighter_to_group(context.window(), group,
|
|
||||||
params[2], highlighter_params);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
HighlighterParameters highlighter_params(params.begin()+1, params.end());
|
|
||||||
registry.add_highlighter_to_window(context.window(),
|
|
||||||
params[0], highlighter_params);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Window& window = context.window();
|
||||||
|
HighlighterGroup& group = parser.has_option("group") ?
|
||||||
|
window.highlighters().get_group(parser.option_value("group"))
|
||||||
|
: window.highlighters();
|
||||||
|
|
||||||
|
registry.add_highlighter_to_group(window, group, name,
|
||||||
|
highlighter_params);
|
||||||
}
|
}
|
||||||
catch (runtime_error& err)
|
catch (runtime_error& err)
|
||||||
{
|
{
|
||||||
|
@ -170,23 +329,17 @@ void add_highlighter(const CommandParameters& params, const Context& context)
|
||||||
|
|
||||||
void rm_highlighter(const CommandParameters& params, const Context& context)
|
void rm_highlighter(const CommandParameters& params, const Context& context)
|
||||||
{
|
{
|
||||||
if (params.size() < 1)
|
ParametersParser parser(params, { { "group", true } });
|
||||||
|
if (parser.positional_count() != 1)
|
||||||
throw wrong_argument_count();
|
throw wrong_argument_count();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (params[0] == "-group")
|
Window& window = context.window();
|
||||||
{
|
HighlighterGroup& group = parser.has_option("group") ?
|
||||||
if (params.size() != 3)
|
window.highlighters().get_group(parser.option_value("group"))
|
||||||
throw wrong_argument_count();
|
: window.highlighters();
|
||||||
HighlighterGroup& group = context.window().highlighters().get_group(params[1]);
|
|
||||||
group.remove(params[2]);
|
group.remove(*parser.begin());
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (params.size() != 1)
|
|
||||||
throw wrong_argument_count();
|
|
||||||
context.window().highlighters().remove(params[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (runtime_error& err)
|
catch (runtime_error& err)
|
||||||
{
|
{
|
||||||
|
@ -244,14 +397,20 @@ void add_hook(const CommandParameters& params, const Context& context)
|
||||||
|
|
||||||
void define_command(const CommandParameters& params, const Context& context)
|
void define_command(const CommandParameters& params, const Context& context)
|
||||||
{
|
{
|
||||||
if (params.size() < 2)
|
ParametersParser parser(params,
|
||||||
|
{ { "env-params", false },
|
||||||
|
{ "append-params", false } });
|
||||||
|
|
||||||
|
if (parser.positional_count() < 2)
|
||||||
throw wrong_argument_count();
|
throw wrong_argument_count();
|
||||||
|
|
||||||
if (params[0] == "-env-params")
|
auto begin = parser.begin();
|
||||||
|
const String& cmd_name = *begin;
|
||||||
|
std::vector<String> cmd_params(++begin, parser.end());
|
||||||
|
Command cmd;
|
||||||
|
if (parser.has_option("env-params"))
|
||||||
{
|
{
|
||||||
std::vector<String> cmd_params(params.begin() + 2, params.end());
|
cmd = [=](const CommandParameters& params, const Context& context) {
|
||||||
CommandManager::instance().register_command(params[1],
|
|
||||||
[=](const CommandParameters& params, const Context& context) {
|
|
||||||
char param_name[] = "kak_param0";
|
char param_name[] = "kak_param0";
|
||||||
for (size_t i = 0; i < 10; ++i)
|
for (size_t i = 0; i < 10; ++i)
|
||||||
{
|
{
|
||||||
|
@ -262,29 +421,26 @@ void define_command(const CommandParameters& params, const Context& context)
|
||||||
unsetenv(param_name);
|
unsetenv(param_name);
|
||||||
}
|
}
|
||||||
CommandManager::instance().execute(cmd_params, context);
|
CommandManager::instance().execute(cmd_params, context);
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
else if (params[0] == "-append-params")
|
else if (parser.has_option("append-params"))
|
||||||
{
|
{
|
||||||
std::vector<String> cmd_params(params.begin() + 2, params.end());
|
cmd = [=](const CommandParameters& params, const Context& context) {
|
||||||
CommandManager::instance().register_command(params[1],
|
|
||||||
[=](const CommandParameters& params, const Context& context) {
|
|
||||||
std::vector<String> merged_params = cmd_params;
|
std::vector<String> merged_params = cmd_params;
|
||||||
for (auto& param : params)
|
for (auto& param : params)
|
||||||
merged_params.push_back(param);
|
merged_params.push_back(param);
|
||||||
CommandManager::instance().execute(merged_params, context);
|
CommandManager::instance().execute(merged_params, context);
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::vector<String> cmd_params(params.begin() + 1, params.end());
|
cmd = [=](const CommandParameters& params, const Context& context) {
|
||||||
CommandManager::instance().register_command(params[0],
|
|
||||||
[=](const CommandParameters& params, const Context& context) {
|
|
||||||
if (not params.empty())
|
if (not params.empty())
|
||||||
throw wrong_argument_count();
|
throw wrong_argument_count();
|
||||||
CommandManager::instance().execute(cmd_params, context);
|
CommandManager::instance().execute(cmd_params, context);
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
CommandManager::instance().register_command(cmd_name, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void echo_message(const CommandParameters& params, const Context& context)
|
void echo_message(const CommandParameters& params, const Context& context)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user