diff --git a/src/input_handler.cc b/src/input_handler.cc index c80ca7b1..afb2d741 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -194,8 +194,8 @@ public: if (m_waiting_for_reg) { - if (key.modifiers == Key::Modifiers::None) - m_params.reg = key.key; + if (auto cp = key.codepoint()) + m_params.reg = *cp; m_waiting_for_reg = false; return; } @@ -212,8 +212,9 @@ public: if (context().has_ui()) context().ui().info_hide(); - if (key.modifiers == Key::Modifiers::None and isdigit(key.key)) - m_params.count = m_params.count * 10 + key.key - '0'; + auto cp = key.codepoint(); + if (cp and isdigit(*cp)) + m_params.count = m_params.count * 10 + *cp - '0'; else if (key == Key::Backspace) m_params.count /= 10; else if (key == '\\') @@ -397,9 +398,9 @@ public: to_next_word_end(m_cursor_pos, m_line); else if (key == ctrlalt('e')) to_next_word_end(m_cursor_pos, m_line); - else if (key.modifiers == Key::Modifiers::None) + else if (auto cp = key.codepoint()) { - m_line = m_line.substr(0, m_cursor_pos) + codepoint_to_str(key.key) + m_line = m_line.substr(0, m_cursor_pos) + codepoint_to_str(*cp) + m_line.substr(m_cursor_pos); ++m_cursor_pos; } @@ -646,11 +647,14 @@ public: { on_next_key_with_autoinfo(context(), KeymapMode::None, [this](Key key, Context&) { - StringView reg = context().main_sel_register_value(String{key.key}); - m_line_editor.insert(reg); + if (auto cp = key.codepoint()) + { + StringView reg = context().main_sel_register_value(String{*cp}); + m_line_editor.insert(reg); - display(); - m_callback(m_line_editor.line(), PromptEvent::Change, context()); + display(); + m_callback(m_line_editor.line(), PromptEvent::Change, context()); + } }, "Enter register name", register_doc); return; } @@ -1013,21 +1017,17 @@ public: } selections.sort_and_merge_overlapping(); } - else if (key.modifiers == Key::Modifiers::None) - insert(key.key); + else if (auto cp = key.codepoint()) + insert(*cp); else if (key == ctrl('r')) { on_next_key_with_autoinfo(context(), KeymapMode::None, [this](Key key, Context&) { - if (key.modifiers == Key::Modifiers::None) - insert(RegisterManager::instance()[key.key].values(context())); + if (auto cp = key.codepoint()) + insert(RegisterManager::instance()[*cp].values(context())); }, "Enter register name", register_doc); update_completions = false; } - else if (key == ctrl('m')) - insert('\n'); - else if (key == ctrl('i')) - insert('\t'); else if (key == ctrl('n')) { last_insert().keys.pop_back(); @@ -1044,11 +1044,11 @@ public: { on_next_key_with_autoinfo(context(), KeymapMode::None, [this](Key key, Context&) { - if (key.key == 'f') + if (key == 'f') m_completer.explicit_file_complete(); - if (key.key == 'w') + if (key == 'w') m_completer.explicit_word_complete(); - if (key.key == 'l') + if (key == 'l') m_completer.explicit_line_complete(); }, "Complete", " Enter completion type:\n" diff --git a/src/keys.cc b/src/keys.cc index b01fcc52..349cbd53 100644 --- a/src/keys.cc +++ b/src/keys.cc @@ -10,7 +10,7 @@ namespace Kakoune { -Key canonicalize_ifn(Key key) +static Key canonicalize_ifn(Key key) { if (key.key > 0 and key.key < 27) { @@ -21,6 +21,18 @@ Key canonicalize_ifn(Key key) return key; } +Optional Key::codepoint() const +{ + if (*this == ctrl('m')) + return '\n'; + if (*this == ctrl('i')) + return '\t'; + if (modifiers == Modifiers::None and key > 27 and + (key < 0xD800 or key > 0xDFFF)) // avoid surrogates + return key; + return {}; +} + struct KeyAndName { const char* name; Codepoint key; }; static constexpr KeyAndName keynamemap[] = { { "ret", '\r' }, diff --git a/src/keys.hh b/src/keys.hh index d9865f77..6aa5eb4e 100644 --- a/src/keys.hh +++ b/src/keys.hh @@ -1,11 +1,12 @@ #ifndef keys_hh_INCLUDED #define keys_hh_INCLUDED -#include "unicode.hh" +#include "coord.hh" #include "flags.hh" #include "hash.hh" +#include "optional.hh" +#include "unicode.hh" #include "vector.hh" -#include "coord.hh" namespace Kakoune { @@ -75,6 +76,8 @@ struct Key constexpr bool operator<(Key other) const { return val() < other.val(); } constexpr CharCoord mouse_coord() const { return {(int)((key & 0xFFFF0000) >> 16), (int)(key & 0x0000FFFF)}; } + + Optional codepoint() const; }; template<> struct WithBitOps : std::true_type {}; diff --git a/src/normal.cc b/src/normal.cc index 90487fc0..a2985fc4 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -110,10 +110,11 @@ void goto_commands(Context& context, NormalParams params) { on_next_key_with_autoinfo(context, KeymapMode::Goto, [](Key key, Context& context) { - if (key.modifiers != Key::Modifiers::None) + auto cp = key.codepoint(); + if (not cp) return; auto& buffer = context.buffer(); - switch (tolower(key.key)) + switch (tolower(*cp)) { case 'g': case 'k': @@ -234,12 +235,13 @@ void view_commands(Context& context, NormalParams params) { on_next_key_with_autoinfo(context, KeymapMode::View, [params](Key key, Context& context) { - if (key.modifiers != Key::Modifiers::None or not context.has_window()) + auto cp = key.codepoint(); + if (not cp or not context.has_window()) return; LineCount cursor_line = context.selections().main().cursor().line; Window& window = context.window(); - switch (tolower(key.key)) + switch (tolower(*cp)) { case 'v': case 'c': @@ -278,7 +280,8 @@ void replace_with_char(Context& context, NormalParams) { on_next_key_with_autoinfo(context, KeymapMode::None, [](Key key, Context& context) { - if (not iswprint(key.key)) + auto cp = key.codepoint(); + if (not cp) return; ScopedEdition edition(context); Buffer& buffer = context.buffer(); @@ -287,7 +290,7 @@ void replace_with_char(Context& context, NormalParams) for (auto& sel : selections) { CharCount count = char_length(buffer, sel); - strings.emplace_back(key.key, count); + strings.emplace_back(*cp, count); } selections.insert(strings, InsertMode::Replace); }, "replace with char", "enter char to replace with\n"); @@ -857,9 +860,9 @@ void select_object(Context& context, NormalParams params) const int level = params.count <= 0 ? 0 : params.count - 1; on_next_key_with_autoinfo(context, KeymapMode::Object, [level](Key key, Context& context) { - if (key.modifiers != Key::Modifiers::None) + auto cp = key.codepoint(); + if (not cp) return; - const Codepoint c = key.key; static constexpr struct { @@ -876,11 +879,11 @@ void select_object(Context& context, NormalParams params) }; for (auto& sel : selectors) { - if (c == sel.key) + if (*cp == sel.key) return select(context, std::bind(sel.func, _1, _2, flags)); } - if (c == 'u') + if (*cp == 'u') { return select(context, std::bind(select_argument, _1, _2, level, flags)); } @@ -900,8 +903,8 @@ void select_object(Context& context, NormalParams params) }; for (auto& sur : surrounding_pairs) { - if (sur.pair.opening == c or sur.pair.closing == c or - (sur.name != 0 and sur.name == c)) + if (sur.pair.opening == *cp or sur.pair.closing == *cp or + (sur.name != 0 and sur.name == *cp)) return select(context, std::bind(select_surrounding, _1, _2, sur.pair, level, flags)); } @@ -1026,10 +1029,13 @@ void select_to_next_char(Context& context, NormalParams params) { on_next_key_with_autoinfo(context, KeymapMode::None, [params](Key key, Context& context) { - select( - context, - std::bind(flags & SelectFlags::Reverse ? select_to_reverse : select_to, - _1, _2, key.key, params.count, flags & SelectFlags::Inclusive)); + constexpr auto new_flags = flags & SelectFlags::Extend ? SelectMode::Extend + : SelectMode::Replace; + if (auto cp = key.codepoint()) + select( + context, + std::bind(flags & SelectFlags::Reverse ? select_to_reverse : select_to, + _1, _2, *cp, params.count, flags & SelectFlags::Inclusive)); }, "select to next char","enter char to select to"); } @@ -1045,8 +1051,9 @@ void start_or_end_macro_recording(Context& context, NormalParams) else on_next_key_with_autoinfo(context, KeymapMode::None, [](Key key, Context& context) { - if (key.modifiers == Key::Modifiers::None and is_basic_alpha(key.key)) - context.input_handler().start_recording(tolower(key.key)); + auto cp = key.codepoint(); + if (cp and is_basic_alpha(*cp)) + context.input_handler().start_recording(tolower(*cp)); }, "record macro", "enter macro name "); } @@ -1060,10 +1067,11 @@ void replay_macro(Context& context, NormalParams params) { on_next_key_with_autoinfo(context, KeymapMode::None, [params](Key key, Context& context) mutable { - if (key.modifiers == Key::Modifiers::None and is_basic_alpha(key.key)) + auto cp = key.codepoint(); + if (cp and is_basic_alpha(*cp)) { static bool running_macros[26] = {}; - const char name = tolower(key.key); + const char name = tolower(*cp); const size_t idx = (size_t)(name - 'a'); if (running_macros[idx]) throw runtime_error("recursive macros call detected"); @@ -1255,18 +1263,18 @@ void save_selections(Context& context, NormalParams) { on_next_key_with_autoinfo(context, KeymapMode::None, [](Key key, Context& context) { - if (key.modifiers != Key::Modifiers::None or key == Key::Escape) + auto cp = key.codepoint(); + if (not cp or not is_basic_alpha(*cp)) return; - const char reg = key.key; String desc = format("{}@{}%{}", selection_list_to_string(context.selections()), context.buffer().name(), context.buffer().timestamp()); - RegisterManager::instance()[reg] = desc; + RegisterManager::instance()[*cp] = desc; - context.print_status({format("Saved selections in register '{}'", reg), get_face("Information")}); + context.print_status({format("Saved selections in register '{}'", *cp), get_face("Information")}); }, "Save selections", "Enter register to save selections into"); } @@ -1274,22 +1282,21 @@ void restore_selections(Context& context, NormalParams) { on_next_key_with_autoinfo(context, KeymapMode::None, [](Key key, Context& context) { - if (key.modifiers != Key::Modifiers::None or key == Key::Escape) + auto cp = key.codepoint(); + if (not cp or not is_basic_alpha(*cp)) return; - const char reg = key.key; - - auto content = RegisterManager::instance()[reg].values(context); + auto content = RegisterManager::instance()[*cp].values(context); if (content.size() != 1) - throw runtime_error(format("Register {} does not contain a selections desc", reg)); + throw runtime_error(format("Register {} does not contain a selections desc", *cp)); StringView desc = content[0]; auto arobase = find(desc, '@'); auto percent = find(desc, '%'); if (arobase == desc.end() or percent == desc.end()) - throw runtime_error(format("Register {} does not contain a selections desc", reg)); + throw runtime_error(format("Register {} does not contain a selections desc", *cp)); Buffer& buffer = BufferManager::instance().get_buffer({arobase+1, percent}); size_t timestamp = str_to_int({percent + 1, desc.end()}); @@ -1305,7 +1312,7 @@ void restore_selections(Context& context, NormalParams) context.selections_write_only() = std::move(sel_list); - context.print_status({format("Restored selections from register '{}'", reg), get_face("Information")}); + context.print_status({format("Restored selections from register '{}'", *cp), get_face("Information")}); }, "Restore selections", "Enter register to restore selections from"); }