buffer.hpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. #pragma once
  2. #include <string>
  3. #include <vector>
  4. #include <functional>
  5. #include <optional>
  6. #include <filesystem>
  7. #include <memory>
  8. #include "lumacs/theme.hpp" // For ThemeElement mapping if needed
  9. namespace lumacs {
  10. /// Represents a cursor position in a buffer
  11. struct Position {
  12. size_t line;
  13. size_t column;
  14. auto operator<=>(const Position&) const = default;
  15. };
  16. /// Represents a range in the buffer
  17. struct Range {
  18. Position start;
  19. Position end;
  20. auto operator<=>(const Range&) const = default;
  21. };
  22. /// Buffer events for hooks/callbacks
  23. enum class BufferEvent {
  24. // Lifecycle events
  25. Created, // Buffer was created
  26. Loaded, // File was loaded into buffer
  27. Closed, // Buffer is being closed
  28. // Modification events
  29. BeforeChange, // About to modify buffer (can be used to save state for undo)
  30. AfterChange, // Buffer content was modified
  31. LineChanged, // Specific line was modified
  32. // File operations
  33. BeforeSave, // About to save
  34. AfterSave, // File was saved
  35. // Language/mode
  36. LanguageChanged // Buffer language/mode changed
  37. };
  38. /// Event data passed to callbacks
  39. struct BufferEventData {
  40. BufferEvent event;
  41. size_t line = 0; // Line number for LineChanged events
  42. std::string language = ""; // For LanguageChanged events
  43. };
  44. /// Text styling attributes for syntax highlighting
  45. struct TextAttribute {
  46. enum class Style {
  47. Normal = 0,
  48. Bold = 1,
  49. Italic = 2,
  50. Underline = 4,
  51. };
  52. // Common semantic colors for syntax highlighting (Legacy, used for mapping)
  53. enum class ColorType {
  54. Default,
  55. Keyword, // if, for, while, etc.
  56. String, // String literals
  57. Comment, // Comments
  58. Function, // Function names
  59. Type, // Type names, classes
  60. Number, // Numeric literals
  61. Operator, // +, -, *, etc.
  62. Variable, // Variable names
  63. Constant, // Constants, enums
  64. Error, // Error highlighting
  65. };
  66. std::string face_name = "default";
  67. TextAttribute() = default;
  68. TextAttribute(std::string face) : face_name(std::move(face)) {}
  69. // Legacy constructor
  70. TextAttribute(ColorType c, int /*s*/ = 0) {
  71. // Map legacy ColorType to face name
  72. switch (c) {
  73. case ColorType::Keyword: face_name = "font-lock-keyword-face"; break;
  74. case ColorType::String: face_name = "font-lock-string-face"; break;
  75. case ColorType::Comment: face_name = "font-lock-comment-face"; break;
  76. case ColorType::Function: face_name = "font-lock-function-name-face"; break;
  77. case ColorType::Type: face_name = "font-lock-type-face"; break;
  78. case ColorType::Number: face_name = "font-lock-constant-face"; break; // Map number to constant
  79. case ColorType::Operator: face_name = "default"; break; // No specific face yet
  80. case ColorType::Variable: face_name = "font-lock-variable-name-face"; break;
  81. case ColorType::Constant: face_name = "font-lock-constant-face"; break;
  82. case ColorType::Error: face_name = "error"; break;
  83. default: face_name = "default"; break;
  84. }
  85. // Note: style flags (s) are ignored here as faces define weight/slant
  86. }
  87. };
  88. /// A range with associated styling
  89. struct StyledRange {
  90. Range range;
  91. TextAttribute attr;
  92. StyledRange() = default;
  93. StyledRange(Range r, TextAttribute a) : range(r), attr(a) {}
  94. };
  95. /// Undo/Redo state snapshot
  96. struct UndoState {
  97. std::vector<std::string> lines;
  98. Position cursor;
  99. UndoState() = default;
  100. UndoState(const std::vector<std::string>& l, Position c) : lines(l), cursor(c) {}
  101. };
  102. /// A text buffer that manages the content of a file or scratch buffer
  103. class Buffer {
  104. public:
  105. /// Create an empty buffer
  106. Buffer();
  107. /// Create a buffer with a name (for scratch buffers)
  108. explicit Buffer(std::string name);
  109. /// Create a buffer from a file
  110. static std::optional<Buffer> from_file(const std::filesystem::path& path);
  111. // Disable copy, allow move
  112. Buffer(const Buffer&) = delete;
  113. Buffer& operator=(const Buffer&) = delete;
  114. Buffer(Buffer&&) noexcept = default;
  115. Buffer& operator=(Buffer&&) noexcept = default;
  116. ~Buffer() = default;
  117. // === Content Access ===
  118. /// Get the number of lines in the buffer
  119. [[nodiscard]] size_t line_count() const noexcept;
  120. /// Get a line by index (0-based)
  121. [[nodiscard]] const std::string& line(size_t index) const;
  122. /// Get all lines
  123. [[nodiscard]] const std::vector<std::string>& lines() const noexcept;
  124. /// Get the entire buffer content as a single string
  125. [[nodiscard]] std::string content() const;
  126. // === Modification ===
  127. /// Insert text at a position
  128. void insert(Position pos, std::string_view text);
  129. /// Insert a character at a position
  130. void insert_char(Position pos, char c);
  131. /// Insert a newline at a position
  132. void insert_newline(Position pos);
  133. /// Delete a range of text
  134. void erase(Range range);
  135. /// Delete a character at a position (backspace)
  136. void erase_char(Position pos);
  137. /// Replace text in a range
  138. void replace(Range range, std::string_view text);
  139. /// Find text starting from a position
  140. [[nodiscard]] std::optional<Range> find(const std::string& query, Position start_pos) const;
  141. /// Find text searching backwards from a position
  142. [[nodiscard]] std::optional<Range> find_backward(const std::string& query, Position start_pos) const;
  143. /// Clear the entire buffer
  144. void clear();
  145. // === File Operations ===
  146. /// Save the buffer to its associated file
  147. bool save();
  148. /// Save the buffer to a specific file
  149. bool save_as(const std::filesystem::path& path);
  150. /// Check if the buffer has been modified since last save
  151. [[nodiscard]] bool is_modified() const noexcept;
  152. /// Get the file path associated with this buffer (if any)
  153. [[nodiscard]] std::optional<std::filesystem::path> file_path() const noexcept;
  154. // === Buffer Properties ===
  155. /// Get the buffer name
  156. [[nodiscard]] const std::string& name() const noexcept;
  157. /// Set the buffer name
  158. void set_name(std::string name);
  159. /// Check if position is valid
  160. [[nodiscard]] bool is_valid_position(Position pos) const noexcept;
  161. /// Clamp a position to valid bounds
  162. [[nodiscard]] Position clamp_position(Position pos) const noexcept;
  163. // === Syntax Highlighting / Styling ===
  164. /// Set styling for a range of text
  165. void set_style(Range range, TextAttribute attr);
  166. /// Get all styled ranges for a specific line
  167. [[nodiscard]] const std::vector<StyledRange>& get_line_styles(size_t line) const;
  168. /// Clear all styling information
  169. void clear_styles();
  170. /// Clear styling for a specific line
  171. void clear_line_styles(size_t line);
  172. // === Events & Hooks ===
  173. using BufferEventCallback = std::function<void(const BufferEventData&)>;
  174. /// Register a callback for buffer events
  175. void on_buffer_event(BufferEventCallback callback);
  176. /// Get the language/file type of this buffer
  177. [[nodiscard]] const std::string& language() const noexcept { return language_; }
  178. /// Set the language/file type (triggers LanguageChanged event)
  179. void set_language(std::string lang);
  180. /// Auto-detect language from file path
  181. static std::string detect_language(const std::filesystem::path& path);
  182. // === Undo/Redo ===
  183. /// Undo the last change
  184. bool undo(Position& out_cursor);
  185. /// Redo the last undone change
  186. bool redo(Position& out_cursor);
  187. /// Check if undo is available
  188. [[nodiscard]] bool can_undo() const noexcept { return !undo_stack_.empty(); }
  189. /// Check if redo is available
  190. [[nodiscard]] bool can_redo() const noexcept { return !redo_stack_.empty(); }
  191. /// Save current state to undo stack (called automatically before changes)
  192. void save_undo_state(Position cursor);
  193. /// Clear redo stack (called when new change happens)
  194. void clear_redo_stack();
  195. // === Mark and Region ===
  196. /// Set the mark at a position
  197. void set_mark(Position pos);
  198. /// Clear/deactivate the mark
  199. void deactivate_mark();
  200. /// Get the current mark position (if set)
  201. [[nodiscard]] std::optional<Position> mark() const noexcept { return mark_; }
  202. /// Check if mark is active
  203. [[nodiscard]] bool has_active_mark() const noexcept { return mark_active_; }
  204. /// Get the region between mark and a given position (if mark is active)
  205. [[nodiscard]] std::optional<Range> get_region(Position point) const;
  206. /// Get text in a range
  207. [[nodiscard]] std::string get_text_in_range(Range range) const;
  208. private:
  209. std::string name_;
  210. std::vector<std::string> lines_;
  211. std::optional<std::filesystem::path> file_path_;
  212. bool modified_;
  213. std::string language_;
  214. // Styling information: one vector of styled ranges per line
  215. std::vector<std::vector<StyledRange>> line_styles_;
  216. // Event callbacks
  217. std::vector<BufferEventCallback> event_callbacks_;
  218. // Undo/Redo stacks
  219. std::vector<UndoState> undo_stack_;
  220. std::vector<UndoState> redo_stack_;
  221. static constexpr size_t MAX_UNDO_LEVELS = 100;
  222. bool in_undo_redo_ = false; // Prevent saving state during undo/redo
  223. // Mark and Region
  224. std::optional<Position> mark_;
  225. bool mark_active_ = false;
  226. /// Ensure the buffer has at least one line
  227. void ensure_min_lines();
  228. /// Mark the buffer as modified
  229. void mark_modified();
  230. /// Ensure styles vector matches lines vector size
  231. void ensure_styles_size();
  232. /// Emit a buffer event to all registered callbacks
  233. void emit_event(BufferEvent event, size_t line = 0);
  234. };
  235. } // namespace lumacs