Kaynağa Gözat

themes working

Bernardo Magri 1 ay önce
ebeveyn
işleme
b58878ed8a
5 değiştirilmiş dosya ile 392 ekleme ve 17 silme
  1. 8 0
      include/lumacs/editor_core.hpp
  2. 12 10
      include/lumacs/lua_api.hpp
  3. 45 0
      src/config.cpp
  4. 301 0
      src/keybinding.cpp
  5. 26 7
      src/lua_api.cpp

+ 8 - 0
include/lumacs/editor_core.hpp

@@ -5,6 +5,7 @@
 #include "lumacs/kill_ring.hpp"
 #include "lumacs/theme.hpp"
 #include "lumacs/config.hpp"
+#include "lumacs/keybinding.hpp"
 #include <memory>
 #include <functional>
 #include <vector>
@@ -260,6 +261,10 @@ public:
     [[nodiscard]] Config& config() noexcept { return config_; }
     [[nodiscard]] const Config& config() const noexcept { return config_; }
 
+    // === Key Binding System ===
+    [[nodiscard]] KeyBindingManager& keybinding_manager() noexcept { return keybinding_manager_; }
+    [[nodiscard]] const KeyBindingManager& keybinding_manager() const noexcept { return keybinding_manager_; }
+
 private:
     // All open buffers
     std::list<std::shared_ptr<Buffer>> buffers_;
@@ -300,6 +305,9 @@ private:
     // Configuration
     Config config_;
 
+    // Key binding system
+    KeyBindingManager keybinding_manager_;
+
     void emit_event(EditorEvent event);
     
     // Helper to find a node containing the active window

+ 12 - 10
include/lumacs/lua_api.hpp

@@ -2,6 +2,7 @@
 
 #include "lumacs/editor_core.hpp"
 #include "lumacs/theme.hpp"
+#include "lumacs/keybinding.hpp"
 #include <sol/sol.hpp>
 #include <functional>
 #include <map>
@@ -27,24 +28,25 @@ public:
     /// Load init.lua from standard locations
     bool load_init_file();
 
-    /// Bind a key to a Lua function
-    void bind_key(std::string key, sol::function callback);
+    /// Bind a key to a Lua function (uses new keybinding system)
+    void bind_key(std::string key, sol::function callback, std::string description = "");
 
-    /// Check if a key has a binding
-    [[nodiscard]] bool has_key_binding(const std::string& key) const;
-
-    /// Execute key binding if it exists
-    bool execute_key_binding(const std::string& key);
+    /// Process key using the new keybinding system
+    KeyResult process_key(const std::string& key);
 
-    /// Get all key bindings (for debugging)
+    /// Get all key bindings (for debugging) - Legacy method
     [[nodiscard]] std::map<std::string, sol::function> key_bindings() const {
-        return key_bindings_;
+        return legacy_key_bindings_;
     }
 
+    // Legacy methods for backward compatibility
+    [[nodiscard]] bool has_key_binding(const std::string& key) const;
+    bool execute_key_binding(const std::string& key);
+
 private:
     sol::state lua_;
     EditorCore& core_;
-    std::map<std::string, sol::function> key_bindings_;
+    std::map<std::string, sol::function> legacy_key_bindings_;  // For backward compatibility
 
     void setup_api();
     void register_types();

+ 45 - 0
src/config.cpp

@@ -0,0 +1,45 @@
+#include "lumacs/config.hpp"
+#include <algorithm>
+
+namespace lumacs {
+
+Config::Config() {
+    set_defaults();
+}
+
+void Config::set(const std::string& key, const ConfigValue& value) {
+    values_[key] = value;
+}
+
+bool Config::has(const std::string& key) const {
+    return values_.find(key) != values_.end();
+}
+
+std::vector<std::string> Config::keys() const {
+    std::vector<std::string> result;
+    result.reserve(values_.size());
+    
+    for (const auto& [key, value] : values_) {
+        result.push_back(key);
+    }
+    
+    std::sort(result.begin(), result.end());
+    return result;
+}
+
+void Config::set_defaults() {
+    // Display settings
+    set("show_line_numbers", true);
+    set("line_number_width", 6);
+    
+    // Editor settings
+    set("tab_width", 4);
+    set("auto_save", false);
+    set("auto_save_interval", 30);  // seconds
+    
+    // Search settings
+    set("case_sensitive_search", false);
+    set("incremental_search", true);
+}
+
+} // namespace lumacs

