editor_core.hpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #pragma once
  2. #include "lumacs/buffer.hpp"
  3. #include "lumacs/window.hpp"
  4. #include "lumacs/kill_ring.hpp"
  5. #include <memory>
  6. #include <functional>
  7. #include <vector>
  8. #include <list>
  9. namespace lumacs {
  10. /// Editor state change events
  11. enum class EditorEvent {
  12. BufferModified,
  13. CursorMoved,
  14. ViewportChanged,
  15. WindowLayoutChanged, // New event
  16. WindowFocused, // New event: a different window gained focus
  17. Message, // New event
  18. CommandMode, // Trigger command mode (minibuffer)
  19. BufferSwitchMode, // Trigger buffer switch mode
  20. KillBufferMode, // Trigger kill buffer mode
  21. ISearchMode, // Trigger incremental search mode
  22. ISearchBackwardMode, // Trigger incremental search mode (backward)
  23. Quit
  24. };
  25. struct LayoutNode;
  26. /// Core editor logic, independent of UI framework
  27. class EditorCore {
  28. public:
  29. EditorCore();
  30. ~EditorCore() = default;
  31. // Disable copy, allow move
  32. EditorCore(const EditorCore&) = delete;
  33. EditorCore& operator=(const EditorCore&) = delete;
  34. EditorCore(EditorCore&&) noexcept = default;
  35. EditorCore& operator=(EditorCore&&) noexcept = default;
  36. // === Message System ===
  37. void set_message(std::string msg) {
  38. last_message_ = std::move(msg);
  39. emit_event(EditorEvent::Message);
  40. }
  41. const std::string& last_message() const { return last_message_; }
  42. // === Actions ===
  43. void enter_command_mode() {
  44. emit_event(EditorEvent::CommandMode);
  45. }
  46. void enter_buffer_switch_mode() {
  47. emit_event(EditorEvent::BufferSwitchMode);
  48. }
  49. void enter_kill_buffer_mode() {
  50. emit_event(EditorEvent::KillBufferMode);
  51. }
  52. void enter_isearch_mode() {
  53. emit_event(EditorEvent::ISearchMode);
  54. }
  55. void enter_isearch_backward_mode() {
  56. emit_event(EditorEvent::ISearchBackwardMode);
  57. }
  58. // === Buffer Management ===
  59. /// Get the current buffer (of the active window)
  60. [[nodiscard]] const Buffer& buffer() const noexcept;
  61. [[nodiscard]] Buffer& buffer() noexcept;
  62. /// Load a file into the current window
  63. bool load_file(const std::filesystem::path& path);
  64. /// Create a new empty buffer in current window
  65. void new_buffer(std::string name = "*scratch*");
  66. /// Get list of all buffer names
  67. [[nodiscard]] std::vector<std::string> get_buffer_names() const;
  68. /// Get buffer by name (returns nullptr if not found)
  69. [[nodiscard]] std::shared_ptr<Buffer> get_buffer_by_name(const std::string& name);
  70. /// Switch active window to buffer by name
  71. bool switch_buffer_in_window(const std::string& name);
  72. /// Close buffer by name (returns false if buffer is displayed or doesn't exist)
  73. bool close_buffer(const std::string& name);
  74. /// Buffer information for list display
  75. struct BufferInfo {
  76. std::string name;
  77. size_t size;
  78. bool modified;
  79. std::string mode;
  80. std::optional<std::filesystem::path> filepath;
  81. };
  82. /// Get information about all buffers
  83. [[nodiscard]] std::vector<BufferInfo> get_all_buffer_info() const;
  84. // === Window Management ===
  85. /// Split the current window horizontally (active window becomes top, new one bottom)
  86. void split_horizontally();
  87. /// Split the current window vertically (active window becomes left, new one right)
  88. void split_vertically();
  89. /// Close the current window
  90. void close_active_window();
  91. /// Move focus to the next window
  92. void next_window();
  93. /// Get the active window
  94. std::shared_ptr<Window> active_window() const { return active_window_; }
  95. /// Get the root of the layout tree (for rendering)
  96. std::shared_ptr<LayoutNode> root_layout() const { return root_node_; }
  97. // === Cursor Management (Proxies to active window) ===
  98. [[nodiscard]] Position cursor() const noexcept;
  99. void set_cursor(Position pos);
  100. // === Cursor Movement (Proxies to active window) ===
  101. void move_up();
  102. void move_down();
  103. void move_left();
  104. void move_right();
  105. void move_to_line_start();
  106. void move_to_line_end();
  107. void move_forward_word();
  108. void move_backward_word();
  109. void page_up();
  110. void page_down();
  111. void goto_beginning();
  112. void goto_end();
  113. void goto_line(size_t line);
  114. // === Viewport Management (Proxies to active window) ===
  115. const Viewport& viewport() const noexcept;
  116. void set_viewport_size(int width, int height);
  117. void adjust_scroll();
  118. std::pair<size_t, size_t> visible_line_range() const;
  119. // === Event Callbacks ===
  120. using EventCallback = std::function<void(EditorEvent)>;
  121. void on_event(EventCallback callback) {
  122. event_callbacks_.push_back(std::move(callback));
  123. }
  124. // === Actions ===
  125. void request_quit() {
  126. emit_event(EditorEvent::Quit);
  127. }
  128. // === Undo/Redo ===
  129. bool undo();
  130. bool redo();
  131. bool can_undo() const;
  132. bool can_redo() const;
  133. // === Kill Ring ===
  134. /// Get the kill ring
  135. [[nodiscard]] KillRing& kill_ring() noexcept { return kill_ring_; }
  136. [[nodiscard]] const KillRing& kill_ring() const noexcept { return kill_ring_; }
  137. /// Kill (cut) text from position to end of line
  138. void kill_line();
  139. /// Kill (cut) the active region
  140. void kill_region();
  141. /// Copy the active region to kill ring (without deleting)
  142. void copy_region_as_kill();
  143. /// Yank (paste) from kill ring
  144. void yank();
  145. /// Yank and pop to previous kill ring entry
  146. void yank_pop();
  147. /// Kill word forward
  148. void kill_word();
  149. /// Kill word backward
  150. void backward_kill_word();
  151. private:
  152. // All open buffers
  153. std::list<std::shared_ptr<Buffer>> buffers_;
  154. // Word movement helpers
  155. Position calculate_forward_word_pos(Position start_pos);
  156. Position calculate_backward_word_pos(Position start_pos);
  157. // Window layout
  158. std::shared_ptr<LayoutNode> root_node_;
  159. std::shared_ptr<Window> active_window_;
  160. std::string last_message_;
  161. std::vector<EventCallback> event_callbacks_;
  162. // Kill ring for cut/copy/paste
  163. KillRing kill_ring_;
  164. // Last yank position (for yank-pop)
  165. std::optional<Position> last_yank_start_;
  166. std::optional<Position> last_yank_end_;
  167. void emit_event(EditorEvent event);
  168. // Helper to find a node containing the active window
  169. LayoutNode* find_parent_node(LayoutNode* root, std::shared_ptr<Window> target);
  170. // Helper to collect all windows in traversal order
  171. void collect_windows(LayoutNode* node, std::vector<std::shared_ptr<Window>>& windows);
  172. };
  173. struct LayoutNode {
  174. enum class Type { Leaf, HorizontalSplit, VerticalSplit };
  175. Type type;
  176. // If Leaf
  177. std::shared_ptr<Window> window;
  178. // If Split
  179. std::shared_ptr<LayoutNode> child1;
  180. std::shared_ptr<LayoutNode> child2;
  181. float ratio = 0.5f; // For future resizing
  182. LayoutNode(std::shared_ptr<Window> w) : type(Type::Leaf), window(w) {}
  183. LayoutNode(Type t, std::shared_ptr<LayoutNode> c1, std::shared_ptr<LayoutNode> c2)
  184. : type(t), child1(c1), child2(c2) {}
  185. };
  186. } // namespace lumacs