From 33a190c495ffa518cce8bb5a2e21d31b6907bc1b Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sat, 27 Aug 2016 16:53:55 -0400 Subject: [PATCH] complete cursor movement routines taking Encoding and tabs into account --- src/core/PieceTable.cc | 94 +++++++++++++++++++++++++---------------- src/core/PieceTable.h | 16 ++++--- test/src/test_Buffer.cc | 12 +++--- 3 files changed, 74 insertions(+), 48 deletions(-) diff --git a/src/core/PieceTable.cc b/src/core/PieceTable.cc index 74f7140..91310a5 100644 --- a/src/core/PieceTable.cc +++ b/src/core/PieceTable.cc @@ -116,25 +116,11 @@ void PieceTable::Iterator::go_prev_piece() } } -#if 0 -uint32_t PieceTable::Cursor::column() const +PieceTable::Cursor::Cursor(PieceTable * pt) : iterator(pt) { - /* 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; + line = iterator.valid() ? 1u : 0u; + init_column(); } -#endif void PieceTable::Cursor::go_start_of_line() { @@ -142,6 +128,7 @@ void PieceTable::Cursor::go_start_of_line() { iterator.go_prev_piece(); } + init_column(); } void PieceTable::Cursor::go_end_of_line() @@ -151,9 +138,10 @@ void PieceTable::Cursor::go_end_of_line() iterator.go_next_piece(); } iterator.go_end_of_piece(); + calculate_column(); } -bool PieceTable::Cursor::go_up(int n) +bool PieceTable::Cursor::go_up(int n, uint32_t desired_column) { Piece * p = prev_line(); if (p == nullptr) @@ -162,14 +150,13 @@ bool PieceTable::Cursor::go_up(int n) } iterator.piece = p; iterator.offset = 0u; -#if 0 - uint32_t current_column = column(); - forward_to_column(current_column); -#endif + line--; + init_column(); + forward_to_column(desired_column); return true; } -bool PieceTable::Cursor::go_down(int n) +bool PieceTable::Cursor::go_down(int n, uint32_t desired_column) { Piece * p = next_line(); if (p == nullptr) @@ -178,10 +165,9 @@ bool PieceTable::Cursor::go_down(int n) } iterator.piece = p; iterator.offset = 0u; -#if 0 - uint32_t current_column = column(); - forward_to_column(current_column); -#endif + line++; + init_column(); + forward_to_column(desired_column); return true; } @@ -191,7 +177,17 @@ bool PieceTable::Cursor::go_left(int n) { return false; } + uint32_t chr = *iterator; iterator.go_prev_position(); + if (chr == '\t') + { + calculate_column(); + } + else + { + /* TODO: handle multi-column characters */ + column--; + } return true; } @@ -202,6 +198,17 @@ bool PieceTable::Cursor::go_right(int n) return false; } iterator.go_next_position(); + uint32_t chr = *iterator; + if (chr == '\t') + { + column += iterator.piece_table->tabstop; + column -= column % iterator.piece_table->tabstop; + } + else + { + /* TODO: handle multi-column characters */ + column++; + } return true; } @@ -257,23 +264,36 @@ PieceTable::Piece * PieceTable::Cursor::next_line() const return p; } -#if 0 +void PieceTable::Cursor::init_column() +{ + uint32_t chr = *iterator; + /* TODO: handle multi-column characters */ + column = (chr == 0xFFFFFFFFu) ? 0u : (chr == '\t') ? iterator.piece_table->tabstop : 1u; +} + +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) { - uint32_t current_column = column(); - /* TODO: support tabs */ - /* TODO: Use Encoding */ - int32_t diff = abs((int32_t)(c - current_column)); - while (go_right()) + int32_t diff = abs((int32_t)(c - column)); + while (go_right(1)) { - current_column++; - int32_t new_diff = abs((int32_t)(c - current_column)); + int32_t new_diff = abs((int32_t)(c - column)); if (new_diff > diff) { - go_left(); + go_left(1); return; } diff = new_diff; } } -#endif diff --git a/src/core/PieceTable.h b/src/core/PieceTable.h index 64e864f..d4f0a37 100644 --- a/src/core/PieceTable.h +++ b/src/core/PieceTable.h @@ -98,26 +98,32 @@ public: { 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) : iterator(pt) {} + Cursor(PieceTable * pt); 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); + bool go_up(int n, uint32_t desired_column); + bool go_down(int n, uint32_t desired_column); + bool go_left(int n); + bool go_right(int n); 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); }; diff --git a/test/src/test_Buffer.cc b/test/src/test_Buffer.cc index e183157..1f4b8f9 100644 --- a/test/src/test_Buffer.cc +++ b/test/src/test_Buffer.cc @@ -28,16 +28,16 @@ TEST(BufferTest, allows_navigating_using_cursors) ASSERT_TRUE(b.load_from_file("test/files/line_endings/lf_format.txt")); std::shared_ptr cursor = b.piece_table->add_cursor(); ASSERT_EQ((uint32_t)'H', **cursor); - cursor->go_up(); + cursor->go_up(1, cursor->column); ASSERT_EQ((uint32_t)'H', **cursor); - cursor->go_right(); + cursor->go_right(1); ASSERT_EQ((uint32_t)'e', **cursor); - cursor->go_down(); + cursor->go_down(1, cursor->column); ASSERT_EQ((uint32_t)'h', **cursor); - cursor->go_left(); + cursor->go_left(1); ASSERT_EQ((uint32_t)'T', **cursor); - cursor->go_left(); + cursor->go_left(1); ASSERT_EQ((uint32_t)'T', **cursor); - cursor->go_down(); + cursor->go_down(1, cursor->column); ASSERT_EQ((uint32_t)'T', **cursor); }