فهرست منبع

Implement Minibuffer History (M-p/M-n) - Phase 4 Part 2

- Added history storage for command, buffer switch, kill buffer, and isearch modes
- Implemented previous_history() and next_history() navigation functions
- Added M-p and M-n key handling for cycling through minibuffer history
- History is added when commands are executed and reset on mode entry
- Each mode maintains separate history with 100-item limit
- History navigation resets on user edits for intuitive behavior

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

Co-Authored-By: Claude <noreply@anthropic.com>
Bernardo Magri 1 ماه پیش
والد
کامیت
bfd7f99cc8
1فایلهای تغییر یافته به همراه102 افزوده شده و 5 حذف شده
  1. 102 5
      src/main_ncurses.cpp

+ 102 - 5
src/main_ncurses.cpp

@@ -7,6 +7,7 @@
 #include <chrono>
 #include <string>
 #include <sstream>
+#include <algorithm>
 
 // Global debug log
 std::ofstream debug_log("lumacs_debug.log");
@@ -44,7 +45,9 @@ public:
         
         // Set initial viewport size (leave room for status and message lines)
         int content_height = height_ - 2; // -1 for status, -1 for message
-        int content_width = width_ - 6;   // -6 for line numbers (3 digits + " | ")
+        bool show_line_numbers = core_->config().get<bool>("show_line_numbers", true);
+        int line_number_width = show_line_numbers ? core_->config().get<int>("line_number_width", 6) : 0;
+        int content_width = width_ - line_number_width;
         core_->set_viewport_size(content_width, content_height);
         
         // Then LuaApi, which depends on Core
@@ -107,7 +110,9 @@ public:
                 height_ = new_height;
                 width_ = new_width;
                 int content_height = height_ - 2;
-                int content_width = width_ - 6;
+                bool show_line_numbers = core_->config().get<bool>("show_line_numbers", true);
+                int line_number_width = show_line_numbers ? core_->config().get<int>("line_number_width", 6) : 0;
+                int content_width = width_ - line_number_width;
                 core_->set_viewport_size(content_width, content_height);
                 debug_log << "Screen resized to: " << width_ << "x" << height_ << std::endl;
                 debug_log << "Content area: " << content_width << "x" << content_height << std::endl;
@@ -158,6 +163,13 @@ private:
     size_t completion_index_ = 0;
     std::string completion_prefix_;
 
+    // Minibuffer history
+    std::vector<std::string> command_history_;
+    std::vector<std::string> buffer_switch_history_;
+    std::vector<std::string> kill_buffer_history_;
+    std::vector<std::string> isearch_history_;
+    size_t history_index_ = 0;
+
     // Prefix handling
     bool waiting_for_prefix_ = false;
     std::string prefix_key_;
@@ -178,14 +190,17 @@ private:
             mode_ = Mode::Command;
             command_buffer_.clear();
             reset_completion();
+            reset_history_navigation();
         } else if (event == EditorEvent::BufferSwitchMode) {
             mode_ = Mode::BufferSwitch;
             command_buffer_.clear();
             reset_completion();
+            reset_history_navigation();
         } else if (event == EditorEvent::KillBufferMode) {
             mode_ = Mode::KillBuffer;
             command_buffer_.clear();
             reset_completion();
+            reset_history_navigation();
         } else if (event == EditorEvent::ISearchMode) {
             mode_ = Mode::ISearch;
             isearch_query_.clear();
@@ -273,6 +288,64 @@ private:
     }
     
     // Helper to filter candidates based on prefix
+    // History management
+    std::vector<std::string>& get_current_history() {
+        switch (mode_) {
+            case Mode::Command: return command_history_;
+            case Mode::BufferSwitch: return buffer_switch_history_;
+            case Mode::KillBuffer: return kill_buffer_history_;
+            case Mode::ISearch: return isearch_history_;
+            default: return command_history_;
+        }
+    }
+    
+    void add_to_history(const std::string& entry) {
+        if (entry.empty()) return;
+        
+        auto& history = get_current_history();
+        
+        // Remove if already exists (move to front)
+        auto it = std::find(history.begin(), history.end(), entry);
+        if (it != history.end()) {
+            history.erase(it);
+        }
+        
+        // Add to front
+        history.insert(history.begin(), entry);
+        
+        // Limit history size
+        const size_t MAX_HISTORY = 100;
+        if (history.size() > MAX_HISTORY) {
+            history.resize(MAX_HISTORY);
+        }
+    }
+    
+    void previous_history() {
+        auto& history = get_current_history();
+        if (history.empty()) return;
+        
+        if (history_index_ < history.size()) {
+            command_buffer_ = history[history_index_];
+            history_index_++;
+        }
+    }
+    
+    void next_history() {
+        auto& history = get_current_history();
+        if (history.empty() || history_index_ == 0) return;
+        
+        history_index_--;
+        if (history_index_ == 0) {
+            command_buffer_ = "";
+        } else {
+            command_buffer_ = history[history_index_ - 1];
+        }
+    }
+    
+    void reset_history_navigation() {
+        history_index_ = 0;
+    }
+
     void update_completion_candidates(const std::string& prefix) {
         auto all_buffers = core_->get_buffer_names();
         completion_candidates_.clear();
@@ -448,6 +521,9 @@ private:
 
             // Return - execute
             if (ch == '\n' || ch == '\r') {
+                // Add to history before execution
+                add_to_history(command_buffer_);
+                
                 if (mode_ == Mode::Command) {
                     execute_command(command_buffer_);
                 } else if (mode_ == Mode::FindFile) {
@@ -482,6 +558,21 @@ private:
                 mode_ = Mode::Normal;
                 command_buffer_.clear();
                 reset_completion();
+                reset_history_navigation();
+                return true;
+            }
+
+            // M-p (Alt+p) - Previous history
+            if (ch == '\x90') { // Meta+p
+                previous_history();
+                reset_completion();
+                return true;
+            }
+            
+            // M-n (Alt+n) - Next history  
+            if (ch == '\x8E') { // Meta+n
+                next_history();
+                reset_completion();
                 return true;
             }
 
@@ -490,6 +581,7 @@ private:
                 if (!command_buffer_.empty()) {
                     command_buffer_.pop_back();
                     reset_completion(); // Reset completion on edit
+                    reset_history_navigation(); // Reset history on edit
                 } else {
                     mode_ = Mode::Normal;
                 }
@@ -816,8 +908,11 @@ private:
     void render_window(std::shared_ptr<Window> window, int x, int y, int width, int height) {
         if (!window) return;
         
+        // Check configuration for line numbers
+        bool show_line_numbers = core_->config().get<bool>("show_line_numbers", true);
+        int line_number_width = show_line_numbers ? core_->config().get<int>("line_number_width", 6) : 0;
+        
         // Update window viewport size
-        int line_number_width = 6; // "999 │ "
         int content_width = width - line_number_width;
         window->set_viewport_size(content_width, height);
         
@@ -841,8 +936,10 @@ private:
             move(y + screen_y, x);
             for (int i = 0; i < width; ++i) addch(' ');
             
-            // Line number
-            mvprintw(y + screen_y, x, "%3zu │ ", buffer_line_idx + 1);
+            // Line number (if enabled)
+            if (show_line_numbers) {
+                mvprintw(y + screen_y, x, "%3zu │ ", buffer_line_idx + 1);
+            }
 
             // Line content with syntax highlighting
             if (!line_text.empty()) {