Explorar el Código

refactor(minibuffer): polish ISearch integration and TUI rendering

Exposed MinibufferManager::start_isearch for explicit direction control. Updated GtkEditor and TuiEditor to handle ISearchBackwardMode correctly. Implemented immediate 'y'/'n' handling for y_or_n_p mode. Fixed TUI minibuffer completion rendering to avoid artifacts.
Bernardo Magri hace 1 mes
padre
commit
ba648df3f8

+ 3 - 1
documentation/PLAN.md

@@ -138,6 +138,7 @@ Lumacs/
 - ✅ **GTK Enhancements**: (Fully completed - Phase A).
 - ✅ **Minibuffer & Command System**: Minibuffer core logic, history management, and completion are fully centralized and integrated with the Command System (Phases Z and C completed).
     - **Advanced Completion UI**: Completed (Implemented popup completion window with descriptions and better visual feedback).
+    - **ISearch & TUI Polish**: ✅ Fixed. Exposed ISearch direction control, fixed TUI rendering overlap, and implemented `y_or_n_p` input handling.
 - ✅ **Theme System Refactoring**:
     - [x] Implemented `editor:create_and_register_theme` Lua API to allow theme definition from Lua.
     - [x] Factored all hardcoded C++ themes (`default`, `everforest-dark`, `dracula`, `solarized-dark`, `nord`, `gruvbox-light`) into individual Lua files (`lua/themes/*.lua`).
@@ -148,6 +149,7 @@ Lumacs/
         - **Missing set-theme command**: Added `set-theme` command to `defaults.hpp` as a fundamental command that theme switching functions depend on.
         - **Active theme access issue**: Fixed incorrect `active_theme` property access in `lua/themes/themes_init.lua` - changed from `editor.theme_manager.active_theme` to `editor.theme_manager:active_theme()` function calls.
         - **Theme system fully functional**: All 6 themes (default, dracula, everforest-dark, gruvbox-light, nord, solarized-dark) load correctly, theme switching works via `M-x set-theme`, `C-x t` keybindings work (including `C-x t l` for listing), and theme cycling is operational.
+        - **Theme Cache Invalidation**: ✅ Fixed. Implemented `EditorEvent::ThemeChanged` and `GtkRenderer::invalidate_cache()` to ensure UI updates immediately when a new theme is applied.
 - ✅ **Phase 15 Polishing**: Successfully addressed GTK Cleanup and Modeline Refactor.
 - ✅ **Plugin Management**: Implemented dynamic loading and lifecycle management of Lua plugins.
 - ✅ **Lua Debugging**: Integrated basic remote debugging support for Lua scripts via MobDebug.
@@ -174,7 +176,7 @@ Lumacs/
 - **Build System**: CMake-based with proper dependency management
 - **Rendering Performance**: ✅ Fixed. Implemented line-based rendering cache in GtkRenderer to optimize drawing of unchanged text lines, especially during scrolling and minor edits. Dynamic elements like cursor and selection are composited on top.
 - **Focus Stability**: GTK frontend caches active_window_ during redraw cycles to prevent race conditions in multi-window async rendering
-- **GTK Popup**: `GtkCompletionPopup` logic is stubbed to fix build; needs full GTK4 migration (Popover).
+- **GTK Popup**: Uses `Gtk::Popover` but needs refinement for positioning and styling.
 - **TUI ISearch**: ISearch highlighting temporarily disabled in TUI.
 - **Backspace Bug**: ✅ Fixed. Was a logical error in Lua's `lumacs_backward_delete_char` function regarding position calculation for `erase_char` and cursor update.
 - **`EditorCoreTest.MoveCursorRight` Disabled**: ✅ Fixed. Re-enabled and passing.

+ 3 - 2
include/lumacs/minibuffer_manager.hpp

@@ -67,6 +67,9 @@ public:
     std::optional<Range> get_isearch_match_range() const { return isearch_match_range_; }
     bool is_isearch_failed() const { return isearch_failed_; }
 
+    // ISearch control methods
+    void start_isearch(bool forward);
+
     /// @brief Parses a command string and executes the corresponding command.
 
     /// @brief Parses a command string and executes the corresponding command.
@@ -102,8 +105,6 @@ private:
     bool isearch_direction_forward_ = true; // True for forward search, false for backward
     std::string last_search_query_; // Last successful or attempted query
 
-    // ISearch control methods
-    void start_isearch(bool forward);
     void update_isearch(const std::string& query);
     void next_isearch_match();
     void previous_isearch_match();

+ 10 - 1
src/gtk_editor.cpp