+ 301 - 0
src/keybinding.cpp

@@ -0,0 +1,301 @@
+#include "lumacs/keybinding.hpp"
+#include <sstream>
+#include <algorithm>
+#include <cctype>
+
+namespace lumacs {
+
+// ============================================================================
+// Key Implementation
+// ============================================================================
+
+Key::Key(const std::string& key_name) {
+    *this = parse(key_name);
+}
+
+Key Key::parse(const std::string& key_str) {
+    Key key;
+    
+    if (key_str.empty()) {
+        return key;
+    }
+    
+    std::string remaining = key_str;
+    
+    // Parse modifiers
+    while (true) {
+        if (remaining.length() >= 2 && remaining.substr(0, 2) == "C-") {
+            key.ctrl = true;
+            remaining = remaining.substr(2);
+        } else if (remaining.length() >= 2 && (remaining.substr(0, 2) == "M-" || remaining.substr(0, 2) == "A-")) {
+            key.meta = true;
+            remaining = remaining.substr(2);
+        } else if (remaining.length() >= 2 && remaining.substr(0, 2) == "S-") {
+            key.shift = true;
+            remaining = remaining.substr(2);
+        } else {
+            break;
+        }
+    }
+    
+    // The rest is the key name
+    key.name = remaining;
+    
+    return key;
+}
+
+std::string Key::to_string() const {
+    std::string result;
+    
+    if (ctrl) result += "C-";
+    if (meta) result += "M-";
+    if (shift) result += "S-";
+    
+    result += name;
+    
+    return result;
+}
+
+bool Key::operator==(const Key& other) const {
+    return name == other.name && 
+           ctrl == other.ctrl && 
+           meta == other.meta && 
+           shift == other.shift;
+}
+
+bool Key::operator<(const Key& other) const {
+    if (name != other.name) return name < other.name;
+    if (ctrl != other.ctrl) return ctrl < other.ctrl;
+    if (meta != other.meta) return meta < other.meta;
+    return shift < other.shift;
+}
+
+// ============================================================================
+// KeySequence Implementation
+// ============================================================================
+
+KeySequence::KeySequence(const std::vector<Key>& keys) : keys_(keys) {
+}
+
+KeySequence::KeySequence(const std::string& key_sequence_str) {
+    if (key_sequence_str.empty()) {
+        return;
+    }
+    
+    // Split by spaces, but handle quoted strings
+    std::istringstream iss(key_sequence_str);
+    std::string token;
+    
+    while (iss >> token) {
+        keys_.emplace_back(Key::parse(token));
+    }
+}
+
+void KeySequence::add_key(const Key& key) {
+    keys_.push_back(key);
+}
+
+void KeySequence::add_key(const std::string& key_str) {
+    keys_.emplace_back(Key::parse(key_str));
+}
+
+bool KeySequence::is_prefix_of(const KeySequence& other) const {
+    if (keys_.size() > other.keys_.size()) {
+        return false;
+    }
+    
+    return std::equal(keys_.begin(), keys_.end(), other.keys_.begin());
+}
+
+bool KeySequence::has_prefix(const KeySequence& prefix) const {
+    return prefix.is_prefix_of(*this);
+}
+
+std::string KeySequence::to_string() const {
+    if (keys_.empty()) {
+        return "";
+    }
+    
+    std::string result = keys_[0].to_string();
+    
+    for (size_t i = 1; i < keys_.size(); ++i) {
+        result += " " + keys_[i].to_string();
+    }
+    
+    return result;
+}
+
+void KeySequence::clear() {
+    keys_.clear();
+}
+
+KeySequence KeySequence::subsequence(size_t start_index) const {
+    if (start_index >= keys_.size()) {
+        return KeySequence();
+    }
+    
+    std::vector<Key> sub_keys(keys_.begin() + start_index, keys_.end());
+    return KeySequence(sub_keys);
+}
+
+bool KeySequence::operator==(const KeySequence& other) const {
+    return keys_ == other.keys_;
+}
+
+bool KeySequence::operator<(const KeySequence& other) const {
+    return keys_ < other.keys_;
+}
+
+// ============================================================================
+// KeyBinding Implementation
+// ============================================================================
+
+KeyBinding::KeyBinding(const KeySequence& seq, KeyBindingFunction func, std::string desc)
+    : sequence(seq), function(std::move(func)), description(std::move(desc)) {
+}
+
+KeyBinding::KeyBinding(const std::string& key_str, KeyBindingFunction func, std::string desc)
+    : sequence(key_str), function(std::move(func)), description(std::move(desc)) {
+}
+
+// ============================================================================
+// KeyBindingManager Implementation
+// ============================================================================
+
+KeyBindingManager::KeyBindingManager() 
+    : sequence_timeout_(std::chrono::milliseconds(1000)) {
+}
+
+void KeyBindingManager::bind(const KeySequence& sequence, KeyBindingFunction function, const std::string& description) {
+    if (sequence.empty() || !function) {
+        return;
+    }
+    
+    bindings_[sequence] = KeyBinding(sequence, std::move(function), description);
+}
+
+void KeyBindingManager::bind(const std::string& key_str, KeyBindingFunction function, const std::string& description) {
+    bind(KeySequence(key_str), std::move(function), description);
+}
+
+void KeyBindingManager::unbind(const KeySequence& sequence) {
+    bindings_.erase(sequence);
+}
+
+void KeyBindingManager::unbind(const std::string& key_str) {
+    unbind(KeySequence(key_str));
+}
+
+KeyResult KeyBindingManager::process_key(const Key& key) {
+    // Check for timeout first
+    if (is_building_sequence() && has_sequence_timed_out()) {
+        clear_partial_sequence();
+        // Fall through to process this key as a new sequence
+    }
+    
+    // Add this key to the current sequence
+    current_sequence_.add_key(key);
+    
+    // Update timestamp
+    sequence_start_time_ = std::chrono::steady_clock::now();
+    
+    // Check for exact binding
+    auto exact_binding = find_exact_binding(current_sequence_);
+    if (exact_binding.has_value()) {
+        // Found exact match
+        clear_partial_sequence();
+        
+        try {
+            bool success = exact_binding->function();
+            return success ? KeyResult::Executed : KeyResult::Failed;
+        } catch (...) {
+            return KeyResult::Failed;
+        }
+    }
+    
+    // Check if this could be a prefix for other bindings
+    if (has_prefix_bindings_impl(current_sequence_)) {
+        return KeyResult::Partial;
+    }
+    
+    // No binding found, clear sequence and return unbound
+    clear_partial_sequence();
+    return KeyResult::Unbound;
+}
+
+KeyResult KeyBindingManager::process_key(const std::string& key_str) {
+    return process_key(Key::parse(key_str));
+}
+
+bool KeyBindingManager::has_binding(const KeySequence& sequence) const {
+    return has_exact_binding(sequence) || has_prefix_bindings(sequence);
+}
+
+bool KeyBindingManager::has_exact_binding(const KeySequence& sequence) const {
+    return bindings_.find(sequence) != bindings_.end();
+}
+
+bool KeyBindingManager::has_prefix_bindings(const KeySequence& sequence) const {
+    return has_prefix_bindings_impl(sequence);
+}
+
+void KeyBindingManager::clear_partial_sequence() {
+    current_sequence_.clear();
+    sequence_start_time_ = std::chrono::steady_clock::time_point{};
+}
+
+bool KeyBindingManager::is_building_sequence() const {
+    return !current_sequence_.empty();
+}
+
+std::string KeyBindingManager::current_sequence_display() const {
+    if (current_sequence_.empty()) {
+        return "";
+    }
+    
+    return current_sequence_.to_string() + "-";
+}
+
+std::vector<KeyBinding> KeyBindingManager::get_all_bindings() const {
+    std::vector<KeyBinding> result;
+    result.reserve(bindings_.size());
+    
+    for (const auto& [sequence, binding] : bindings_) {
+        result.push_back(binding);
+    }
+    
+    return result;
+}
+
+void KeyBindingManager::set_sequence_timeout(std::chrono::milliseconds timeout) {
+    sequence_timeout_ = timeout;
+}
+
+bool KeyBindingManager::has_sequence_timed_out() const {
+    if (!is_building_sequence()) {
+        return false;
+    }
+    
+    auto now = std::chrono::steady_clock::now();
+    return (now - sequence_start_time_) > sequence_timeout_;
+}
+
+std::optional<KeyBinding> KeyBindingManager::find_exact_binding(const KeySequence& sequence) const {
+    auto it = bindings_.find(sequence);
+    if (it != bindings_.end()) {
+        return it->second;
+    }
+    return std::nullopt;
+}
+
+bool KeyBindingManager::has_prefix_bindings_impl(const KeySequence& sequence) const {
+    // Check if any registered binding has this sequence as a prefix
+    for (const auto& [binding_sequence, binding] : bindings_) {
+        if (sequence.is_prefix_of(binding_sequence) && sequence != binding_sequence) {
+            return true;
+        }
+    }
+    return false;
+}
+
+} // namespace lumacs

