diff --git a/src/commands.cc b/src/commands.cc index 62d9f1e0..507ec0c3 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -17,6 +17,7 @@ #include "event_manager.hh" #include "color_registry.hh" #include "client_manager.hh" +#include "parameters_parser.hh" #include #include @@ -38,188 +39,6 @@ struct wrong_argument_count : runtime_error wrong_argument_count() : runtime_error("wrong argument count") {} }; -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 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_byte)); - 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_byte) == 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_byte) == 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]; - } - - 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()); - } - - // access positional parameter by index - const String& operator[] (size_t index) const - { - assert(index < positional_count()); - iterator it = begin(); - while (index) - { - ++it; - --index; - } - return *it; - } - -private: - const CommandParameters& m_params; - std::vector m_positional; - std::unordered_map m_options; -}; - Buffer* open_or_create(const String& filename, Context& context) { Buffer* buffer = create_buffer_from_file(filename); diff --git a/src/parameters_parser.cc b/src/parameters_parser.cc new file mode 100644 index 00000000..cdcfb0c1 --- /dev/null +++ b/src/parameters_parser.cc @@ -0,0 +1,103 @@ +#include "parameters_parser.hh" + +namespace Kakoune +{ + +ParametersParser::ParametersParser(const ParameterList& params, + std::unordered_map options) + : m_params(params), m_positional(params.size(), true), + m_options(std::move(options)) +{ + for (size_t i = 0; i < params.size(); ++i) + { + if (params[i][0] == '-') + { + auto it = m_options.find(params[i].substr(1_byte)); + if (it == m_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; + } +} + +bool ParametersParser::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_byte) == name) + return true; + + if (param == "--") + break; + } + return false; +} + +const String& ParametersParser::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_byte) == name) + return m_params[i+1]; + + if (m_params[i] == "--") + break; + } + static String empty; + return empty; +} + +size_t ParametersParser::positional_count() const +{ + size_t res = 0; + for (bool positional : m_positional) + { + if (positional) + ++res; + } + return res; +} + +const String& ParametersParser::operator[] (size_t index) const +{ + assert(index < positional_count()); + iterator it = begin(); + while (index) + { + ++it; + --index; + } + return *it; +} + +ParametersParser::iterator ParametersParser::begin() const +{ + int index = 0; + while (index < m_positional.size() and not m_positional[index]) + ++index; + return iterator(*this, index); +} + +ParametersParser::iterator ParametersParser::end() const +{ + return iterator(*this, m_params.size()); +} + +} diff --git a/src/parameters_parser.hh b/src/parameters_parser.hh new file mode 100644 index 00000000..2ba77c76 --- /dev/null +++ b/src/parameters_parser.hh @@ -0,0 +1,118 @@ +#ifndef parameters_parser_hh_INCLUDED +#define parameters_parser_hh_INCLUDED + +#include "string.hh" +#include "memoryview.hh" +#include "exception.hh" + +#include + +namespace Kakoune +{ + +using ParameterList = memoryview; + +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 ParameterList& params, + std::unordered_map options); + + // check if a named option (either string or boolean) is specified + bool has_option(const String& name) const; + + // get a string option value, returns an empty string if the option + // is not defined + const String& option_value(const String& name) const; + + // positional parameters count + size_t positional_count() const; + + 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]; + } + + 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; + }; + + // access positional parameter by index + const String& operator[] (size_t index) const; + // positional parameter begin + iterator begin() const; + // positional parameter end + iterator end() const; + +private: + ParameterList m_params; + std::vector m_positional; + std::unordered_map m_options; +}; + +} + +#endif // parameters_parser_hh_INCLUDED +