#pragma once #include "lumacs/buffer.hpp" #include "lumacs/window.hpp" #include "lumacs/kill_ring.hpp" #include "lumacs/theme.hpp" #include "lumacs/config.hpp" #include "lumacs/keybinding.hpp" #include "lumacs/modeline.hpp" // #include "lumacs/minibuffer_manager.hpp" // Changed to forward declaration below #include "lumacs/ui_interface.hpp" // Include for EditorEvent #include #include #include #include #include #include // For std::chrono::steady_clock #include // For std::optional namespace lumacs { class LuaApi; // Forward declaration class CommandSystem; // Forward declaration class CompletionSystem; // Forward declaration class MinibufferManager; // Forward declaration /// @brief Represents a node in the window layout tree. struct LayoutNode { enum class Type { Leaf, HorizontalSplit, VerticalSplit }; Type type; // If Leaf std::shared_ptr window; // If Split std::shared_ptr child1; std::shared_ptr child2; float ratio = 0.5f; // Split ratio (0.0 to 1.0) LayoutNode(std::shared_ptr w) : type(Type::Leaf), window(w) {} LayoutNode(Type t, std::shared_ptr c1, std::shared_ptr c2) : type(t), child1(c1), child2(c2) {} }; /// @brief Core logic of the Lumacs editor, independent of the UI framework. /// /// This class acts as the central controller/facade for the editor's logic. /// It manages buffers, windows, the kill ring, registers, macros, configuration, /// and subsystems like the command system and Lua API. /// It emits events to notify the UI (IEditorView) of state changes. class EditorCore { public: EditorCore(); ~EditorCore(); // Disable copy, allow move EditorCore(const EditorCore&) = delete; EditorCore& operator=(const EditorCore&) = delete; EditorCore(EditorCore&&) noexcept = default; EditorCore& operator=(EditorCore&&) noexcept = default; // === Message System === /// @brief Display a message to the user (usually in the minibuffer). /// @param msg The message string to display. void set_message(std::string msg) { last_message_ = std::move(msg); message_clear_time_ = std::chrono::steady_clock::now() + std::chrono::seconds(5); // Clear after 5 seconds emit_event(EditorEvent::Message); // Use this for transient messages } /// @brief Get the last set message. const std::string& last_message() const { return last_message_; } /// @brief Check if a message is set and if its display time has expired. /// Clears the message if expired. void check_and_clear_message() { if (message_clear_time_.has_value() && std::chrono::steady_clock::now() > message_clear_time_.value()) { last_message_.clear(); message_clear_time_.reset(); emit_event(EditorEvent::TransientMessageCleared); // Notify UI } } // === Actions === // These methods trigger specific input modes in the UI. void enter_command_mode() { emit_event(EditorEvent::CommandMode); } void enter_buffer_switch_mode() { emit_event(EditorEvent::BufferSwitchMode); } void enter_kill_buffer_mode() { emit_event(EditorEvent::KillBufferMode); } void enter_find_file_mode() { emit_event(EditorEvent::FindFileMode); } void enter_theme_selection_mode() { emit_event(EditorEvent::ThemeSelectionMode); } void enter_isearch_mode() { emit_event(EditorEvent::ISearchMode); } void enter_isearch_backward_mode() { emit_event(EditorEvent::ISearchBackwardMode); } // === Buffer Management === /// @brief Get the active buffer (associated with the active window). [[nodiscard]] const Buffer& buffer() const noexcept; [[nodiscard]] Buffer& buffer() noexcept; /// @brief Load a file into a buffer and display it in the active window. /// @param path Filesystem path to load. /// @return true if successful, false otherwise. bool load_file(const std::filesystem::path& path); /// @brief Create a new empty buffer (e.g., *scratch*) and display it. /// @param name The name of the new buffer. void new_buffer(std::string name = "*scratch*"); /// @brief Get a list of names of all open buffers. [[nodiscard]] std::vector get_buffer_names() const; /// @brief Find a buffer by its name. /// @return Shared pointer to the buffer, or nullptr if not found. [[nodiscard]] std::shared_ptr get_buffer_by_name(const std::string& name); /// @brief Switch the active window to display the specified buffer. /// @param name The name of the buffer to switch to. /// @return true if successful. bool switch_buffer_in_window(const std::string& name); /// @brief Close (kill) a buffer. /// @param name The name of the buffer to close. /// @return true if closed, false if it doesn't exist or is the last buffer. bool close_buffer(const std::string& name); /// @brief Structure containing summary information about a buffer. struct BufferInfo { std::string name; size_t size; bool modified; std::string mode; std::optional filepath; }; /// @brief Get detailed information about all buffers. [[nodiscard]] std::vector get_all_buffer_info() const; // === Window Management === /// @brief Split the active window horizontally (active becomes top). void split_horizontally(); /// @brief Split the active window vertically (active becomes left). void split_vertically(); /// @brief Close the active window and remove it from the layout tree. void close_active_window(); /// @brief Move focus to the next window in the tree traversal order. void next_window(); /// @brief Safe version of next_window (deprecated, alias for next_window). void next_window_safe(); /// @brief Get the currently focused window. std::shared_ptr active_window() const { return active_window_; } /// @brief Set the active window explicitly (must exist in the tree). bool set_active_window(std::shared_ptr window); /// @brief Get the root node of the window layout tree. std::shared_ptr root_layout() const { return root_node_; } // === Cursor Management (Proxies to active window) === [[nodiscard]] Position cursor() const noexcept; void set_cursor(Position pos); // === Cursor Movement (Proxies to active window) === void move_up(); void move_down(); void move_left(); void move_right(); void move_to_line_start(); void move_to_line_end(); void move_forward_word(); void move_backward_word(); void page_up(); void page_down(); void goto_beginning(); void goto_end(); void goto_line(size_t line); // === Viewport Management (Proxies to active window) === const Viewport& viewport() const noexcept; void set_viewport_size(int width, int height); void adjust_scroll(); std::pair visible_line_range() const; // === Event Callbacks === using EventCallback = std::function; /// @brief Register a callback to be notified of editor events. void on_event(EventCallback callback) { event_callbacks_.push_back(std::move(callback)); } /// @brief Clear all registered event callbacks. void clear_event_callbacks() { event_callbacks_.clear(); } // === Actions === /// @brief Request the application to quit. void request_quit() { emit_event(EditorEvent::Quit); } // === Undo/Redo === bool undo(); bool redo(); bool can_undo() const; bool can_redo() const; // === Kill Ring === /// @brief Access the global Kill Ring (clipboard history). [[nodiscard]] KillRing& kill_ring() noexcept { return kill_ring_; } [[nodiscard]] const KillRing& kill_ring() const noexcept { return kill_ring_; } // === Registers === /// @brief Save text to a named register. void copy_to_register(char register_name, const std::string& text); /// @brief Insert text from a named register. bool insert_register(char register_name); /// @brief Copy the active region to a register (C-x r s). void copy_region_to_register(char register_name); /// @brief Insert text from a register (C-x r i). bool yank_from_register(char register_name); // === Keyboard Macros === /// @brief Start recording a keyboard macro (F3). void start_kbd_macro(); /// @brief Stop recording or execute the last macro (F4). void end_kbd_macro_or_call(); /// @brief Record a key sequence if macro recording is active. void record_key_sequence(const std::string& key_sequence); /// @brief Check if macro recording is currently active. [[nodiscard]] bool is_recording_macro() const noexcept { return recording_macro_; } // === Rectangles === /// @brief Kill (cut) a rectangular region (C-x r k). void kill_rectangle(); /// @brief Yank (paste) the last killed rectangle (C-x r y). void yank_rectangle(); /// @brief Replace a rectangular region with a string (C-x r t). void string_rectangle(const std::string& text); // === Standard Editing Commands === /// @brief Kill text from cursor to end of line (C-k). void kill_line(); /// @brief Kill the text in the active region (C-w). void kill_region(); /// @brief Copy the active region to the kill ring (M-w). void copy_region_as_kill(); /// @brief Yank (paste) from the kill ring (C-y). void yank(); /// @brief Replace the just-yanked text with the next item in kill ring (M-y). void yank_pop(); /// @brief Kill word forward (M-d). void kill_word(); /// @brief Kill word backward (M-Backspace). void backward_kill_word(); // === Theme Management === [[nodiscard]] ThemeManager& theme_manager() noexcept { return theme_manager_; } [[nodiscard]] const ThemeManager& theme_manager() const noexcept { return theme_manager_; } /// @brief Set the active theme by name. void set_theme(const std::string& name) { theme_manager_.set_active_theme(name); } /// @brief Get the currently active theme. [[nodiscard]] std::shared_ptr active_theme() const { return theme_manager_.active_theme(); } // === Configuration === [[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_; } // === Lua API === [[nodiscard]] LuaApi* lua_api() const { return lua_api_.get(); } // === Command System === [[nodiscard]] CommandSystem& command_system() noexcept { return *command_system_; } [[nodiscard]] const CommandSystem& command_system() const noexcept { return *command_system_; } // === Modeline Manager === [[nodiscard]] ModelineManager& modeline_manager() noexcept { return modeline_manager_; } [[nodiscard]] const ModelineManager& modeline_manager() const noexcept { return modeline_manager_; } // === Minibuffer Manager === [[nodiscard]] MinibufferManager& minibuffer_manager() noexcept { return *minibuffer_manager_; } [[nodiscard]] const MinibufferManager& minibuffer_manager() const noexcept { return *minibuffer_manager_; } // === Completion System === [[nodiscard]] CompletionSystem& completion_system() noexcept { return *completion_system_; } [[nodiscard]] const CompletionSystem& completion_system() const noexcept { return *completion_system_; } private: std::list> buffers_; // Word movement helpers Position calculate_forward_word_pos(Position start_pos); Position calculate_backward_word_pos(Position start_pos); // Window layout std::shared_ptr root_node_; std::shared_ptr active_window_; std::string last_message_; std::optional message_clear_time_; std::vector event_callbacks_; // Kill ring for cut/copy/paste KillRing kill_ring_; // Last yank position (for yank-pop) std::optional last_yank_start_; std::optional last_yank_end_; // Registers for storing text (a-z, A-Z, 0-9) std::unordered_map registers_; // Keyboard macros std::vector current_macro_; std::vector last_macro_; bool recording_macro_ = false; // Rectangle storage std::vector rectangle_kill_ring_; // Subsystems ThemeManager theme_manager_; Config config_; std::unique_ptr command_system_; // Must be declared before KeyBindingManager KeyBindingManager keybinding_manager_; std::unique_ptr lua_api_; ModelineManager modeline_manager_; void emit_event(EditorEvent event); // Helper to find a node containing the active window LayoutNode* find_parent_node(LayoutNode* root, std::shared_ptr target); // Helper to collect all windows in traversal order void collect_windows(LayoutNode* node, std::vector>& windows); }; } // namespace lumacs