@@ -157,8 +157,17 @@ void GtkEditor::handle_editor_event(EditorEvent event) {
     } else if (event == EditorEvent::ISearchMode) {
         core_->minibuffer_manager().activate_minibuffer(
             MinibufferMode::ISearch, "I-search: ",
-            [this](const std::string& input) { /* TODO: Implement actual isearch logic */ core_->set_message("I-search: " + input); }, nullptr
+            [](const std::string&) { /* Submit handled by return key in isearch */ }, 
+            nullptr
         );
+        core_->minibuffer_manager().start_isearch(true);
+    } else if (event == EditorEvent::ISearchBackwardMode) {
+        core_->minibuffer_manager().activate_minibuffer(
+            MinibufferMode::ISearch, "I-search backward: ",
+            [](const std::string&) { /* Submit handled by return key in isearch */ }, 
+            nullptr
+        );
+        core_->minibuffer_manager().start_isearch(false);
     } else if (event == EditorEvent::TransientMessageCleared) {
         // Force redraw to clear the message
         if (content_widget_) queue_redraw_all_windows(content_widget_);

+ 18 - 0
src/minibuffer_manager.cpp

@@ -116,6 +116,24 @@ bool MinibufferManager::handle_key_event(const std::string& key_name) {
         return false;
     }
 
+    if (current_mode_ == MinibufferMode::y_or_n_p) {
+        if (key_name == "y" || key_name == "Y") {
+            if (on_submit_callback_) on_submit_callback_("y");
+            deactivate_minibuffer();
+            return true;
+        } else if (key_name == "n" || key_name == "N") {
+            if (on_submit_callback_) on_submit_callback_("n");
+            deactivate_minibuffer();
+            return true;
+        } else if (key_name == "Escape" || key_name == "C-g") {
+             if (on_cancel_callback_) on_cancel_callback_();
+             deactivate_minibuffer();
+             return true;
+        }
+        // Ignore other keys
+        return true;
+    }
+
     // --- Generic Minibuffer Handling (for other modes) ---
     if (key_name == "Return") {
         add_to_history();

+ 13 - 6
src/tui_editor.cpp

@@ -234,15 +234,17 @@ void TuiEditor::handle_editor_event(EditorEvent event) {
     } else if (event == EditorEvent::ISearchMode) {
         core_->minibuffer_manager().activate_minibuffer(
             MinibufferMode::ISearch, "I-search: ",
-            [this](const std::string& input) { /* TODO: Implement actual isearch logic */ core_->set_message("I-search query: " + input); },
+            [](const std::string&) { },
             [this]() { core_->set_message("Cancelled I-search"); }
         );
+        core_->minibuffer_manager().start_isearch(true);
     } else if (event == EditorEvent::ISearchBackwardMode) {
         core_->minibuffer_manager().activate_minibuffer(
             MinibufferMode::ISearch, "I-search backward: ",
-            [this](const std::string& input) { /* TODO: Implement actual isearch logic */ core_->set_message("I-search backward query: " + input); },
+            [](const std::string&) { },
             [this]() { core_->set_message("Cancelled I-search backward"); }
         );
+        core_->minibuffer_manager().start_isearch(false);
     } else if (event == EditorEvent::TransientMessageCleared) {
         // Redraw to clear the message from the screen
         render();
@@ -651,18 +653,23 @@ void TuiEditor::render_message_line() {
         
         mvprintw(msg_y, 0, "%s", display_text.c_str());
 
-        // Display completion candidates below the input line
+        // Display completion candidates below the input line (conceptually, but physically above if at bottom)
         auto candidates = core_->minibuffer_manager().get_completion_candidates();
-        if (!candidates.empty()) {
+        if (!candidates.empty() && msg_y > 0) {
+            // Move up one line to display completions above the current minibuffer line
+            move(msg_y - 1, 0);
+            clrtoeol(); // Clear the line first to remove artifacts from underlying window
+            
             std::string completion_display;
             for (size_t i = 0; i < candidates.size() && static_cast<int>(completion_display.length()) < width_ - 5; ++i) {
                 if (!completion_display.empty()) completion_display += " ";
-                completion_display += candidates[i].display_text; // Use display_text
+                completion_display += candidates[i].display_text; 
             }
             if (static_cast<int>(completion_display.length()) >= width_ - 5) {
                 completion_display = completion_display.substr(0, width_ - 8) + "...";
             }
-            // Move up one line to display completions above the current minibuffer line
+            
+            // Render with a different color/attribute if possible
             mvprintw(msg_y - 1, 0, "%s", completion_display.c_str());
         }