diff --git a/README.asciidoc b/README.asciidoc index d37f93e5..6fac63d5 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -340,6 +340,7 @@ object you want. * _s_: select the sentence * _p_: select the paragraph * _i_: select the current indentation block + * _n_: select the number For nestable objects, a count can be used in order to specify which surrounding level to select. diff --git a/src/command_manager.cc b/src/command_manager.cc index 82fcea46..9ae3cbc5 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -30,21 +30,11 @@ void CommandManager::register_command(String command_name, std::move(completer) }; } -void CommandManager::register_commands(memoryview command_names, - Command command, - String docstring, - ParameterDesc param_desc, - CommandFlags flags, - CommandCompleter completer) +void CommandManager::register_alias(String alias, String command) { - kak_assert(not command_names.empty()); - m_commands[command_names[0]] = { std::move(command), - std::move(docstring), - std::move(param_desc), - flags, - completer }; - for (size_t i = 1; i < command_names.size(); ++i) - m_aliases[command_names[i]] = command_names[0]; + kak_assert(not alias.empty()); + kak_assert(command_defined(command)); + m_aliases[alias] = std::move(command); } struct parse_error : runtime_error diff --git a/src/command_manager.hh b/src/command_manager.hh index ea577d00..8d0cc957 100644 --- a/src/command_manager.hh +++ b/src/command_manager.hh @@ -79,12 +79,7 @@ public: ParameterDesc param_desc, CommandFlags flags = CommandFlags::None, CommandCompleter completer = CommandCompleter()); - - void register_commands(memoryview command_names, Command command, - String docstring, - ParameterDesc param_desc, - CommandFlags flags = CommandFlags::None, - CommandCompleter completer = CommandCompleter()); + void register_alias(String alias, String command); private: void execute_single_command(CommandParameters params, diff --git a/src/commands.cc b/src/commands.cc index bad0f01d..a7b6c833 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -568,6 +568,14 @@ void define_command(const ParametersParser& parser, Context& context) if (parser.has_option("docstring")) docstring = parser.option_value("docstring"); + String alias; + if (parser.has_option("alias")) + { + alias = parser.option_value("alias"); + if (alias.empty()) + throw runtime_error("alias should not be an empty string"); + } + String commands = parser[1]; Command cmd; ParameterDesc desc; @@ -649,13 +657,11 @@ void define_command(const ParametersParser& parser, Context& context) return Completions{ 0_byte, params[token_to_complete].length(), split(output, '\n') }; }; } - if (parser.has_option("alias")) - CommandManager::instance().register_commands( - { cmd_name, parser.option_value("alias") }, - cmd, std::move(docstring), desc, flags, completer); - else - CommandManager::instance().register_command( - cmd_name, cmd, std::move(docstring), desc, flags, completer); + + auto& cm = CommandManager::instance(); + cm.register_command(cmd_name, cmd, std::move(docstring), desc, flags, completer); + if (not alias.empty()) + cm.register_alias(std::move(alias), cmd_name); } const CommandDesc define_command_cmd = { @@ -1322,10 +1328,9 @@ void exec_keys(const KeyList& keys, Context& context) static void register_command(CommandManager& cm, const CommandDesc& c) { + cm.register_command(c.name, c.func, c.docstring, c.params, c.flags, c.completer); if (c.alias) - cm.register_commands({ c.name, c.alias }, c.func, c.docstring, c.params, c.flags, c.completer); - else - cm.register_command(c.name, c.func, c.docstring, c.params, c.flags, c.completer); + cm.register_alias(c.alias, c.name); } void register_commands() diff --git a/src/id_map.hh b/src/id_map.hh index 023c73c0..e8fe1ccb 100644 --- a/src/id_map.hh +++ b/src/id_map.hh @@ -18,6 +18,9 @@ public: using iterator = typename container_type::iterator; using const_iterator = typename container_type::const_iterator; + id_map() = default; + id_map(std::initializer_list val) : m_content{val} {} + void append(const value_type& value) { m_content.push_back(value); @@ -91,6 +94,8 @@ public: prefix, cursor_pos, [](const value_type&) { return true; }); } + bool empty() const { return m_content.empty(); } + iterator begin() { return m_content.begin(); } iterator end() { return m_content.end(); } const_iterator begin() const { return m_content.begin(); } diff --git a/src/keys.cc b/src/keys.cc index 2caef9a7..a899fb99 100644 --- a/src/keys.cc +++ b/src/keys.cc @@ -1,6 +1,7 @@ #include "keys.hh" #include "utils.hh" +#include "utf8_iterator.hh" namespace Kakoune { @@ -40,19 +41,21 @@ static const KeyAndName keynamemap[] = { KeyList parse_keys(StringView str) { KeyList result; - for (ByteCount pos = 0; pos < str.length(); ++pos) + using PassPolicy = utf8::InvalidBytePolicy::Pass; + using Utf8It = utf8::utf8_iterator; + for (Utf8It it = str.begin(), str_end = str.end(); it < str_end; ++it) { - if (str[pos] == '<') + if (*it == '<') { - ByteCount end_pos = pos; - while (end_pos < str.length() and str[end_pos] != '>') - ++end_pos; + Utf8It end_it = it; + while (end_it < str_end and *end_it != '>') + ++end_it; - if (end_pos < str.length()) + if (end_it < str_end) { Key::Modifiers modifier = Key::Modifiers::None; - auto keyname = str.substr(pos+1, end_pos - pos - 1); + StringView keyname{it.base()+1, end_it.base()}; if (keyname.length() > 2) { if (tolower(keyname[0]) == 'c' and keyname[1] == '-') @@ -66,19 +69,19 @@ KeyList parse_keys(StringView str) keyname = keyname.substr(2_byte); } } - if (keyname.length() == 1) + if (keyname.char_length() == 1) { - result.push_back(Key{ modifier, Codepoint(keyname[0]) }); - pos = end_pos; + result.push_back(Key{ modifier, utf8::codepoint(keyname.begin()) }); + it = end_it; continue; } - auto it = find_if(keynamemap, [&keyname](const KeyAndName& item) - { return item.first == keyname; }); - if (it != end(keynamemap)) + auto name_it = find_if(keynamemap, [&keyname](const KeyAndName& item) + { return item.first == keyname; }); + if (name_it != end(keynamemap)) { - Key key = canonicalize_ifn(Key{ modifier, it->second }); + Key key = canonicalize_ifn(Key{ modifier, name_it->second }); result.push_back(key); - pos = end_pos; + it = end_it; continue; } if ((keyname[0] == 'f' or keyname[0] == 'F') and @@ -100,13 +103,13 @@ KeyList parse_keys(StringView str) if (val >= 1 and val <= 12) { result.push_back(Key{ modifier, Key::F1 + (val - 1) }); - pos = end_pos; + it = end_it; continue; } } } } - result.push_back({Key::Modifiers::None, Codepoint(str[pos])}); + result.push_back({Key::Modifiers::None, *it}); } return result; } diff --git a/src/normal.cc b/src/normal.cc index 1a046b6d..0ceaeade 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -840,6 +840,7 @@ void select_object(Context& context, int param) { 's', select_sentence }, { 'p', select_paragraph }, { 'i', select_indent }, + { 'n', select_number }, }; for (auto& sel : selectors) { diff --git a/src/parameters_parser.hh b/src/parameters_parser.hh index b41dc9a0..49d970f7 100644 --- a/src/parameters_parser.hh +++ b/src/parameters_parser.hh @@ -2,11 +2,10 @@ #define parameters_parser_hh_INCLUDED #include "exception.hh" +#include "id_map.hh" #include "memoryview.hh" #include "string.hh" -#include - namespace Kakoune { @@ -40,7 +39,7 @@ struct SwitchDesc String description; }; -using SwitchMap = std::unordered_map; +using SwitchMap = id_map; String generate_switches_doc(const SwitchMap& opts); diff --git a/src/selectors.cc b/src/selectors.cc index 8116570a..ca85142a 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -218,6 +218,31 @@ Selection select_to_eol_reverse(const Buffer& buffer, const Selection& selection return utf8_range(begin, end == buffer.begin() ? end : end+1); } +Selection select_number(const Buffer& buffer, const Selection& selection, ObjectFlags flags) +{ + auto is_number = [&](char c) { + return (c >= '0' and c <= '9') or + (not (flags & ObjectFlags::Inner) and c == '.'); + }; + + BufferIterator first = buffer.iterator_at(selection.cursor()); + if (flags & ObjectFlags::ToBegin) + { + skip_while_reverse(first, buffer.begin(), is_number); + if (not is_number(*first) or not *first == '-') + ++first; + } + BufferIterator last = buffer.iterator_at(selection.cursor()); + if (flags & ObjectFlags::ToEnd) + { + skip_while(last, buffer.end(), is_number); + --last; + } + + return (flags & ObjectFlags::ToEnd) ? Selection{first.coord(), last.coord()} + : Selection{last.coord(), first.coord()}; +} + static bool is_end_of_sentence(char c) { return c == '.' or c == ';' or c == '!' or c == '?'; diff --git a/src/selectors.hh b/src/selectors.hh index 2aa23053..7e86a738 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -134,10 +134,8 @@ Selection select_to_previous_word(const Buffer& buffer, const Selection& selecti return utf8_range(begin, with_end ? end : end+1); } -Selection select_line(const Buffer& buffer, -const Selection& selection); -Selection select_matching(const Buffer& buffer, - const Selection& selection); +Selection select_line(const Buffer& buffer, const Selection& selection); +Selection select_matching(const Buffer& buffer, const Selection& selection); Selection select_to(const Buffer& buffer, const Selection& selection, Codepoint c, int count, bool inclusive); @@ -159,7 +157,9 @@ constexpr ObjectFlags operator|(ObjectFlags lhs, ObjectFlags rhs) { return (ObjectFlags)((int)lhs | (int) rhs); } template -Selection select_word(const Buffer& buffer, const Selection& selection, ObjectFlags flags) +Selection select_word(const Buffer& buffer, + const Selection& selection, + ObjectFlags flags) { Utf8Iterator first = buffer.iterator_at(selection.cursor()); Utf8Iterator last = first; @@ -200,6 +200,10 @@ Selection select_word(const Buffer& buffer, const Selection& selection, ObjectFl : utf8_range(last, first); } +Selection select_number(const Buffer& buffer, + const Selection& selection, + ObjectFlags flags); + Selection select_sentence(const Buffer& buffer, const Selection& selection, ObjectFlags flags);