#pragma once #include "lumacs/buffer.hpp" #include "lumacs/window.hpp" #include "lumacs/kill_ring.hpp" #include #include #include #include namespace lumacs { /// Editor state change events enum class EditorEvent { BufferModified, CursorMoved, ViewportChanged, WindowLayoutChanged, // New event WindowFocused, // New event: a different window gained focus Message, // New event CommandMode, // Trigger command mode (minibuffer) BufferSwitchMode, // Trigger buffer switch mode KillBufferMode, // Trigger kill buffer mode Quit }; struct LayoutNode; /// Core editor logic, independent of UI framework class EditorCore { public: EditorCore(); ~EditorCore() = default; // Disable copy, allow move EditorCore(const EditorCore&) = delete; EditorCore& operator=(const EditorCore&) = delete; EditorCore(EditorCore&&) noexcept = default; EditorCore& operator=(EditorCore&&) noexcept = default; // === Message System === void set_message(std::string msg) { last_message_ = std::move(msg); emit_event(EditorEvent::Message); } const std::string& last_message() const { return last_message_; } // === Actions === 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); } // === Buffer Management === /// Get the current buffer (of the active window) [[nodiscard]] const Buffer& buffer() const noexcept; [[nodiscard]] Buffer& buffer() noexcept; /// Load a file into the current window bool load_file(const std::filesystem::path& path); /// Create a new empty buffer in current window void new_buffer(std::string name = "*scratch*"); /// Get list of all buffer names [[nodiscard]] std::vector get_buffer_names() const; /// Get buffer by name (returns nullptr if not found) [[nodiscard]] std::shared_ptr get_buffer_by_name(const std::string& name); /// Switch active window to buffer by name bool switch_buffer_in_window(const std::string& name); /// Close buffer by name (returns false if buffer is displayed or doesn't exist) bool close_buffer(const std::string& name); /// Buffer information for list display struct BufferInfo { std::string name; size_t size; bool modified; std::string mode; std::optional filepath; }; /// Get information about all buffers [[nodiscard]] std::vector get_all_buffer_info() const; // === Window Management === /// Split the current window horizontally (active window becomes top, new one bottom) void split_horizontally(); /// Split the current window vertically (active window becomes left, new one right) void split_vertically(); /// Close the current window void close_active_window(); /// Move focus to the next window void next_window(); /// Get the active window std::shared_ptr active_window() const { return active_window_; } /// Get the root of the layout tree (for rendering) 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; void on_event(EventCallback callback) { event_callbacks_.push_back(std::move(callback)); } // === Actions === void request_quit() { emit_event(EditorEvent::Quit); } // === Undo/Redo === bool undo(); bool redo(); bool can_undo() const; bool can_redo() const; // === Kill Ring === /// Get the kill ring [[nodiscard]] KillRing& kill_ring() noexcept { return kill_ring_; } [[nodiscard]] const KillRing& kill_ring() const noexcept { return kill_ring_; } /// Kill (cut) text from position to end of line void kill_line(); /// Kill (cut) the active region void kill_region(); /// Copy the active region to kill ring (without deleting) void copy_region_as_kill(); /// Yank (paste) from kill ring void yank(); /// Yank and pop to previous kill ring entry void yank_pop(); private: // All open buffers std::list> buffers_; // Window layout std::shared_ptr root_node_; std::shared_ptr active_window_; std::string last_message_; 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_; 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); }; 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; // For future resizing 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) {} }; } // namespace lumacs