summaryrefslogtreecommitdiff
path: root/src/input_handler.cc
diff options
context:
space:
mode:
authorIgor Ramazanov <igor.ramazanov@protonmail.com>2025-01-18 11:43:57 +0000
committerGitHub <noreply@github.com>2025-01-18 11:43:57 +0000
commitce4dc805e375dbd8c0477ad5005c2110f83de2aa (patch)
treeac2ca142a2182eb51ed64ffd029fa4111b4d8f48 /src/input_handler.cc
parentae9758e95f531d26847cfc7e884da3ceb47670f3 (diff)
parent54084900624493033cb24640d864c109cdac40a7 (diff)
Merge branch 'mawww:master' into contrib/gendocs.sh
Diffstat (limited to 'src/input_handler.cc')
-rw-r--r--src/input_handler.cc177
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*>(&current_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;