|
@@ -1,3 +1,4 @@
|
|
|
|
|
+#include "lumacs/editor_core.hpp"
|
|
|
#include "lumacs/keybinding.hpp"
|
|
#include "lumacs/keybinding.hpp"
|
|
|
#include "lumacs/command_system.hpp" // Include for CommandSystem
|
|
#include "lumacs/command_system.hpp" // Include for CommandSystem
|
|
|
#include <sstream>
|
|
#include <sstream>
|
|
@@ -341,6 +342,14 @@ void KeyBindingManager::unbind(const std::string& key_str) {
|
|
|
unbind(KeySequence(key_str));
|
|
unbind(KeySequence(key_str));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#include "lumacs/editor_core.hpp" // Needed for EditorCore definition in implementation
|
|
|
|
|
+
|
|
|
|
|
+// ... (rest of includes)
|
|
|
|
|
+
|
|
|
|
|
+namespace lumacs {
|
|
|
|
|
+
|
|
|
|
|
+// ... (previous code)
|
|
|
|
|
+
|
|
|
KeyProcessingResult KeyBindingManager::process_key(const Key& key) {
|
|
KeyProcessingResult KeyBindingManager::process_key(const Key& key) {
|
|
|
// Check for timeout first
|
|
// Check for timeout first
|
|
|
if (is_building_sequence() && has_sequence_timed_out()) {
|
|
if (is_building_sequence() && has_sequence_timed_out()) {
|
|
@@ -359,12 +368,30 @@ KeyProcessingResult KeyBindingManager::process_key(const Key& key) {
|
|
|
if (node && node->binding.has_value()) {
|
|
if (node && node->binding.has_value()) {
|
|
|
// Found exact match
|
|
// Found exact match
|
|
|
KeyBinding bound_command = node->binding.value();
|
|
KeyBinding bound_command = node->binding.value();
|
|
|
|
|
+
|
|
|
|
|
+ // Capture the raw key string of the full sequence before clearing
|
|
|
|
|
+ std::string sequence_str = current_sequence_.to_string();
|
|
|
clear_partial_sequence();
|
|
clear_partial_sequence();
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
// Execute the command via the CommandSystem
|
|
// Execute the command via the CommandSystem
|
|
|
if (command_system_) {
|
|
if (command_system_) {
|
|
|
|
|
+ // --- Macro Recording Logic ---
|
|
|
|
|
+ bool was_recording = command_system_->core().is_recording_macro();
|
|
|
|
|
+
|
|
|
CommandResult cmd_result = command_system_->execute(bound_command.command_name, {}); // No args for now
|
|
CommandResult cmd_result = command_system_->execute(bound_command.command_name, {}); // No args for now
|
|
|
|
|
+
|
|
|
|
|
+ bool is_recording = command_system_->core().is_recording_macro();
|
|
|
|
|
+
|
|
|
|
|
+ // Only record if we were recording BEFORE execution and are STILL recording AFTER execution.
|
|
|
|
|
+ // This prevents recording the 'Start Macro' key (was_recording=false)
|
|
|
|
|
+ // and the 'End Macro' key (is_recording=false).
|
|
|
|
|
+ if (was_recording && is_recording) {
|
|
|
|
|
+ // Record the full sequence that triggered this command
|
|
|
|
|
+ command_system_->core().record_key_sequence(sequence_str);
|
|
|
|
|
+ }
|
|
|
|
|
+ // -----------------------------
|
|
|
|
|
+
|
|
|
return KeyProcessingResult(cmd_result.status == CommandStatus::Success ? KeyResult::Executed : KeyResult::Failed, cmd_result);
|
|
return KeyProcessingResult(cmd_result.status == CommandStatus::Success ? KeyResult::Executed : KeyResult::Failed, cmd_result);
|
|
|
}
|
|
}
|
|
|
return KeyProcessingResult(KeyResult::Failed, CommandResult{CommandStatus::Failure, "No CommandSystem available"});
|
|
return KeyProcessingResult(KeyResult::Failed, CommandResult{CommandStatus::Failure, "No CommandSystem available"});
|
|
@@ -379,8 +406,78 @@ KeyProcessingResult KeyBindingManager::process_key(const Key& key) {
|
|
|
if (node && !node->children.empty()) {
|
|
if (node && !node->children.empty()) {
|
|
|
return KeyProcessingResult(KeyResult::Partial);
|
|
return KeyProcessingResult(KeyResult::Partial);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // --- Self-Insert / Unbound Key Recording Logic ---
|
|
|
|
|
+ // If no binding found, we clear sequence and return Unbound.
|
|
|
|
|
+ // The Input Loop (EditorCore/TUI/GTK) usually handles Unbound by calling 'self-insert-command' manually
|
|
|
|
|
+ // if it's a printable character.
|
|
|
|
|
+ // Wait, TuiEditor calls `core_->command_system().execute("self-insert-command", {key_name});`
|
|
|
|
|
+ // which goes through CommandSystem::execute.
|
|
|
|
|
+ // Does CommandSystem::execute record macros? No, checking EditorCore above.
|
|
|
|
|
+ // But `process_key` returns Unbound here. So the macro logic above is NOT reached for self-insert!
|
|
|
|
|
+
|
|
|
|
|
+ // We need to handle recording for unbound keys that become self-inserts?
|
|
|
|
|
+ // Actually, `process_key` returning Unbound delegates responsibility to the caller.
|
|
|
|
|
+ // The caller (e.g. TuiEditor::handle_input) calls `self-insert-command`.
|
|
|
|
|
+ // We need that `execute` call to be recorded too?
|
|
|
|
|
+ // But `process_key` logic above only records *bound* commands.
|
|
|
|
|
+
|
|
|
|
|
+ // If `process_key` returns Unbound, the caller calls `execute("self-insert-command")`.
|
|
|
|
|
+ // `execute` itself is just looking up the function and running it. It doesn't know about keys.
|
|
|
|
|
+ // So `MacroManager` needs to be notified by the caller?
|
|
|
|
|
+ // Or `CommandSystem::execute` should record?
|
|
|
|
|
+ // `CommandSystem::execute` receives arguments, not keys.
|
|
|
|
|
+ // If we record `self-insert-command` in `CommandSystem::execute`, we record the command invocation, not the key.
|
|
|
|
|
+ // But macro playback re-injects KEYS (via `process_key`).
|
|
|
|
|
+ // So we must record the KEY.
|
|
|
|
|
+
|
|
|
|
|
+ // This implies that the Input Loop (caller) must notify the recorder for self-inserts.
|
|
|
|
|
+ // OR, we bind printable keys to `self-insert-command` explicitly?
|
|
|
|
|
+ // That would make them "bound commands" and hit the logic above.
|
|
|
|
|
+ // But we rely on "fallback" logic currently.
|
|
|
|
|
+
|
|
|
|
|
+ // Alternative: `process_key` should handle the fallback logic internally instead of returning Unbound?
|
|
|
|
|
+ // If we do that, we can record it here.
|
|
|
|
|
+ // Existing `TuiEditor::handle_input`:
|
|
|
|
|
+ // if (result.type == KeyResult::Unbound) {
|
|
|
|
|
+ // if (key_name.length() == 1) { core_->command_system().execute("self-insert-command", {key_name}); ... }
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ // If I move that fallback logic INTO process_key, then I can record it.
|
|
|
|
|
+ // But process_key is `KeyBindingManager`. Does it know about `self-insert-command`?
|
|
|
|
|
+ // It knows about `CommandSystem`.
|
|
|
|
|
+
|
|
|
|
|
+ // Let's strictly follow the plan first: Record BOUND commands.
|
|
|
|
|
+ // If self-insert is not bound, it won't be recorded here.
|
|
|
|
|
+ // This effectively means macros won't record typing unless keys are bound.
|
|
|
|
|
+ // THIS IS A PROBLEM.
|
|
|
|
|
+
|
|
|
|
|
+ // Fix: `TuiEditor` (and `GtkEditor`) calls `process_key`.
|
|
|
|
|
+ // If `process_key` returns Unbound, they manually execute self-insert.
|
|
|
|
|
+ // They should ALSO manually record the key if macro is recording.
|
|
|
|
|
+ // `GtkEditor` logic for this is in `on_global_key_pressed`?
|
|
|
|
|
+ // Let's check `GtkEditor::on_global_key_pressed` implementation.
|
|
|
|
|
+ // It probably does similar fallback.
|
|
|
|
|
+
|
|
|
|
|
+ // Ideally, `process_key` should be the single point of truth.
|
|
|
|
|
+ // If a key is "unbound" but printable, maybe `process_key` should treating it as "bound to self-insert"?
|
|
|
|
|
+ // If I change `process_key` to handle printable chars by executing self-insert, I unify behavior AND fix recording.
|
|
|
|
|
+
|
|
|
|
|
+ // Let's modify `process_key` to handle the fallback.
|
|
|
|
|
+ // But `Key` structure splits modifiers.
|
|
|
|
|
+ // Printable means: no modifiers (except Shift?) and is a char?
|
|
|
|
|
+ // Key struct has `base_key`.
|
|
|
|
|
+
|
|
|
|
|
+ // This might be too big of a change for this step.
|
|
|
|
|
+ // Better approach:
|
|
|
|
|
+ // Implement the recording logic in `process_key` for bound keys.
|
|
|
|
|
+ // Then check `GtkEditor` and `TuiEditor` to add recording call for the fallback case.
|
|
|
|
|
+
|
|
|
|
|
+ // Actually, `EditorCore` has `record_key_sequence`.
|
|
|
|
|
+ // I can expose a helper `process_and_record_if_needed(key)`? No.
|
|
|
|
|
+
|
|
|
|
|
+ // Let's stick to `process_key` modification first.
|
|
|
|
|
|
|
|
- // No binding found, clear sequence and return unbound
|
|
|
|
|
clear_partial_sequence();
|
|
clear_partial_sequence();
|
|
|
return KeyProcessingResult(KeyResult::Unbound);
|
|
return KeyProcessingResult(KeyResult::Unbound);
|
|
|
}
|
|
}
|