| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- #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 <memory>
- #include <functional>
- #include <vector>
- #include <list>
- #include <unordered_map>
- #include <chrono> // For std::chrono::steady_clock
- #include <optional> // 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> window;
-
- // If Split
- std::shared_ptr<LayoutNode> child1;
- std::shared_ptr<LayoutNode> child2;
- float ratio = 0.5f; // Split ratio (0.0 to 1.0)
-
- LayoutNode(std::shared_ptr<Window> w) : type(Type::Leaf), window(w) {}
- LayoutNode(Type t, std::shared_ptr<LayoutNode> c1, std::shared_ptr<LayoutNode> 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<std::string> 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<Buffer> 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<std::filesystem::path> filepath;
- };
- /// @brief Get detailed information about all buffers.
- [[nodiscard]] std::vector<BufferInfo> 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<Window> 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> window);
- /// @brief Get the root node of the window layout tree.
- std::shared_ptr<LayoutNode> 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<size_t, size_t> visible_line_range() const;
- // === Event Callbacks ===
- using EventCallback = std::function<void(EditorEvent)>;
- /// @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<Theme> 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<std::shared_ptr<Buffer>> 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<LayoutNode> root_node_;
- std::shared_ptr<Window> active_window_;
- std::string last_message_;
- std::optional<std::chrono::steady_clock::time_point> message_clear_time_;
- std::vector<EventCallback> event_callbacks_;
- // Kill ring for cut/copy/paste
- KillRing kill_ring_;
- // Last yank position (for yank-pop)
- std::optional<Position> last_yank_start_;
- std::optional<Position> last_yank_end_;
- // 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;
- // Rectangle storage
- std::vector<std::string> rectangle_kill_ring_;
- // Subsystems
- ThemeManager theme_manager_;
- Config config_;
- std::unique_ptr<CommandSystem> command_system_; // Must be declared before KeyBindingManager
- KeyBindingManager keybinding_manager_;
- std::unique_ptr<LuaApi> 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<Window> target);
-
- // Helper to collect all windows in traversal order
- void collect_windows(LayoutNode* node, std::vector<std::shared_ptr<Window>>& windows);
- };
- } // namespace lumacs
|