#pragma once #include #include #include #include #include #include #include "lumacs/theme.hpp" // For ThemeElement mapping if needed namespace lumacs { /// @brief Represents a cursor position in a buffer (line, column). struct Position { size_t line = 0; size_t column = 0; auto operator<=>(const Position&) const = default; }; /// @brief Represents a range of text in the buffer [start, end). struct Range { Position start; Position end; auto operator<=>(const Range&) const = default; }; /// @brief Buffer lifecycle and modification events. enum class BufferEvent { // Lifecycle events Created, ///< Buffer was created. Loaded, ///< File was loaded into buffer. Closed, ///< Buffer is being closed. // Modification events BeforeChange, ///< About to modify buffer (can be used to save state for undo). AfterChange, ///< Buffer content was modified. LineChanged, ///< Specific line was modified. // File operations BeforeSave, ///< About to save to disk. AfterSave, ///< File was saved to disk. // Language/mode LanguageChanged ///< Buffer language/mode changed (e.g., for syntax highlighting). }; /// @brief Data associated with a buffer event. struct BufferEventData { BufferEvent event; size_t line = 0; ///< Line number for LineChanged events. std::string language = ""; ///< For LanguageChanged events. }; /// @brief Text styling attributes for syntax highlighting. struct TextAttribute { enum class Style { Normal = 0, Bold = 1, Italic = 2, Underline = 4, }; // Common semantic colors for syntax highlighting (Legacy, used for mapping) enum class ColorType { Default, Keyword, // if, for, while, etc. String, // String literals Comment, // Comments Function, // Function names Type, // Type names, classes Number, // Numeric literals Operator, // +, -, *, etc. Variable, // Variable names Constant, // Constants, enums Error, // Error highlighting }; std::string face_name = "default"; TextAttribute() = default; TextAttribute(std::string face) : face_name(std::move(face)) {} // Legacy constructor TextAttribute(ColorType c, int /*s*/ = 0) { // Map legacy ColorType to face name switch (c) { case ColorType::Keyword: face_name = "font-lock-keyword-face"; break; case ColorType::String: face_name = "font-lock-string-face"; break; case ColorType::Comment: face_name = "font-lock-comment-face"; break; case ColorType::Function: face_name = "font-lock-function-name-face"; break; case ColorType::Type: face_name = "font-lock-type-face"; break; case ColorType::Number: face_name = "font-lock-constant-face"; break; // Map number to constant case ColorType::Operator: face_name = "default"; break; // No specific face yet case ColorType::Variable: face_name = "font-lock-variable-name-face"; break; case ColorType::Constant: face_name = "font-lock-constant-face"; break; case ColorType::Error: face_name = "error"; break; default: face_name = "default"; break; } // Note: style flags (s) are ignored here as faces define weight/slant } }; /// @brief A text range associated with a specific style attribute. struct StyledRange { Range range; TextAttribute attr; StyledRange() = default; StyledRange(Range r, TextAttribute a) : range(r), attr(a) {} }; /// @brief Snapshot of buffer state for Undo/Redo operations. struct UndoState { std::vector lines; Position cursor; UndoState() = default; UndoState(const std::vector& l, Position c) : lines(l), cursor(c) {} }; /// @brief A text buffer that manages the content of a file or scratch buffer. /// /// The Buffer class is the core data structure for text storage. It manages: /// - Text content (lines) /// - File association (save/load) /// - Modification tracking /// - Syntax highlighting (styling) /// - Undo/Redo history /// - Mark and Region state class Buffer { public: /// @brief Create an empty buffer. Buffer(); /// @brief Create a named buffer (e.g., for scratch). explicit Buffer(std::string name); /// @brief Create a buffer loaded from a file. /// @param path The path to the file. /// @return Optional Buffer (empty if load failed). static std::optional from_file(const std::filesystem::path& path); // Disable copy, allow move Buffer(const Buffer&) = delete; Buffer& operator=(const Buffer&) = delete; Buffer(Buffer&&) noexcept = default; Buffer& operator=(Buffer&&) noexcept = default; ~Buffer() = default; // === Content Access === /// @brief Get the number of lines in the buffer. [[nodiscard]] size_t line_count() const noexcept; /// @brief Get a specific line by index (0-based). [[nodiscard]] const std::string& line(size_t index) const; /// @brief Get all lines in the buffer. [[nodiscard]] const std::vector& lines() const noexcept; /// @brief Get the entire buffer content as a single string (lines joined by newlines). [[nodiscard]] std::string content() const; // === Modification === /// @brief Insert text at the specified position. void insert(Position pos, std::string_view text); /// @brief Insert a single character at the specified position. void insert_char(Position pos, char c); /// @brief Insert a newline at the specified position (splits the line). void insert_newline(Position pos); /// @brief Delete the text within the specified range. void erase(Range range); /// @brief Delete the character *before* the specified position (Backspace behavior). void erase_char(Position pos); /// @brief Replace the text in the specified range with new text. void replace(Range range, std::string_view text); /// @brief Find the next occurrence of a query string starting from start_pos. [[nodiscard]] std::optional find(const std::string& query, Position start_pos) const; /// @brief Find the previous occurrence of a query string starting backwards from start_pos. [[nodiscard]] std::optional find_backward(const std::string& query, Position start_pos) const; /// @brief Clear the entire buffer content. void clear(); // === File Operations === /// @brief Save the buffer content to its associated file. bool save(); /// @brief Save the buffer content to a new file path and update association. bool save_as(const std::filesystem::path& path); /// @brief Check if the buffer has been modified since the last save. [[nodiscard]] bool is_modified() const noexcept; /// @brief Get the file path associated with this buffer (if any). [[nodiscard]] std::optional file_path() const noexcept; // === Buffer Properties === /// @brief Get the buffer name. [[nodiscard]] const std::string& name() const noexcept; /// @brief Set the buffer name. void set_name(std::string name); /// @brief Check if a position is valid (within buffer bounds). [[nodiscard]] bool is_valid_position(Position pos) const noexcept; /// @brief Clamp a position to valid buffer bounds. [[nodiscard]] Position clamp_position(Position pos) const noexcept; // === Syntax Highlighting / Styling === /// @brief Apply a style attribute to a specific range of text. void set_style(Range range, TextAttribute attr); /// @brief Get all styled ranges for a specific line. [[nodiscard]] const std::vector& get_line_styles(size_t line) const; /// @brief Clear all styling information in the buffer. void clear_styles(); /// @brief Clear styling information for a specific line. void clear_line_styles(size_t line); // === Events & Hooks === using BufferEventCallback = std::function; using BufferEventCallbackHandle = size_t; /// @brief Register a callback for buffer events. BufferEventCallbackHandle on_buffer_event(BufferEventCallback callback); /// @brief Disconnect a previously registered buffer event callback. void disconnect_event_callback(BufferEventCallbackHandle handle); /// @brief Get the language/mode identifier (e.g., "cpp", "lua"). [[nodiscard]] const std::string& language() const noexcept { return language_; } /// @brief Set the language/mode identifier (triggers LanguageChanged event). void set_language(std::string lang); /// @brief Auto-detect language from file extension. static std::string detect_language(const std::filesystem::path& path); // === Undo/Redo === /// @brief Undo the last operation. /// @param out_cursor Output parameter to restore cursor position. /// @return true if undo was successful. bool undo(Position& out_cursor); /// @brief Redo the last undone operation. /// @param out_cursor Output parameter to restore cursor position. /// @return true if redo was successful. bool redo(Position& out_cursor); /// @brief Check if undo is possible. [[nodiscard]] bool can_undo() const noexcept { return !undo_stack_.empty(); } /// @brief Check if redo is possible. [[nodiscard]] bool can_redo() const noexcept { return !redo_stack_.empty(); } /// @brief Save current state to undo stack (internal use). void save_undo_state(Position cursor); /// @brief Clear redo stack (internal use). void clear_redo_stack(); // === Mark and Region === /// @brief Set the mark at the specified position and activate it. void set_mark(Position pos); /// @brief Deactivate the mark (hide region highlight). void deactivate_mark(); /// @brief Get the current mark position (if set). [[nodiscard]] std::optional mark() const noexcept { return mark_; } /// @brief Check if the mark is currently active. [[nodiscard]] bool has_active_mark() const noexcept { return mark_active_; } /// @brief Get the region between the mark (if active) and the given point. [[nodiscard]] std::optional get_region(Position point) const; /// @brief Get the text content within the specified range. [[nodiscard]] std::string get_text_in_range(Range range) const; private: std::string name_; std::vector lines_; std::optional file_path_; bool modified_; std::string language_; // Styling information: one vector of styled ranges per line std::vector> line_styles_; // Event callbacks std::map event_callbacks_; size_t next_callback_handle_ = 0; // Undo/Redo stacks std::vector undo_stack_; std::vector redo_stack_; static constexpr size_t MAX_UNDO_LEVELS = 100; bool in_undo_redo_ = false; // Prevent saving state during undo/redo // Mark and Region std::optional mark_; bool mark_active_ = false; /// Ensure the buffer has at least one line void ensure_min_lines(); /// Mark the buffer as modified void mark_modified(); /// Ensure styles vector matches lines vector size void ensure_styles_size(); /// Emit a buffer event to all registered callbacks void emit_event(BufferEvent event, size_t line = 0); }; } // namespace lumacs