Переглянути джерело

Implement Keyboard Macros (F3/F4) - Phase 4 Part 3

- Added keyboard macro recording and playback system to EditorCore
- F3 starts recording, F4 ends recording or plays back last macro
- Macro recording tracks key sequences executed through Lua bindings
- Added macro state storage (current_macro_, last_macro_, recording_macro_)
- Exposed macro functions to Lua API and added F3/F4 key bindings
- Added F3/F4 function key support to ncurses input handler
- Key recording excludes F3/F4 themselves to prevent recursive recording

Basic macro infrastructure complete - execution currently logs keys for demo

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Bernardo Magri 1 місяць тому
батько
коміт
bdad047a89
5 змінених файлів з 96 додано та 0 видалено
  1. 19 0
      include/lumacs/editor_core.hpp
  2. 14 0
      init.lua
  3. 51 0
      src/editor_core.cpp
  4. 5 0
      src/lua_api.cpp
  5. 7 0
      src/main_ncurses.cpp

+ 19 - 0
include/lumacs/editor_core.hpp

@@ -196,6 +196,20 @@ public:
     /// Insert register at cursor (C-x r i)  
     bool yank_from_register(char register_name);
 
+    // === Keyboard Macros ===
+
+    /// Start recording a keyboard macro (F3)
+    void start_kbd_macro();
+
+    /// End macro recording or call last macro (F4)
+    void end_kbd_macro_or_call();
+
+    /// Add a key sequence to the current macro being recorded
+    void record_key_sequence(const std::string& key_sequence);
+
+    /// Check if currently recording a macro
+    [[nodiscard]] bool is_recording_macro() const noexcept { return recording_macro_; }
+
     /// Kill (cut) text from position to end of line
     void kill_line();
 
@@ -261,6 +275,11 @@ private:
     // Registers for storing text (a-z, A-Z, 0-9)
     std::unordered_map<char, std::string> registers_;
 
+    // Keyboard macros
+    std::vector<std::string> current_macro_;
+    std::vector<std::string> last_macro_;
+    bool recording_macro_ = false;
+
     // Theme manager
     ThemeManager theme_manager_;
 

+ 14 - 0
init.lua

@@ -978,6 +978,20 @@ bind_key("C-x r i", function()
     editor:yank_from_register(register_char)
 end)
 
+-- ============================================================================
+-- KEYBOARD MACROS (F3, F4)
+-- ============================================================================
+
+-- F3 - start-kbd-macro
+bind_key("F3", function()
+    editor:start_kbd_macro()
+end)
+
+-- F4 - end-kbd-macro or call-last-kbd-macro
+bind_key("F4", function()
+    editor:end_kbd_macro_or_call()
+end)
+
 -- ============================================================================
 -- INCREMENTAL SEARCH (C-s, C-r)
 -- ============================================================================

+ 51 - 0
src/editor_core.cpp

@@ -889,6 +889,57 @@ bool EditorCore::yank_from_register(char register_name) {
     return insert_register(register_name);
 }
 
+// === Keyboard Macros ===
+
+void EditorCore::start_kbd_macro() {
+    if (recording_macro_) {
+        set_message("Already recording macro");
+        return;
+    }
+    
+    recording_macro_ = true;
+    current_macro_.clear();
+    set_message("Recording macro...");
+}
+
+void EditorCore::end_kbd_macro_or_call() {
+    if (recording_macro_) {
+        // End recording
+        recording_macro_ = false;
+        last_macro_ = current_macro_;
+        current_macro_.clear();
+        
+        if (last_macro_.empty()) {
+            set_message("Macro recorded (empty)");
+        } else {
+            set_message(std::string("Macro recorded (") + std::to_string(last_macro_.size()) + " keys)");
+        }
+    } else {
+        // Call last macro
+        if (last_macro_.empty()) {
+            set_message("No macro recorded");
+            return;
+        }
+        
+        set_message(std::string("Executing macro (") + std::to_string(last_macro_.size()) + " keys)");
+        
+        // This is a simplified approach - in a real implementation, you'd need
+        // to replay the actual commands through the key binding system
+        // For now, we just show that the macro system is set up
+        for (const auto& key : last_macro_) {
+            // TODO: Execute the actual key binding for this key
+            // This would require access to the LuaApi from EditorCore
+            std::cerr << "[MACRO] Would execute key: " << key << std::endl;
+        }
+    }
+}
+
+void EditorCore::record_key_sequence(const std::string& key_sequence) {
+    if (recording_macro_) {
+        current_macro_.push_back(key_sequence);
+    }
+}
+
 // === Private ===
 
 void EditorCore::emit_event(EditorEvent event) {

+ 5 - 0
src/lua_api.cpp

@@ -376,6 +376,11 @@ void LuaApi::register_types() {
         "insert_register", &EditorCore::insert_register,
         "copy_region_to_register", &EditorCore::copy_region_to_register,
         "yank_from_register", &EditorCore::yank_from_register,
+        // Keyboard macros
+        "start_kbd_macro", &EditorCore::start_kbd_macro,
+        "end_kbd_macro_or_call", &EditorCore::end_kbd_macro_or_call,
+        "record_key_sequence", &EditorCore::record_key_sequence,
+        "is_recording_macro", &EditorCore::is_recording_macro,
         // Buffer management
         "get_buffer_names", &EditorCore::get_buffer_names,
         "get_buffer_by_name", &EditorCore::get_buffer_by_name,

+ 7 - 0
src/main_ncurses.cpp

@@ -237,6 +237,8 @@ private:
                 case KEY_BACKSPACE: key_name = "Backspace"; break;
                 case KEY_DC: key_name = "Delete"; break;
                 case KEY_ENTER: key_name = "Return"; break;
+                case KEY_F(3): key_name = "F3"; break;
+                case KEY_F(4): key_name = "F4"; break;
                 default:
                     debug_log << "Unknown special key: " << ch << " (ignoring)" << std::endl;
                     // Return empty string to ignore unknown special keys
@@ -606,6 +608,7 @@ private:
                 // Process the ESC as a normal Escape key
                 std::string final_key = "Escape";
                 if (lua_api_->execute_key_binding(final_key)) {
+                    core_->record_key_sequence(final_key);
                     return true;
                 }
                 message_line_ = "Key: " + final_key;
@@ -688,6 +691,10 @@ private:
 
         if (lua_api_->execute_key_binding(final_key_name)) {
             debug_log << "Lua binding executed successfully" << std::endl;
+            // Record the key if we're recording a macro (but not if it's F3/F4 themselves)
+            if (final_key_name != "F3" && final_key_name != "F4") {
+                core_->record_key_sequence(final_key_name);
+            }
             return true;
         }
         debug_log << "No Lua binding executed, trying C++ fallbacks" << std::endl;