editor_core.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. #pragma once
  2. #include "lumacs/buffer.hpp"
  3. #include "lumacs/window.hpp"
  4. #include "lumacs/kill_ring.hpp"
  5. #include "lumacs/theme.hpp"
  6. #include "lumacs/config.hpp"
  7. #include "lumacs/keybinding.hpp"
  8. #include "lumacs/modeline.hpp"
  9. #include <memory>
  10. #include <functional>
  11. #include <vector>
  12. #include <list>
  13. #include <unordered_map>
  14. namespace lumacs {
  15. class LuaApi; // Forward declaration
  16. class CommandSystem; // Forward declaration
  17. /// @brief Editor state change events triggered by core actions.
  18. /// Used by the UI layer to react to model updates.
  19. enum class EditorEvent {
  20. BufferModified, ///< The content of the active buffer has changed.
  21. CursorMoved, ///< The cursor position has changed.
  22. ViewportChanged, ///< The visible area (scroll/size) has changed.
  23. WindowLayoutChanged, ///< The window tree structure (splits) has changed.
  24. WindowFocused, ///< A different window has gained focus.
  25. Message, ///< A transient message should be displayed (e.g., in minibuffer).
  26. // Modal Interaction Events
  27. CommandMode, ///< Enter command input mode (M-x).
  28. BufferSwitchMode, ///< Enter buffer switching mode (C-x b).
  29. KillBufferMode, ///< Enter kill buffer mode (C-x k).
  30. FindFileMode, ///< Enter file finding mode (C-x C-f).
  31. ThemeSelectionMode, ///< Enter theme selection mode.
  32. ISearchMode, ///< Enter incremental search mode (C-s).
  33. ISearchBackwardMode, ///< Enter backward incremental search mode (C-r).
  34. Quit ///< The application should exit.
  35. };
  36. struct LayoutNode;
  37. /// @brief Core logic of the Lumacs editor, independent of the UI framework.
  38. ///
  39. /// This class acts as the central controller/facade for the editor's logic.
  40. /// It manages buffers, windows, the kill ring, registers, macros, configuration,
  41. /// and subsystems like the command system and Lua API.
  42. /// It emits events to notify the UI (IEditorView) of state changes.
  43. class EditorCore {
  44. public:
  45. EditorCore();
  46. ~EditorCore();
  47. // Disable copy, allow move
  48. EditorCore(const EditorCore&) = delete;
  49. EditorCore& operator=(const EditorCore&) = delete;
  50. EditorCore(EditorCore&&) noexcept = default;
  51. EditorCore& operator=(EditorCore&&) noexcept = default;
  52. // === Message System ===
  53. /// @brief Display a message to the user (usually in the minibuffer).
  54. /// @param msg The message string to display.
  55. void set_message(std::string msg) {
  56. last_message_ = std::move(msg);
  57. emit_event(EditorEvent::Message);
  58. }
  59. /// @brief Get the last set message.
  60. const std::string& last_message() const { return last_message_; }
  61. // === Actions ===
  62. // These methods trigger specific input modes in the UI.
  63. void enter_command_mode() { emit_event(EditorEvent::CommandMode); }
  64. void enter_buffer_switch_mode() { emit_event(EditorEvent::BufferSwitchMode); }
  65. void enter_kill_buffer_mode() { emit_event(EditorEvent::KillBufferMode); }
  66. void enter_find_file_mode() { emit_event(EditorEvent::FindFileMode); }
  67. void enter_theme_selection_mode() { emit_event(EditorEvent::ThemeSelectionMode); }
  68. void enter_isearch_mode() { emit_event(EditorEvent::ISearchMode); }
  69. void enter_isearch_backward_mode() { emit_event(EditorEvent::ISearchBackwardMode); }
  70. // === Buffer Management ===
  71. /// @brief Get the active buffer (associated with the active window).
  72. [[nodiscard]] const Buffer& buffer() const noexcept;
  73. [[nodiscard]] Buffer& buffer() noexcept;
  74. /// @brief Load a file into a buffer and display it in the active window.
  75. /// @param path Filesystem path to load.
  76. /// @return true if successful, false otherwise.
  77. bool load_file(const std::filesystem::path& path);
  78. /// @brief Create a new empty buffer (e.g., *scratch*) and display it.
  79. /// @param name The name of the new buffer.
  80. void new_buffer(std::string name = "*scratch*");
  81. /// @brief Get a list of names of all open buffers.
  82. [[nodiscard]] std::vector<std::string> get_buffer_names() const;
  83. /// @brief Find a buffer by its name.
  84. /// @return Shared pointer to the buffer, or nullptr if not found.
  85. [[nodiscard]] std::shared_ptr<Buffer> get_buffer_by_name(const std::string& name);
  86. /// @brief Switch the active window to display the specified buffer.
  87. /// @param name The name of the buffer to switch to.
  88. /// @return true if successful.
  89. bool switch_buffer_in_window(const std::string& name);
  90. /// @brief Close (kill) a buffer.
  91. /// @param name The name of the buffer to close.
  92. /// @return true if closed, false if it doesn't exist or is the last buffer.
  93. bool close_buffer(const std::string& name);
  94. /// @brief Structure containing summary information about a buffer.
  95. struct BufferInfo {
  96. std::string name;
  97. size_t size;
  98. bool modified;
  99. std::string mode;
  100. std::optional<std::filesystem::path> filepath;
  101. };
  102. /// @brief Get detailed information about all buffers.
  103. [[nodiscard]] std::vector<BufferInfo> get_all_buffer_info() const;
  104. // === Window Management ===
  105. /// @brief Split the active window horizontally (active becomes top).
  106. void split_horizontally();
  107. /// @brief Split the active window vertically (active becomes left).
  108. void split_vertically();
  109. /// @brief Close the active window and remove it from the layout tree.
  110. void close_active_window();
  111. /// @brief Move focus to the next window in the tree traversal order.
  112. void next_window();
  113. /// @brief Safe version of next_window (deprecated, alias for next_window).
  114. void next_window_safe();
  115. /// @brief Get the currently focused window.
  116. std::shared_ptr<Window> active_window() const { return active_window_; }
  117. /// @brief Set the active window explicitly (must exist in the tree).
  118. bool set_active_window(std::shared_ptr<Window> window);
  119. /// @brief Get the root node of the window layout tree.
  120. std::shared_ptr<LayoutNode> root_layout() const { return root_node_; }
  121. // === Cursor Management (Proxies to active window) ===
  122. [[nodiscard]] Position cursor() const noexcept;
  123. void set_cursor(Position pos);
  124. // === Cursor Movement (Proxies to active window) ===
  125. void move_up();
  126. void move_down();
  127. void move_left();
  128. void move_right();
  129. void move_to_line_start();
  130. void move_to_line_end();
  131. void move_forward_word();
  132. void move_backward_word();
  133. void page_up();
  134. void page_down();
  135. void goto_beginning();
  136. void goto_end();
  137. void goto_line(size_t line);
  138. // === Viewport Management (Proxies to active window) ===
  139. const Viewport& viewport() const noexcept;
  140. void set_viewport_size(int width, int height);
  141. void adjust_scroll();
  142. std::pair<size_t, size_t> visible_line_range() const;
  143. // === Event Callbacks ===
  144. using EventCallback = std::function<void(EditorEvent)>;
  145. /// @brief Register a callback to be notified of editor events.
  146. void on_event(EventCallback callback) {
  147. event_callbacks_.push_back(std::move(callback));
  148. }
  149. /// @brief Clear all registered event callbacks.
  150. void clear_event_callbacks() {
  151. event_callbacks_.clear();
  152. }
  153. // === Actions ===
  154. /// @brief Request the application to quit.
  155. void request_quit() {
  156. emit_event(EditorEvent::Quit);
  157. }
  158. // === Undo/Redo ===
  159. bool undo();
  160. bool redo();
  161. bool can_undo() const;
  162. bool can_redo() const;
  163. // === Kill Ring ===
  164. /// @brief Access the global Kill Ring (clipboard history).
  165. [[nodiscard]] KillRing& kill_ring() noexcept { return kill_ring_; }
  166. [[nodiscard]] const KillRing& kill_ring() const noexcept { return kill_ring_; }
  167. // === Registers ===
  168. /// @brief Save text to a named register.
  169. void copy_to_register(char register_name, const std::string& text);
  170. /// @brief Insert text from a named register.
  171. bool insert_register(char register_name);
  172. /// @brief Copy the active region to a register (C-x r s).
  173. void copy_region_to_register(char register_name);
  174. /// @brief Insert text from a register (C-x r i).
  175. bool yank_from_register(char register_name);
  176. // === Keyboard Macros ===
  177. /// @brief Start recording a keyboard macro (F3).
  178. void start_kbd_macro();
  179. /// @brief Stop recording or execute the last macro (F4).
  180. void end_kbd_macro_or_call();
  181. /// @brief Record a key sequence if macro recording is active.
  182. void record_key_sequence(const std::string& key_sequence);
  183. /// @brief Check if macro recording is currently active.
  184. [[nodiscard]] bool is_recording_macro() const noexcept { return recording_macro_; }
  185. // === Rectangles ===
  186. /// @brief Kill (cut) a rectangular region (C-x r k).
  187. void kill_rectangle();
  188. /// @brief Yank (paste) the last killed rectangle (C-x r y).
  189. void yank_rectangle();
  190. /// @brief Replace a rectangular region with a string (C-x r t).
  191. void string_rectangle(const std::string& text);
  192. // === Standard Editing Commands ===
  193. /// @brief Kill text from cursor to end of line (C-k).
  194. void kill_line();
  195. /// @brief Kill the text in the active region (C-w).
  196. void kill_region();
  197. /// @brief Copy the active region to the kill ring (M-w).
  198. void copy_region_as_kill();
  199. /// @brief Yank (paste) from the kill ring (C-y).
  200. void yank();
  201. /// @brief Replace the just-yanked text with the next item in kill ring (M-y).
  202. void yank_pop();
  203. /// @brief Kill word forward (M-d).
  204. void kill_word();
  205. /// @brief Kill word backward (M-Backspace).
  206. void backward_kill_word();
  207. // === Theme Management ===
  208. [[nodiscard]] ThemeManager& theme_manager() noexcept { return theme_manager_; }
  209. [[nodiscard]] const ThemeManager& theme_manager() const noexcept { return theme_manager_; }
  210. /// @brief Set the active theme by name.
  211. void set_theme(const std::string& name) { theme_manager_.set_active_theme(name); }
  212. /// @brief Get the currently active theme.
  213. [[nodiscard]] std::shared_ptr<Theme> active_theme() const { return theme_manager_.active_theme(); }
  214. // === Configuration ===
  215. [[nodiscard]] Config& config() noexcept { return config_; }
  216. [[nodiscard]] const Config& config() const noexcept { return config_; }
  217. // === Key Binding System ===
  218. [[nodiscard]] KeyBindingManager& keybinding_manager() noexcept { return keybinding_manager_; }
  219. [[nodiscard]] const KeyBindingManager& keybinding_manager() const noexcept { return keybinding_manager_; }
  220. // === Lua API ===
  221. [[nodiscard]] LuaApi* lua_api() const { return lua_api_.get(); }
  222. // === Command System ===
  223. [[nodiscard]] CommandSystem& command_system() noexcept { return *command_system_; }
  224. [[nodiscard]] const CommandSystem& command_system() const noexcept { return *command_system_; }
  225. // === Modeline Manager ===
  226. [[nodiscard]] ModelineManager& modeline_manager() noexcept { return modeline_manager_; }
  227. [[nodiscard]] const ModelineManager& modeline_manager() const noexcept { return modeline_manager_; }
  228. private:
  229. std::list<std::shared_ptr<Buffer>> buffers_;
  230. // Word movement helpers
  231. Position calculate_forward_word_pos(Position start_pos);
  232. Position calculate_backward_word_pos(Position start_pos);
  233. // Window layout
  234. std::shared_ptr<LayoutNode> root_node_;
  235. std::shared_ptr<Window> active_window_;
  236. std::string last_message_;
  237. std::vector<EventCallback> event_callbacks_;
  238. // Kill ring for cut/copy/paste
  239. KillRing kill_ring_;
  240. // Last yank position (for yank-pop)
  241. std::optional<Position> last_yank_start_;
  242. std::optional<Position> last_yank_end_;
  243. // Registers for storing text (a-z, A-Z, 0-9)
  244. std::unordered_map<char, std::string> registers_;
  245. // Keyboard macros
  246. std::vector<std::string> current_macro_;
  247. std::vector<std::string> last_macro_;
  248. bool recording_macro_ = false;
  249. // Rectangle storage
  250. std::vector<std::string> rectangle_kill_ring_;
  251. // Subsystems
  252. ThemeManager theme_manager_;
  253. Config config_;
  254. KeyBindingManager keybinding_manager_;
  255. std::unique_ptr<LuaApi> lua_api_;
  256. std::unique_ptr<CommandSystem> command_system_;
  257. ModelineManager modeline_manager_;
  258. void emit_event(EditorEvent event);
  259. // Helper to find a node containing the active window
  260. LayoutNode* find_parent_node(LayoutNode* root, std::shared_ptr<Window> target);
  261. // Helper to collect all windows in traversal order
  262. void collect_windows(LayoutNode* node, std::vector<std::shared_ptr<Window>>& windows);
  263. };
  264. /// @brief Represents a node in the window layout tree.
  265. struct LayoutNode {
  266. enum class Type { Leaf, HorizontalSplit, VerticalSplit };
  267. Type type;
  268. // If Leaf
  269. std::shared_ptr<Window> window;
  270. // If Split
  271. std::shared_ptr<LayoutNode> child1;
  272. std::shared_ptr<LayoutNode> child2;
  273. float ratio = 0.5f; // Split ratio (0.0 to 1.0)
  274. LayoutNode(std::shared_ptr<Window> w) : type(Type::Leaf), window(w) {}
  275. LayoutNode(Type t, std::shared_ptr<LayoutNode> c1, std::shared_ptr<LayoutNode> c2)
  276. : type(t), child1(c1), child2(c2) {}
  277. };
  278. } // namespace lumacs