#ifndef PIECETABLE_H #define PIECETABLE_H #include #include "PagedBuffer.h" #include #include #include #include "Encoding.h" class PieceTable { public: enum { INTERNAL_EOL = '\n', }; struct Piece { /** The previous piece in the chain. */ Piece * prev; /** The next piece in the chain. */ Piece * next; /** Text that the piece describes. */ uint8_t * start; /** Length of the text (in bytes). */ uint32_t length; /** Get whether this piece is the end of a line. */ bool eol() { return (length > 0u) && (start[length - 1u] == INTERNAL_EOL); } }; struct Iterator { /** The piece table the cursor belongs to. */ PieceTable * piece_table; /** The piece. */ Piece * piece; /** Byte offset within the piece. */ uint32_t offset; Iterator(PieceTable * pt); /** Get the character pointed to by the cursor. */ uint32_t operator*() const { if (has_char()) { return Encoding::decode(piece_table->encoding, &piece->start[offset]); } else { return 0xFFFFFFFFu; } } uint8_t num_bytes_in_code_point() const; void go_next_position(); void go_prev_position(); void go_end_of_piece(); void go_next_piece(); void go_prev_piece(); bool valid() const { return (piece != piece_table->start_piece) && (piece != piece_table->end_piece); } bool has_char() const { return valid() && (offset < piece->length); } bool operator==(const Iterator & other) const { return (other.piece == piece) && (other.offset == offset); } bool operator!=(const Iterator & other) const { return !(*this == other); } }; struct Cursor { Iterator iterator; uint32_t line; uint32_t column; /** Get the character pointed to by the cursor. */ uint32_t operator*() const { return *iterator; } Cursor(PieceTable * pt); void go_start_of_line(); void go_end_of_line(bool allow_on_eol); void go_up(int n, uint32_t desired_column); void go_down(int n, uint32_t desired_column); void go_left(int n); void go_right(int n, bool allow_on_eol); bool check_go_start_of_line(); bool check_go_end_of_line(bool allow_on_eol); bool check_go_up(int n, uint32_t desired_column); bool check_go_down(int n, uint32_t desired_column); bool check_go_left(int n); bool check_go_right(int n, bool allow_on_eol); bool operator==(const Cursor & c) const { return (c.line == line) && (c.column == column); } bool operator!=(const Cursor & c) const { return !(*this == c); } void warp_to_inserted_piece(Piece * piece); protected: bool is_start_of_line(); bool is_end_of_line(); Piece * prev_line() const; Piece * next_line() const; void init_column(); void calculate_column(); void forward_to_column(uint32_t c); }; struct Change { Piece * before; Piece * after; Piece * chains[2][2]; }; Piece * start_piece; Piece * end_piece; uint8_t tabstop; Encoding::Type encoding; PieceTable(); uint32_t get_num_lines() { return m_num_lines; } void append_initial_line_piece(uint8_t * start, uint32_t length, bool eol); std::shared_ptr add_cursor(); void begin_insert(const Cursor & cursor, bool before); void insert_code_point(uint32_t code_point); void end_insert(); void insertion_test(Cursor & c); void undo(); protected: Piece * add_piece() { return &m_pieces[m_piece_index++]; } Piece & operator[](uint32_t index) { return m_pieces[index]; } Change * add_change() { return &m_changes[m_changes_index++]; } void apply_change(Change * change, uint8_t forward); uint32_t m_num_lines; PagedBuffer m_append_buffer; uint32_t m_append_buffer_index; PagedBuffer m_pieces; /** Next available piece index. */ uint32_t m_piece_index; PagedBuffer m_changes; uint32_t m_changes_index; Piece * m_insert_before_piece; Piece * m_inserting_piece; std::list> m_cursors; }; #endif