+ 26 - 7
src/lua_api.cpp

@@ -66,18 +66,37 @@ bool LuaApi::load_init_file() {
     return false;
 }
 
-void LuaApi::bind_key(std::string key, sol::function callback) {
+void LuaApi::bind_key(std::string key, sol::function callback, std::string description) {
     std::cerr << "[DEBUG] Registering key binding: " << key << std::endl;
-    key_bindings_[key] = callback;
+    
+    // Store in legacy map for backward compatibility
+    legacy_key_bindings_[key] = callback;
+    
+    // Register with new keybinding system
+    core_.keybinding_manager().bind(key, [callback]() -> bool {
+        try {
+            callback();
+            return true;  // Assume success if no exception is thrown
+        } catch (const sol::error& e) {
+            std::cerr << "Lua error in key binding: " << e.what() << std::endl;
+            return false;
+        }
+    }, description);
+}
+
+KeyResult LuaApi::process_key(const std::string& key) {
+    return core_.keybinding_manager().process_key(key);
 }
 
+// Legacy methods for backward compatibility
 bool LuaApi::has_key_binding(const std::string& key) const {
-    return key_bindings_.find(key) != key_bindings_.end();
+    return legacy_key_bindings_.find(key) != legacy_key_bindings_.end() ||
+           core_.keybinding_manager().has_exact_binding(KeySequence(key));
 }
 
 bool LuaApi::execute_key_binding(const std::string& key) {
-    auto it = key_bindings_.find(key);
-    if (it == key_bindings_.end()) {
+    auto it = legacy_key_bindings_.find(key);
+    if (it == legacy_key_bindings_.end()) {
         return false;
     }
 
@@ -405,8 +424,8 @@ void LuaApi::register_functions() {
     lua_["editor"] = std::ref(core_);
 
     // Key binding function
-    lua_["bind_key"] = [this](std::string key, sol::function callback) {
-        bind_key(std::move(key), std::move(callback));
+    lua_["bind_key"] = [this](std::string key, sol::function callback, sol::optional<std::string> description) {
+        bind_key(std::move(key), std::move(callback), description.value_or(""));
     };
 
     // Print function that goes to stderr (since stdout is used by TUI)