summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Altmanninger <aclopte@gmail.com>2025-03-23 16:16:25 +0100
committerMaxime Coste <mawww@kakoune.org>2025-03-24 08:28:55 +1100
commitb242a4e049cb94e304183472ab81b027d67c31d6 (patch)
tree2fe1778f962473dace42d269d8d28cf982c23e03
parent6fa99d403c0d5884610fd23e59a2fc10ed258a9a (diff)
Fix prompt history recording when using mapped keys
Commit e3122ab2c (Refactor prompt history handling, 2023-07-05) was a nice simplification but it breaks a rare edge case. It suppresses history recording if all keys the prompt receives were synthesized. That's not quite the right criteria: it means that if prompt is created, edited and and executed, all via mapped keys, we fail to add to history. The criteria should rather be something like "if all keys the prompt receives came from synthesized events". Make it so. This allows us to get rid of the "noninteractive" nested bool that was only used for disabling history.
-rw-r--r--src/commands.cc11
-rw-r--r--src/context.hh4
-rw-r--r--src/hook_manager.cc2
-rw-r--r--src/input_handler.cc34
-rw-r--r--src/normal.cc1
-rw-r--r--test/prompt/history-mapped-keys/cmd0
-rw-r--r--test/prompt/history-mapped-keys/kak_reg_colon1
-rw-r--r--test/prompt/history-mapped-keys/rc3
-rw-r--r--test/prompt/history-mapped-keys/script3
9 files changed, 27 insertions, 32 deletions
diff --git a/src/commands.cc b/src/commands.cc
index 6da92d67..114c1bdc 100644
--- a/src/commands.cc
+++ b/src/commands.cc
@@ -2085,11 +2085,7 @@ void context_wrap(const ParametersParser& parser, Context& context, StringView d
auto context_wrap_for_buffer = [&](Buffer& buffer) {
InputHandler input_handler{{ buffer, Selection{} },
Context::Flags::Draft};
- Context& c = input_handler.context();
-
- ScopedSetBool noninteractive(c.noninteractive());
-
- func(parser, c);
+ func(parser, input_handler.context());
};
if (*bufnames == "*")
{
@@ -2140,7 +2136,6 @@ void context_wrap(const ParametersParser& parser, Context& context, StringView d
Context& c = *effective_context;
- ScopedSetBool noninteractive(c.noninteractive());
ScopedEdition edition{c};
ScopedSelectionEdition selection_edition{c};
@@ -2320,8 +2315,6 @@ const CommandDesc prompt_cmd = {
sc.env_vars.erase("text"_sv);
});
- ScopedSetBool noninteractive{context.noninteractive()};
-
StringView cmd;
switch (event)
{
@@ -2363,7 +2356,6 @@ const CommandDesc on_key_cmd = {
parser.get_switch("mode-name").value_or("on-key"),
KeymapMode::None, [=](Key key, Context& context) mutable {
sc.env_vars["key"_sv] = to_string(key);
- ScopedSetBool noninteractive{context.noninteractive()};
CommandManager::instance().execute(command, context, sc);
});
@@ -2690,7 +2682,6 @@ void enter_user_mode(Context& context, String mode_name, KeymapMode mode, bool l
if (context.keymaps().is_mapped(key, mode))
{
ScopedSetBool disable_keymaps(context.keymaps_disabled());
- ScopedSetBool noninteractive(context.noninteractive());
InputHandler::ScopedForceNormal force_normal{context.input_handler(), {}};
diff --git a/src/context.hh b/src/context.hh
index ce17c3f7..7302c865 100644
--- a/src/context.hh
+++ b/src/context.hh
@@ -131,9 +131,6 @@ public:
NestedBool& keymaps_disabled() { return m_keymaps_disabled; }
const NestedBool& keymaps_disabled() const { return m_keymaps_disabled; }
- NestedBool& noninteractive() { return m_noninteractive; }
- const NestedBool& noninteractive() const { return m_noninteractive; }
-
Flags flags() const { return m_flags; }
JumpList& jump_list() { return m_jump_list; }
@@ -211,7 +208,6 @@ private:
NestedBool m_hooks_disabled;
NestedBool m_keymaps_disabled;
- NestedBool m_noninteractive;
};
struct ScopedEdition
diff --git a/src/hook_manager.cc b/src/hook_manager.cc
index ae3acf65..ee461a83 100644
--- a/src/hook_manager.cc
+++ b/src/hook_manager.cc
@@ -37,8 +37,6 @@ struct HookManager::HookData
enum_desc(Meta::Type<Hook>{})[to_underlying(hook)].name,
param, group));
- ScopedSetBool noninteractive{context.noninteractive()};
-
EnvVarMap env_vars{ {"hook_param", param.str()} };
for (size_t i = 0; i < captures.size(); ++i)
env_vars.insert({format("hook_param_capture_{}", i),
diff --git a/src/input_handler.cc b/src/input_handler.cc
index b6539ce2..d5f010c7 100644
--- a/src/input_handler.cc
+++ b/src/input_handler.cc
@@ -30,8 +30,9 @@ public:
InputMode(const InputMode&) = delete;
InputMode& operator=(const InputMode&) = delete;
- void handle_key(Key key, bool synthesized) { RefPtr<InputMode> keep_alive{this}; on_key(key, synthesized); }
+ void handle_key(Key key) { RefPtr<InputMode> keep_alive{this}; on_key(key); }
virtual void paste(StringView content);
+ virtual void on_raw_key() {}
virtual void on_enabled(bool from_pop) {}
virtual void on_disabled(bool from_push) {}
@@ -57,7 +58,7 @@ public:
using Insertion = InputHandler::Insertion;
protected:
- virtual void on_key(Key key, bool synthesized) = 0;
+ virtual void on_key(Key key) = 0;
void push_mode(InputMode* new_mode) { m_input_handler.push_mode(new_mode); }
void pop_mode() { m_input_handler.pop_mode(this); }
@@ -279,7 +280,7 @@ public:
}
}
- void on_key(Key key, bool) override
+ void on_key(Key key) override
{
bool should_clear = false;
@@ -682,7 +683,6 @@ public:
m_empty_text{std::move(emptystr)},
// This prompt may outlive local scopes so ignore local faces.
m_line_editor{context().faces(false)}, m_flags(flags),
- m_was_interactive{not context().noninteractive()},
m_history{RegisterManager::instance()[history_register]},
m_current_history{-1},
m_auto_complete{context().options()["autocomplete"].get<AutoComplete>() & AutoComplete::Prompt},
@@ -702,11 +702,9 @@ public:
m_line_editor.reset(std::move(initstr), m_empty_text);
}
- void on_key(Key key, bool synthesized) override
+ void on_key(Key key) override
{
const String& line = m_line_editor.line();
- if (not synthesized)
- m_was_interactive = true;
auto can_auto_insert_completion = [&] {
const bool has_completions = not m_completions.candidates.empty();
@@ -968,6 +966,10 @@ public:
m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context()));
}
+ void on_raw_key() override {
+ m_was_interactive = true;
+ }
+
void set_prompt_face(Face face)
{
if (face != m_prompt_face)
@@ -1117,7 +1119,7 @@ private:
LineEditor m_line_editor;
bool m_line_changed = false;
PromptFlags m_flags;
- bool m_was_interactive;
+ bool m_was_interactive = false;
Register& m_history;
int m_current_history;
bool m_auto_complete;
@@ -1143,7 +1145,7 @@ public:
: InputMode(input_handler), m_name{std::move(name)}, m_callback(std::move(callback)), m_keymap_mode(keymap_mode),
m_idle_timer{Clock::now() + get_idle_timeout(context()), std::move(idle_callback)} {}
- void on_key(Key key, bool) override
+ void on_key(Key key) override
{
// maintain hooks disabled in the callback if they were before pop_mode
ScopedSetBool disable_hooks(context().hooks_disabled(),
@@ -1229,7 +1231,7 @@ public:
}
}
- void on_key(Key key, bool synthesized) override
+ void on_key(Key key) override
{
auto& buffer = context().buffer();
@@ -1682,21 +1684,23 @@ void InputHandler::handle_key(Key key, bool synthesized)
++m_handle_key_level;
auto dec = on_scope_end([this]{ --m_handle_key_level;} );
- auto process_key = [this](Key k, bool synthesized) {
+ if (not synthesized)
+ current_mode().on_raw_key();
+
+ auto process_key = [this](Key k) {
record_key(k);
- current_mode().handle_key(k, synthesized);
+ current_mode().handle_key(k);
};
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))
- process_key(k, true);
+ process_key(k);
}
else
- process_key(key, synthesized or m_handle_key_level > 1);
+ process_key(key);
if (m_handle_key_level < m_recording_level)
{
diff --git a/src/normal.cc b/src/normal.cc
index 6be1a857..8a8b958d 100644
--- a/src/normal.cc
+++ b/src/normal.cc
@@ -2078,7 +2078,6 @@ void exec_user_mappings(Context& context, NormalParams params)
return;
ScopedSetBool disable_keymaps(context.keymaps_disabled());
- ScopedSetBool noninteractive(context.noninteractive());
InputHandler::ScopedForceNormal force_normal{context.input_handler(), params};
diff --git a/test/prompt/history-mapped-keys/cmd b/test/prompt/history-mapped-keys/cmd
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/prompt/history-mapped-keys/cmd
diff --git a/test/prompt/history-mapped-keys/kak_reg_colon b/test/prompt/history-mapped-keys/kak_reg_colon
new file mode 100644
index 00000000..dfc83d27
--- /dev/null
+++ b/test/prompt/history-mapped-keys/kak_reg_colon
@@ -0,0 +1 @@
+nop hello
diff --git a/test/prompt/history-mapped-keys/rc b/test/prompt/history-mapped-keys/rc
new file mode 100644
index 00000000..3b933564
--- /dev/null
+++ b/test/prompt/history-mapped-keys/rc
@@ -0,0 +1,3 @@
+map global normal <c-1> %{:nop }
+map global prompt <c-2> %{hello}
+map global prompt <c-j> <ret>
diff --git a/test/prompt/history-mapped-keys/script b/test/prompt/history-mapped-keys/script
new file mode 100644
index 00000000..62340285
--- /dev/null
+++ b/test/prompt/history-mapped-keys/script
@@ -0,0 +1,3 @@
+ui_out -ignore 4
+ui_in '{ "jsonrpc": "2.0", "method": "keys", "params": [ "<c-1><c-2><c-j>" ] }'
+ui_out -until '{ "jsonrpc": "2.0", "method": "refresh", "params": [false] }'