#include "PieceTable.h" PieceTable::PieceTable(const uint8_t * file_buffer, unsigned long file_buffer_size) { m_file_buffer = file_buffer; m_file_buffer_size = file_buffer_size; start_piece = &m_pieces[PIECE_INDEX_START]; end_piece = &m_pieces[PIECE_INDEX_END]; start_piece->next = end_piece; end_piece->prev = start_piece; m_piece_index = 2u; tabstop = 4u; } void PieceTable::append_initial_line_piece(uint8_t * start, uint32_t length, bool eol) { Piece * piece = add_piece(); piece->prev = end_piece->prev; piece->next = end_piece; piece->start = start; piece->length = length; piece->flags = 0u; if (eol) piece->flags |= Piece::EOL_FLAG; end_piece->prev->next = piece; end_piece->prev = piece; m_num_lines++; } std::shared_ptr PieceTable::add_cursor() { auto cursor = std::make_shared(this); m_cursors.push_back(cursor); return cursor; } PieceTable::Iterator::Iterator(PieceTable * pt) { piece_table = pt; piece = pt->start_piece->next; offset = 0u; } uint8_t PieceTable::Iterator::num_bytes_in_code_point() const { if (offset >= piece->length) { return 0u; } else { return Encoding::num_bytes_in_code_point(piece_table->encoding, &piece->start[offset]); } } void PieceTable::Iterator::go_next_position() { uint8_t code_point_size = num_bytes_in_code_point(); if ((offset + code_point_size) >= piece->length) { go_next_piece(); return; } offset += code_point_size; } void PieceTable::Iterator::go_prev_position() { if (offset > 0) { offset = Encoding::beginning_of_code_point(piece_table->encoding, &piece->start[offset - 1u]) - piece->start; } else if (piece != piece_table->start_piece) { piece = piece->prev; if (piece->length > 0u) { offset = Encoding::beginning_of_code_point(piece_table->encoding, &piece->start[piece->length - 1u]) - piece->start; } else { offset = 0u; } } } void PieceTable::Iterator::go_end_of_piece() { if (piece->length > 0u) { offset = Encoding::beginning_of_code_point(piece_table->encoding, &piece->start[piece->length - 1u]) - piece->start; } else { offset = 0u; } } void PieceTable::Iterator::go_next_piece() { if (piece != piece_table->end_piece) { piece = piece->next; offset = 0u; } } void PieceTable::Iterator::go_prev_piece() { if (piece != piece_table->start_piece) { piece = piece->prev; offset = 0u; } } PieceTable::Cursor::Cursor(PieceTable * pt) : iterator(pt) { line = 0u; init_column(); } bool PieceTable::Cursor::go_start_of_line() { if (!iterator.valid()) return false; bool rv = false; while (iterator.valid() && (!iterator.piece->prev->eol()) && (iterator.piece->prev != iterator.piece_table->start_piece)) { iterator.go_prev_piece(); rv = true; } if (iterator.offset != 0u) rv = true; iterator.offset = 0u; init_column(); return rv; } bool PieceTable::Cursor::go_end_of_line() { if (!iterator.valid()) return false; bool rv = false; while (iterator.valid() && (!iterator.piece->eol()) && (iterator.piece->next != iterator.piece_table->end_piece)) { iterator.go_next_piece(); rv = true; } uint32_t current_column = column; iterator.go_end_of_piece(); calculate_column(); if (column != current_column) rv = true; return rv; } bool PieceTable::Cursor::go_up(int n, uint32_t desired_column) { if (!iterator.valid()) return false; Piece * p = prev_line(); if (p == nullptr) { return false; } iterator.piece = p; iterator.offset = 0u; line--; init_column(); forward_to_column(desired_column); return true; } bool PieceTable::Cursor::go_down(int n, uint32_t desired_column) { if (!iterator.valid()) return false; Piece * p = next_line(); if (p == nullptr) { return false; } iterator.piece = p; iterator.offset = 0u; line++; init_column(); forward_to_column(desired_column); return true; } bool PieceTable::Cursor::go_left(int n) { if (!iterator.valid()) return false; if (is_start_of_line()) { return false; } uint32_t chr = *iterator; iterator.go_prev_position(); if (chr == '\t') { calculate_column(); } else { /* TODO: handle multi-column characters */ column--; } return true; } bool PieceTable::Cursor::go_right(int n) { if (!iterator.valid()) return false; if (is_end_of_line()) { return false; } iterator.go_next_position(); uint32_t chr = *iterator; if (chr == '\t') { uint8_t tabstop = iterator.piece_table->tabstop; column += tabstop - (column + 1u) % tabstop; } else { /* TODO: handle multi-column characters */ column++; } return true; } bool PieceTable::Cursor::is_start_of_line() { return iterator.valid() && (iterator.offset == 0u) && ((iterator.piece->prev == iterator.piece_table->start_piece) || iterator.piece->prev->eol()); } bool PieceTable::Cursor::is_end_of_line() { return iterator.valid() && ((iterator.offset + iterator.num_bytes_in_code_point()) >= iterator.piece->length) && (iterator.piece->eol() || (iterator.piece->next == iterator.piece_table->end_piece)); } PieceTable::Piece * PieceTable::Cursor::prev_line() const { Piece * p = iterator.piece; while ((!p->prev->eol()) && (p->prev != iterator.piece_table->start_piece)) { p = p->prev; } if (p->prev == iterator.piece_table->start_piece) { return nullptr; } p = p->prev; while ((!p->prev->eol()) && (p->prev != iterator.piece_table->start_piece)) { p = p->prev; } return p; } PieceTable::Piece * PieceTable::Cursor::next_line() const { Piece * p = iterator.piece; while ((!p->eol()) && (p->next != iterator.piece_table->end_piece)) { p = p->next; } if (p->next == iterator.piece_table->end_piece) { return nullptr; } p = p->next; while ((!p->eol()) && (p->next != iterator.piece_table->end_piece)) { p = p->next; } return p; } void PieceTable::Cursor::init_column() { uint32_t chr = *iterator; /* TODO: handle multi-column characters */ column = (chr == '\t') ? iterator.piece_table->tabstop - 1u : 0u; } void PieceTable::Cursor::calculate_column() { Cursor tmp_cursor = *this; tmp_cursor.go_start_of_line(); while ((tmp_cursor.iterator.piece != iterator.piece) || (tmp_cursor.iterator.offset < iterator.offset)) { tmp_cursor.go_right(1); } column = tmp_cursor.column; } void PieceTable::Cursor::forward_to_column(uint32_t c) { int32_t diff = abs((int32_t)(c - column)); if (diff == 0) return; while (go_right(1)) { int32_t new_diff = abs((int32_t)(c - column)); if (new_diff > diff) { go_left(1); return; } diff = new_diff; } }