From 3c5a7b73419aa476f9cd9a09bdfcaf7156ed0a6b Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 7 Aug 2016 22:09:54 -0400 Subject: [PATCH] add initial cursor movement support --- src/core/PieceTable.cc | 187 +++++++++++++++++++++++++++++++++++++--- src/core/PieceTable.h | 26 ++++-- test/src/test_Buffer.cc | 14 ++- 3 files changed, 208 insertions(+), 19 deletions(-) diff --git a/src/core/PieceTable.cc b/src/core/PieceTable.cc index 76ea3fe..cf54972 100644 --- a/src/core/PieceTable.cc +++ b/src/core/PieceTable.cc @@ -27,18 +27,6 @@ void PieceTable::append_initial_line_piece(uint8_t * start, uint32_t length, boo m_num_lines++; } -PieceTable::Piece * PieceTable::get_start_of_line(PieceTable::Piece * start) const -{ - Piece * piece = start; - - while (piece->prev != start_piece && !piece->prev->eol()) - { - piece = piece->prev; - } - - return piece; -} - std::shared_ptr PieceTable::add_cursor() { auto cursor = std::make_shared(); @@ -47,9 +35,182 @@ std::shared_ptr PieceTable::add_cursor() cursor->piece = start_piece->next; cursor->line_number = 0u; cursor->offset = 0u; - cursor->column = 0u; m_cursors.push_back(cursor); return cursor; } + +uint32_t PieceTable::Cursor::column() const +{ + /* TODO: support tabs */ + Cursor sol = *this; + sol.go_start_of_line(); + if (sol.piece->eol() && sol.piece->length == 0u) + { + return 0u; + } + uint32_t c = 1u; + while ((sol.piece != piece) || (sol.offset < offset)) + { + sol.go_right(); + c++; + } + return c; +} + +void PieceTable::Cursor::go_start_of_line() +{ + while ((!piece->prev->eol()) && (piece->prev != piece_table->start_piece)) + { + piece = piece->prev; + } + offset = 0u; +} + +void PieceTable::Cursor::go_end_of_line() +{ + /* TODO: Use Encoding */ + while ((!piece->eol()) && (piece->next != piece_table->end_piece)) + { + piece = piece->next; + } + offset = (piece->length > 0u) ? piece->length - 1u : 0u; +} + +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; + } + uint32_t current_column = column(); + piece = p; + offset = 0u; + line_number--; + forward_to_column(current_column); + 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; + } + uint32_t current_column = column(); + piece = p; + offset = 0u; + line_number++; + forward_to_column(current_column); + 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 + { + return false; + } + 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 + { + return false; + } + return true; +} + +PieceTable::Piece * PieceTable::Cursor::prev_line() const +{ + Piece * p = piece; + while ((!p->prev->eol()) && (p->prev != piece_table->start_piece)) + { + p = p->prev; + } + if (p->prev == piece_table->start_piece) + { + return nullptr; + } + p = p->prev; + while ((!p->prev->eol()) && (p->prev != piece_table->start_piece)) + { + p = p->prev; + } + return p; +} + +PieceTable::Piece * PieceTable::Cursor::next_line() const +{ + Piece * p = piece; + while ((!p->eol()) && (p->next != piece_table->end_piece)) + { + p = p->next; + } + if (p->next == piece_table->end_piece) + { + return nullptr; + } + p = p->next; + while ((!p->eol()) && (p->next != piece_table->end_piece)) + { + p = p->next; + } + return p; +} + +void PieceTable::Cursor::forward_to_column(uint32_t c) +{ + uint32_t current_column = column(); + /* TODO: support tabs */ + /* TODO: Use Encoding */ + int32_t diff = abs((int32_t)(c - current_column)); + while (go_right()) + { + current_column++; + int32_t new_diff = abs((int32_t)(c - current_column)); + if (new_diff > diff) + { + go_left(); + return; + } + diff = new_diff; + } +} diff --git a/src/core/PieceTable.h b/src/core/PieceTable.h index 3600a39..be78923 100644 --- a/src/core/PieceTable.h +++ b/src/core/PieceTable.h @@ -61,14 +61,32 @@ public: /** Line number the cursor is on (0 based). */ uint32_t line_number; - /** Virtual column (0 based). */ - uint32_t column; - + /** Get the character pointed to by the cursor. */ uint32_t operator*() const { /* TODO: Use Encoding */ return piece->start[offset]; } + + /** + * 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); + bool go_down(int n = 1); + bool go_left(int n = 1); + bool go_right(int n = 1); + + protected: + Piece * prev_line() const; + Piece * next_line() const; + void forward_to_column(uint32_t c); }; Piece * start_piece; @@ -91,8 +109,6 @@ public: void append_initial_line_piece(uint8_t * start, uint32_t length, bool eol); - Piece * get_start_of_line(Piece * start) const; - std::shared_ptr add_cursor(); protected: diff --git a/test/src/test_Buffer.cc b/test/src/test_Buffer.cc index 3aedb48..e183157 100644 --- a/test/src/test_Buffer.cc +++ b/test/src/test_Buffer.cc @@ -27,5 +27,17 @@ TEST(BufferTest, allows_navigating_using_cursors) Buffer b; ASSERT_TRUE(b.load_from_file("test/files/line_endings/lf_format.txt")); std::shared_ptr cursor = b.piece_table->add_cursor(); - EXPECT_EQ((uint32_t)'H', **cursor); + ASSERT_EQ((uint32_t)'H', **cursor); + cursor->go_up(); + ASSERT_EQ((uint32_t)'H', **cursor); + cursor->go_right(); + ASSERT_EQ((uint32_t)'e', **cursor); + cursor->go_down(); + ASSERT_EQ((uint32_t)'h', **cursor); + cursor->go_left(); + ASSERT_EQ((uint32_t)'T', **cursor); + cursor->go_left(); + ASSERT_EQ((uint32_t)'T', **cursor); + cursor->go_down(); + ASSERT_EQ((uint32_t)'T', **cursor); }