diff options
| author | Igor Ramazanov <igor.ramazanov@protonmail.com> | 2025-01-18 11:43:57 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-18 11:43:57 +0000 |
| commit | ce4dc805e375dbd8c0477ad5005c2110f83de2aa (patch) | |
| tree | ac2ca142a2182eb51ed64ffd029fa4111b4d8f48 /src/input_handler.cc | |
| parent | ae9758e95f531d26847cfc7e884da3ceb47670f3 (diff) | |
| parent | 54084900624493033cb24640d864c109cdac40a7 (diff) | |
Merge branch 'mawww:master' into contrib/gendocs.sh
Diffstat (limited to 'src/input_handler.cc')
| -rw-r--r-- | src/input_handler.cc | 177 |
1 files changed, 116 insertions, 61 deletions
diff --git a/src/input_handler.cc b/src/input_handler.cc index 99fb684e..0f2c8987 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -1,7 +1,7 @@ #include "input_handler.hh" -#include "buffer_manager.hh" -#include "buffer_utils.hh" +#include "buffer.hh" +#include "debug.hh" #include "command_manager.hh" #include "client.hh" #include "event_manager.hh" @@ -11,12 +11,11 @@ #include "option_types.hh" #include "regex.hh" #include "register_manager.hh" -#include "hash_map.hh" #include "user_interface.hh" -#include "utf8.hh" #include "window.hh" #include "word_db.hh" +#include <concepts> #include <utility> #include <limits> @@ -51,7 +50,7 @@ public: virtual std::pair<CursorMode, DisplayCoord> get_cursor_info() const { const auto cursor = context().selections().main().cursor(); - auto coord = context().window().display_position(cursor).value_or(DisplayCoord{}); + auto coord = context().window().display_coord(cursor).value_or(DisplayCoord{}); return {CursorMode::Buffer, coord}; } @@ -60,15 +59,12 @@ public: protected: virtual void on_key(Key key, bool synthesized) = 0; - void push_mode(InputMode* new_mode) - { - m_input_handler.push_mode(new_mode); - } + void push_mode(InputMode* new_mode) { m_input_handler.push_mode(new_mode); } + void pop_mode() { m_input_handler.pop_mode(this); } + + void record_key(Key key) { m_input_handler.record_key(key); } + void drop_last_recorded_key() { m_input_handler.drop_last_recorded_key(); } - void pop_mode() - { - m_input_handler.pop_mode(this); - } private: InputHandler& m_input_handler; }; @@ -120,8 +116,10 @@ struct MouseHandler return false; Buffer& buffer = context.buffer(); - BufferCoord cursor; - constexpr auto modifiers = Key::Modifiers::Control | Key::Modifiers::Alt | Key::Modifiers::Shift | Key::Modifiers::MouseButtonMask; + // bits above these potentially store additional information + constexpr auto mask = (Key::Modifiers) 0x7FF; + constexpr auto modifiers = Key::Modifiers::Control | Key::Modifiers::Alt | Key::Modifiers::Shift | Key::Modifiers::MouseButtonMask | ~mask; + switch ((key.modifiers & ~modifiers).value) { case Key::Modifiers::MousePress: @@ -129,20 +127,31 @@ struct MouseHandler { case Key::MouseButton::Right: { m_dragging.reset(); - cursor = context.window().buffer_coord(key.coord()); + auto cursor = context.window().buffer_coord(key.coord()); + if (not cursor) + { + context.ensure_cursor_visible = false; + return true; + } ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); if (key.modifiers & Key::Modifiers::Control) - selections = {{selections.begin()->anchor(), cursor}}; + selections = {{selections.begin()->anchor(), *cursor}}; else - selections.main() = {selections.main().anchor(), cursor}; + selections.main() = {selections.main().anchor(), *cursor}; selections.sort_and_merge_overlapping(); return true; } case Key::MouseButton::Left: { m_dragging.reset(new ScopedSelectionEdition{context}); - m_anchor = context.window().buffer_coord(key.coord()); + auto anchor = context.window().buffer_coord(key.coord()); + if (not anchor) + { + context.ensure_cursor_visible = false; + return true; + } + m_anchor = *anchor; if (not (key.modifiers & Key::Modifiers::Control)) context.selections_write_only() = { buffer, m_anchor}; else @@ -160,34 +169,34 @@ struct MouseHandler } case Key::Modifiers::MouseRelease: { - if (not m_dragging) + auto cursor = context.window().buffer_coord(key.coord()); + if (not m_dragging or not cursor) { context.ensure_cursor_visible = false; return true; } auto& selections = context.selections(); - cursor = context.window().buffer_coord(key.coord()); - selections.main() = {buffer.clamp(m_anchor), cursor}; + selections.main() = {buffer.clamp(m_anchor), *cursor}; selections.sort_and_merge_overlapping(); m_dragging.reset(); return true; } case Key::Modifiers::MousePos: { - if (not m_dragging) + auto cursor = context.window().buffer_coord(key.coord()); + if (not m_dragging or not cursor) { context.ensure_cursor_visible = false; return true; } - cursor = context.window().buffer_coord(key.coord()); auto& selections = context.selections(); - selections.main() = {buffer.clamp(m_anchor), cursor}; + selections.main() = {buffer.clamp(m_anchor), *cursor}; selections.sort_and_merge_overlapping(); return true; } case Key::Modifiers::Scroll: - scroll_window(context, static_cast<int32_t>(key.key), m_dragging ? OnHiddenCursor::MoveCursor : OnHiddenCursor::PreserveSelections); + scroll_window(context, key.scroll_amount(), m_dragging ? OnHiddenCursor::MoveCursor : OnHiddenCursor::PreserveSelections); return true; default: return false; @@ -222,6 +231,9 @@ public: context().flags() & Context::Flags::Draft ? Timer::Callback{} : [this](Timer&) { RefPtr<InputMode> keep_alive{this}; // hook could trigger pop_mode() + if (context().has_client()) + context().client().clear_pending(); + context().hooks().run_hook(Hook::NormalIdle, "", context()); }}, m_fs_check_timer{TimePoint::max(), @@ -269,6 +281,8 @@ public: void on_key(Key key, bool) override { + bool should_clear = false; + kak_assert(m_state != State::PopOnEnabled); ScopedSetBool set_in_on_key{m_in_on_key}; @@ -285,9 +299,7 @@ public: if (m_mouse_handler.handle_key(key, context())) { - context().print_status({}); - if (context().has_client()) - context().client().info_hide(); + should_clear = true; if (not transient) m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context())); @@ -334,9 +346,7 @@ public: m_state = State::PopOnEnabled; }); - context().print_status({}); - if (context().has_client()) - context().client().info_hide(); + should_clear = true; // Hack to parse keys sent by terminals using the 8th bit to mark the // meta key. In normal mode, give priority to a potential alt-key than @@ -367,7 +377,11 @@ public: context().hooks().run_hook(Hook::NormalKey, to_string(key), context()); if (enabled() and not transient) // The hook might have changed mode + { + if (should_clear and context().has_client()) + context().client().schedule_clear(); m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context())); + } } ModeInfo mode_info() const override @@ -393,6 +407,17 @@ public: return {atoms, m_params}; } + void paste(StringView content) override + { + InputMode::paste(content); + if (not (context().flags() & Context::Flags::Draft)) + { + if (context().has_client()) + context().client().schedule_clear(); + m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context())); + } + } + KeymapMode keymap_mode() const override { return KeymapMode::Normal; } StringView name() const override { return "normal"; } @@ -667,7 +692,7 @@ public: Timer::Callback{} : [this](Timer&) { RefPtr<InputMode> keep_alive{this}; // hook or m_callback could trigger pop_mode() if (m_auto_complete and m_refresh_completion_pending) - refresh_completions(CompletionFlags::Fast); + refresh_completions(); if (m_line_changed) { m_callback(m_line_editor.line(), PromptEvent::Change, context()); @@ -804,10 +829,10 @@ public: CandidateList& candidates = m_completions.candidates; if (m_auto_complete and m_refresh_completion_pending) - refresh_completions(CompletionFlags::Fast); + refresh_completions(); if (candidates.empty()) // manual completion, we need to ask our completer for completions { - refresh_completions(CompletionFlags::None); + refresh_completions(); if ((not m_prefix_in_completions and candidates.size() > 1) or candidates.size() > 2) return; @@ -865,7 +890,7 @@ public: }); if (m_explicit_completer) - refresh_completions(CompletionFlags::None); + refresh_completions(); }, "enter completion type", "f: filename\n" "w: buffer word\n"); @@ -876,7 +901,7 @@ public: m_auto_complete = not m_auto_complete; if (m_auto_complete) - refresh_completions(CompletionFlags::Fast); + refresh_completions(); else if (context().has_client()) { clear_completions(); @@ -971,7 +996,7 @@ private: template<typename Completer> void use_explicit_completer(Completer&& completer) { - m_explicit_completer = [completer](const Context& context, CompletionFlags flags, StringView content, ByteCount cursor_pos) { + m_explicit_completer = [completer](const Context& context, StringView content, ByteCount cursor_pos) { Optional<Token> last_token; CommandParser parser{content.substr(0_byte, cursor_pos)}; while (auto token = parser.read_token(false)) @@ -987,7 +1012,7 @@ private: }; } - void refresh_completions(CompletionFlags flags) + void refresh_completions() { try { @@ -997,7 +1022,7 @@ private: return; m_current_completion = -1; const String& line = m_line_editor.line(); - m_completions = completer(context(), flags, line, + m_completions = completer(context(), line, line.byte_count_to(m_line_editor.cursor_pos())); if (not context().has_client()) return; @@ -1288,9 +1313,13 @@ public: selections.sort_and_merge_overlapping(); } else if (auto cp = key.codepoint()) + { + m_completer.try_accept(); insert(*cp); + } else if (key == ctrl('r')) { + m_completer.try_accept(); on_next_key_with_autoinfo(context(), "register", KeymapMode::None, [this](Key key, Context&) { auto cp = key.codepoint(); @@ -1302,11 +1331,10 @@ public: } else if (key == ctrl('n') or key == ctrl('p') or key.modifiers == Key::Modifiers::MenuSelect) { - if (m_last_insert and not synthesized) - m_last_insert->keys.pop_back(); + drop_last_recorded_key(); bool relative = key.modifiers != Key::Modifiers::MenuSelect; int index = relative ? (key == ctrl('n') ? 1 : -1) : key.key; - m_completer.select(index, relative, m_last_insert ? &m_last_insert->keys : nullptr); + m_completer.select(index, relative, [&](Key key) { record_key(key); }); update_completions = false; } else if (key == ctrl('x')) @@ -1345,6 +1373,7 @@ public: } else if (key == ctrl('v')) { + m_completer.try_accept(); on_next_key_with_autoinfo(context(), "raw-insert", KeymapMode::None, [this, transient](Key key, Context&) { if (auto cp = get_raw_codepoint(key)) @@ -1373,6 +1402,7 @@ public: void paste(StringView content) override { + m_completer.try_accept(); insert(ConstArrayView<StringView>{content}); m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context())); } @@ -1410,7 +1440,7 @@ private: template<typename S> void insert(ConstArrayView<S> strings) { - m_completer.try_accept(); + kak_assert(not m_completer.has_candidate_selected()); context().selections().for_each([strings, &buffer=context().buffer()] (size_t index, Selection& sel) { Kakoune::insert(buffer, sel, sel.cursor(), strings[std::min(strings.size()-1, index)]); @@ -1580,7 +1610,7 @@ void InputHandler::repeat_last_insert() push_mode(new InputModes::Insert(*this, m_last_insert.mode, m_last_insert.count, nullptr)); for (auto& key : m_last_insert.keys) - handle_key(key); + handle_key(key, true); kak_assert(dynamic_cast<InputModes::Normal*>(¤t_mode()) != nullptr); } @@ -1642,34 +1672,29 @@ static bool is_valid(Key key) return ((key.modifiers & ~valid_mods) or key.key <= 0x10FFFF); } -void InputHandler::handle_key(Key key) +void InputHandler::handle_key(Key key, bool synthesized) { if (not is_valid(key)) return; - const bool was_recording = is_recording(); ++m_handle_key_level; auto dec = on_scope_end([this]{ --m_handle_key_level;} ); - if (m_last_insert.recording and m_handle_key_level <= 1) - m_last_insert.keys.push_back(key); + auto process_key = [this](Key k, bool synthesized) { + record_key(k); + current_mode().handle_key(k, synthesized); + }; const auto keymap_mode = current_mode().keymap_mode(); KeymapManager& keymaps = m_context.keymaps(); if (keymaps.is_mapped(key, keymap_mode) and not m_context.keymaps_disabled()) { ScopedSetBool noninteractive{context().noninteractive()}; - for (auto& k : keymaps.get_mapping_keys(key, keymap_mode)) - current_mode().handle_key(k, true); + process_key(k, true); } else - current_mode().handle_key(key, m_handle_key_level > 1); - - // do not record the key that made us enter or leave recording mode, - // and the ones that are triggered recursively by previous keys. - if (was_recording and is_recording() and m_handle_key_level == m_recording_level) - m_recorded_keys += to_string(key); + process_key(key, synthesized or m_handle_key_level > 1); if (m_handle_key_level < m_recording_level) { @@ -1679,6 +1704,29 @@ void InputHandler::handle_key(Key key) } } +void InputHandler::record_key(Key key) +{ + if (m_last_insert.recording and m_handle_key_level <= 1) + m_last_insert.keys.push_back(key); + if (is_recording() and m_handle_key_level == m_recording_level) + m_recorded_keys.push_back(key); +} + +void InputHandler::drop_last_recorded_key() +{ + if (m_last_insert.recording and m_handle_key_level <= 1) + { + kak_assert(not m_last_insert.keys.empty()); + m_last_insert.keys.pop_back(); + } + + if (is_recording() and m_handle_key_level == m_recording_level) + { + kak_assert(not m_recorded_keys.empty()); + m_recorded_keys.pop_back(); + } +} + void InputHandler::refresh_ifn() { current_mode().refresh_ifn(); @@ -1688,7 +1736,7 @@ void InputHandler::start_recording(char reg) { kak_assert(m_recording_reg == 0); m_recording_level = m_handle_key_level; - m_recorded_keys = ""; + m_recorded_keys.clear(); m_recording_reg = reg; } @@ -1700,10 +1748,17 @@ bool InputHandler::is_recording() const void InputHandler::stop_recording() { kak_assert(m_recording_reg != 0); - if (not m_recorded_keys.empty()) - RegisterManager::instance()[m_recording_reg].set( - context(), {m_recorded_keys}); + { + // Forget the key that got us to exit recording + if (m_handle_key_level == m_recording_level) + m_recorded_keys.pop_back(); + + String keys; + for (auto& key : m_recorded_keys) + keys += to_string(key); + RegisterManager::instance()[m_recording_reg].set(context(), {keys}); + } m_recording_reg = 0; m_recording_level = -1; |
