#include "PieceTable.h" PieceTable::PieceTable() { m_piece_index = 0u; start_piece = add_piece(); end_piece = add_piece(); start_piece->length = 0u; end_piece->length = 0u; start_piece->next = end_piece; end_piece->prev = start_piece; tabstop = 4u; m_append_buffer_index = 0u; m_changes_index = 0u; } 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; 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; while ((piece->length == 0u) && (piece != piece_table->end_piece)) { piece = 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() { if (valid()) { uint8_t code_point_size = num_bytes_in_code_point(); if ((offset + code_point_size) >= piece->length) { do { piece = piece->next; } while ((piece != piece_table->end_piece) && (piece->length == 0u)); offset = 0u; return; } offset += code_point_size; } } void PieceTable::Iterator::go_prev_position() { if (piece != piece_table->start_piece) { if (offset > 0) { offset = Encoding::beginning_of_code_point(piece_table->encoding, &piece->start[offset - 1u]) - piece->start; } else { do { piece = piece->prev; } while ((piece != piece_table->start_piece) && (piece->length == 0u)); 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) { do { piece = piece->next; } while ((piece != piece_table->end_piece) && (piece->length == 0u)); offset = 0u; } } void PieceTable::Iterator::go_prev_piece() { if (piece != piece_table->start_piece) { do { piece = piece->prev; } while ((piece != piece_table->start_piece) && (piece->length == 0u)); offset = 0u; } } PieceTable::Cursor::Cursor(PieceTable * pt) : iterator(pt) { line = 0u; init_column(); } void PieceTable::Cursor::go_start_of_line() { Iterator last_iterator = iterator; for (;;) { iterator.go_prev_piece(); if (iterator.piece_eol() || !iterator.valid()) { break; } last_iterator = iterator; } iterator = last_iterator; iterator.offset = 0u; init_column(); } bool PieceTable::Cursor::check_go_start_of_line() { uint32_t current_line = line; uint32_t current_column = column; go_start_of_line(); return (current_line != line) || (current_column != column); } void PieceTable::Cursor::go_end_of_line(bool allow_on_eol) { if (!iterator.valid()) { return; } for (;;) { if (iterator.piece_eol()) { break; } Iterator move_iterator = iterator; move_iterator.go_next_piece(); if (move_iterator.valid()) { iterator = move_iterator; } else { break; } } iterator.go_end_of_piece(); calculate_column(); if (!allow_on_eol) { go_left(1); } } bool PieceTable::Cursor::check_go_end_of_line(bool allow_on_eol) { uint32_t current_line = line; uint32_t current_column = column; go_end_of_line(allow_on_eol); return (current_line != line) || (current_column != column); } void PieceTable::Cursor::go_up(int n, uint32_t desired_column) { if (!iterator.valid()) return; Piece * p = prev_line(); if (p == nullptr) { return; } iterator.piece = p; iterator.offset = 0u; line--; init_column(); forward_to_column(desired_column); } bool PieceTable::Cursor::check_go_up(int n, uint32_t desired_column) { uint32_t current_line = line; uint32_t current_column = column; go_up(n, desired_column); return (current_line != line) || (current_column != column); } void PieceTable::Cursor::go_down(int n, uint32_t desired_column) { if (!iterator.valid()) return; Piece * p = next_line(); if (p == nullptr) { return; } iterator.piece = p; iterator.offset = 0u; line++; init_column(); forward_to_column(desired_column); } bool PieceTable::Cursor::check_go_down(int n, uint32_t desired_column) { uint32_t current_line = line; uint32_t current_column = column; go_down(n, desired_column); return (current_line != line) || (current_column != column); } void PieceTable::Cursor::go_left(int n) { if (!iterator.valid()) return; if (is_start_of_line()) { return; } uint32_t chr = *iterator; iterator.go_prev_position(); if (chr == '\t') { calculate_column(); } else { /* TODO: handle multi-column characters */ column--; } } bool PieceTable::Cursor::check_go_left(int n) { uint32_t current_line = line; uint32_t current_column = column; go_left(n); return (current_line != line) || (current_column != column); } void PieceTable::Cursor::go_right(int n, bool allow_on_eol) { if (!iterator.valid()) return; if (is_end_of_line()) { return; } Iterator iterator_2 = iterator; iterator.go_next_position(); if ((!allow_on_eol) && is_end_of_line()) { iterator = iterator_2; return; } 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++; } } bool PieceTable::Cursor::check_go_right(int n, bool allow_on_eol) { uint32_t current_line = line; uint32_t current_column = column; go_right(n, allow_on_eol); return (current_line != line) || (current_column != column); } void PieceTable::Cursor::warp_to_inserted_piece(Piece * piece) { iterator.piece = piece; iterator.offset = 0u; iterator.go_end_of_piece(); calculate_column(); } 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; } return p->next; } 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, true); } 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 (check_go_right(1, false)) { int32_t new_diff = abs((int32_t)(c - column)); if (new_diff > diff) { go_left(1); return; } diff = new_diff; } } void PieceTable::begin_insert(const Cursor & cursor, bool before) { Piece * piece = cursor.iterator.piece; uint32_t offset = cursor.iterator.offset; if (!before && cursor.iterator.has_char() && (*cursor != INTERNAL_EOL)) { offset += cursor.iterator.num_bytes_in_code_point(); } if (offset == 0u) { /* Insert happens at piece boundary between piece->prev and piece. */ m_insert_before_piece = piece->prev; } else if (offset >= piece->length) { /* Insert happens at piece boundary between piece and piece->next. */ m_insert_before_piece = piece; } else { /* Insert happens in the middle of a current piece. The piece must * be split up. */ Piece * new_piece_1 = add_piece(); new_piece_1->start = piece->start; new_piece_1->length = offset; Piece * new_piece_2 = add_piece(); new_piece_2->start = piece->start + offset; new_piece_2->length = piece->length - offset; new_piece_1->prev = piece->prev; new_piece_1->next = new_piece_2; new_piece_2->prev = new_piece_1; new_piece_2->next = piece->next; Change * change = add_change(); change->before = piece->prev; change->after = piece->next; change->chains[0][0] = piece; change->chains[0][1] = nullptr; change->chains[1][0] = new_piece_1; change->chains[1][1] = new_piece_2; apply_change(change, 1u); m_insert_before_piece = new_piece_1; } m_inserting_piece = nullptr; } void PieceTable::insert_code_point(uint32_t code_point) { if (m_inserting_piece == nullptr) { m_inserting_piece = add_piece(); m_inserting_piece->start = &m_append_buffer[m_append_buffer_index]; m_inserting_piece->length = 0u; m_inserting_piece->prev = m_insert_before_piece; m_inserting_piece->next = m_insert_before_piece->next; Change * change = add_change(); change->before = m_insert_before_piece; change->after = m_insert_before_piece->next; change->chains[0][0] = nullptr; change->chains[0][1] = nullptr; change->chains[1][0] = m_inserting_piece; change->chains[1][1] = nullptr; apply_change(change, 1u); } uint8_t bytes = Encoding::encode(code_point, encoding, &m_append_buffer[m_append_buffer_index]); m_inserting_piece->length += bytes; m_append_buffer_index += bytes; } void PieceTable::end_insert() { } void PieceTable::insertion_test(Cursor & c) { Piece * piece = add_piece(); piece->start = &m_append_buffer[m_append_buffer_index]; piece->length = 3u; m_append_buffer[m_append_buffer_index++] = 'x'; m_append_buffer[m_append_buffer_index++] = 'y'; m_append_buffer[m_append_buffer_index++] = 'z'; Change * change = add_change(); Change * change2 = nullptr; if (c.iterator.valid()) { change->before = c.iterator.piece->prev; change->after = c.iterator.piece->next; change->chains[0][0] = c.iterator.piece; change->chains[0][1] = nullptr; if (c.iterator.offset > 0u) { Piece * piece2 = add_piece(); piece2->start = c.iterator.piece->start; piece2->length = c.iterator.offset; Piece * piece3 = add_piece(); piece3->start = c.iterator.piece->start + c.iterator.offset; piece3->length = c.iterator.piece->length - c.iterator.offset; piece2->next = piece; piece->prev = piece2; piece->next = piece3; piece3->prev = piece; change->chains[1][0] = piece2; change->chains[1][1] = piece; change2 = add_change(); change2->before = piece; change2->after = c.iterator.piece->next; change2->chains[0][0] = nullptr; change2->chains[0][1] = nullptr; change2->chains[1][0] = piece3; change2->chains[1][1] = nullptr; } else { change->chains[1][0] = piece; change->chains[1][1] = nullptr; if (c.iterator.piece->length != 0u) { change->after = c.iterator.piece; } } } else { change->before = start_piece; change->after = end_piece; change->chains[0][0] = nullptr; change->chains[0][1] = nullptr; change->chains[1][0] = piece; change->chains[1][1] = nullptr; } apply_change(change, 1u); if (change2 != nullptr) { apply_change(change2, 1u); } c.warp_to_inserted_piece(piece); } void PieceTable::undo() { if (m_changes_index > 0u) { m_changes_index--; apply_change(&m_changes[m_changes_index], 0u); } } void PieceTable::apply_change(Change * change, uint8_t forward) { Piece * p = change->before; for (uint8_t i = 0; i < sizeof(change->chains[0]) / sizeof(change->chains[0][0]); i++) { if (change->chains[forward][i] == nullptr) { break; } p->next = change->chains[forward][i]; change->chains[forward][i]->prev = p; p = change->chains[forward][i]; } p->next = change->after; change->after->prev = p; }