diff --git a/src/core/PieceTable.cc b/src/core/PieceTable.cc index cf54972..cf74667 100644 --- a/src/core/PieceTable.cc +++ b/src/core/PieceTable.cc @@ -31,16 +31,89 @@ std::shared_ptr PieceTable::add_cursor() { auto cursor = std::make_shared(); - cursor->piece_table = this; - cursor->piece = start_piece->next; - cursor->line_number = 0u; - cursor->offset = 0u; + cursor->iterator.piece_table = this; + cursor->iterator.piece = start_piece->next; + cursor->iterator.offset = 0u; m_cursors.push_back(cursor); return cursor; } +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; + } +} + +#if 0 uint32_t PieceTable::Cursor::column() const { /* TODO: support tabs */ @@ -58,119 +131,104 @@ uint32_t PieceTable::Cursor::column() const } return c; } +#endif void PieceTable::Cursor::go_start_of_line() { - while ((!piece->prev->eol()) && (piece->prev != piece_table->start_piece)) + while (iterator.valid() && (!iterator.piece->prev->eol())) { - piece = piece->prev; + iterator.go_prev_piece(); } - offset = 0u; } void PieceTable::Cursor::go_end_of_line() { - /* TODO: Use Encoding */ - while ((!piece->eol()) && (piece->next != piece_table->end_piece)) + while (iterator.valid() && (!iterator.piece->eol())) { - piece = piece->next; + iterator.go_next_piece(); } - offset = (piece->length > 0u) ? piece->length - 1u : 0u; + iterator.go_end_of_piece(); } bool PieceTable::Cursor::go_up(int n) { - /* TODO: Use Encoding */ - /* TODO: support tabs */ - /* TODO: support n */ Piece * p = prev_line(); if (p == nullptr) { return false; } + iterator.piece = p; + iterator.offset = 0u; +#if 0 uint32_t current_column = column(); - piece = p; - offset = 0u; - line_number--; forward_to_column(current_column); +#endif return true; } bool PieceTable::Cursor::go_down(int n) { - /* TODO: Use Encoding */ - /* TODO: support tabs */ - /* TODO: support n */ Piece * p = next_line(); if (p == nullptr) { return false; } + iterator.piece = p; + iterator.offset = 0u; +#if 0 uint32_t current_column = column(); - piece = p; - offset = 0u; - line_number++; forward_to_column(current_column); +#endif return true; } bool PieceTable::Cursor::go_left(int n) { - /* TODO: Use Encoding */ - /* TODO: support tabs */ - /* TODO: support n */ - if (offset > 0) - { - offset--; - } - else if ((piece->prev != piece_table->start_piece) && - (!piece->prev->eol())) - { - piece = piece->prev; - offset = piece->length - 1u; - } - else + if (is_start_of_line()) { return false; } + iterator.go_prev_position(); return true; } bool PieceTable::Cursor::go_right(int n) { - /* TODO: Use Encoding */ - /* TODO: support tabs */ - /* TODO: support n */ - if (offset < piece->length - 1u) - { - offset++; - } - else if ((piece->next != piece_table->end_piece) && - (!piece->eol())) - { - piece = piece->next; - offset = 0u; - } - else + if (is_end_of_line()) { return false; } + iterator.go_next_position(); return true; } +bool PieceTable::Cursor::is_start_of_line() +{ + return iterator.valid() && + (iterator.offset == 0u) && + (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(); +} + PieceTable::Piece * PieceTable::Cursor::prev_line() const { - Piece * p = piece; - while ((!p->prev->eol()) && (p->prev != piece_table->start_piece)) + Piece * p = iterator.piece; + while ((!p->prev->eol()) && (p->prev != iterator.piece_table->start_piece)) { p = p->prev; } - if (p->prev == piece_table->start_piece) + if (p->prev == iterator.piece_table->start_piece) { return nullptr; } p = p->prev; - while ((!p->prev->eol()) && (p->prev != piece_table->start_piece)) + while ((!p->prev->eol()) && (p->prev != iterator.piece_table->start_piece)) { p = p->prev; } @@ -179,23 +237,24 @@ PieceTable::Piece * PieceTable::Cursor::prev_line() const PieceTable::Piece * PieceTable::Cursor::next_line() const { - Piece * p = piece; - while ((!p->eol()) && (p->next != piece_table->end_piece)) + Piece * p = iterator.piece; + while ((!p->eol()) && (p->next != iterator.piece_table->end_piece)) { p = p->next; } - if (p->next == piece_table->end_piece) + if (p->next == iterator.piece_table->end_piece) { return nullptr; } p = p->next; - while ((!p->eol()) && (p->next != piece_table->end_piece)) + while ((!p->eol()) && (p->next != iterator.piece_table->end_piece)) { p = p->next; } return p; } +#if 0 void PieceTable::Cursor::forward_to_column(uint32_t c) { uint32_t current_column = column(); @@ -214,3 +273,4 @@ void PieceTable::Cursor::forward_to_column(uint32_t c) diff = new_diff; } } +#endif diff --git a/src/core/PieceTable.h b/src/core/PieceTable.h index f3c6247..b877b38 100644 --- a/src/core/PieceTable.h +++ b/src/core/PieceTable.h @@ -48,7 +48,7 @@ public: void toggle_eol() { flags ^= EOL_FLAG; } }; - struct Cursor + struct Iterator { /** The piece table the cursor belongs to. */ PieceTable * piece_table; @@ -59,23 +59,49 @@ public: /** Byte offset within the piece. */ uint32_t offset; - /** Line number the cursor is on (0 based). */ - uint32_t line_number; + /** 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); + } + }; + + struct Cursor + { + Iterator iterator; /** Get the character pointed to by the cursor. */ uint32_t operator*() const { - return Encoding::decode(piece_table->encoding, &piece->start[offset]); + return *iterator; } - /** - * Get the screen column of the cursor (taking into account tabs). - * - * This function will return 0 only if the cursor is on an empty line. - * The first column of a line with at least one character is column 1. - */ - uint32_t column() const; - void go_start_of_line(); void go_end_of_line(); bool go_up(int n = 1); @@ -84,6 +110,8 @@ public: bool go_right(int n = 1); protected: + bool is_start_of_line(); + bool is_end_of_line(); Piece * prev_line() const; Piece * next_line() const; void forward_to_column(uint32_t c);