jes/src/core/PieceTable.h

204 lines
4.6 KiB
C++

#ifndef PIECETABLE_H
#define PIECETABLE_H
#include <stdint.h>
#include "PagedBuffer.h"
#include <utility>
#include <memory>
#include <list>
#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<Cursor> 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<uint8_t> m_append_buffer;
uint32_t m_append_buffer_index;
PagedBuffer<Piece> m_pieces;
/** Next available piece index. */
uint32_t m_piece_index;
PagedBuffer<Change> m_changes;
uint32_t m_changes_index;
Piece * m_insert_before_piece;
Piece * m_inserting_piece;
std::list<std::shared_ptr<Cursor>> m_cursors;
};
#endif