diff --git a/src/core/BufferView.cc b/src/core/BufferView.cc index 7b87b7e..9840208 100644 --- a/src/core/BufferView.cc +++ b/src/core/BufferView.cc @@ -17,7 +17,9 @@ BufferView::BufferView(std::shared_ptr buffer, m_cursor_screen_column = 0; m_cursor_virtual_column = 0; m_cursor_row_offset = 0; - m_target_column = 0; + m_rows_in_cursor_line = 0; + m_target_screen_column = 0; + m_target_virtual_column = 0; m_update_target_column = false; } @@ -43,7 +45,7 @@ void BufferView::update() * and cursor column values. */ auto start_of_line = std::make_shared(*m_iterator); start_of_line->go_start_of_line(); - int rows_in_cursor_line = calculate_rows_in_cursor_line(start_of_line); + m_rows_in_cursor_line = calculate_rows_in_cursor_line(start_of_line); /* Limit the cursor screen row taking into account view dimensions, * available buffer contents above and below the current line, and scroll @@ -51,7 +53,7 @@ void BufferView::update() std::list>> backward_lines; int so = effective_scroll_offset(); int rows_above = screen_rows_above_line(start_of_line, backward_lines) + m_cursor_row_offset; - int rows_below = screen_rows_below_line(start_of_line) + std::max(0, rows_in_cursor_line - m_cursor_row_offset - 1); + int rows_below = screen_rows_below_line(start_of_line) + std::max(0, m_rows_in_cursor_line - m_cursor_row_offset - 1); int min_rows_to_leave_above = std::min(rows_above, so); int min_rows_to_leave_below = std::min(rows_below, so); m_cursor_screen_row = std::min(m_height - min_rows_to_leave_below - 1, m_cursor_screen_row); @@ -120,11 +122,13 @@ bool BufferView::cursor_move(CursorMovement which, bool allow_eol) break; case CursorMovement::SOL: moved = m_iterator->go_start_of_line(); - m_target_column = 0; + m_target_screen_column = 0; + m_target_virtual_column = 0; break; case CursorMovement::EOL: moved = m_iterator->go_end_of_line(allow_eol); - m_target_column = INT_MAX; + m_target_screen_column = INT_MAX; + m_target_virtual_column = INT_MAX; break; case CursorMovement::FIRST_LINE: { @@ -135,7 +139,8 @@ bool BufferView::cursor_move(CursorMovement which, bool allow_eol) moved = true; } } - m_target_column = 0; + m_target_screen_column = 0; + m_target_virtual_column = 0; break; case CursorMovement::LAST_LINE: { @@ -148,19 +153,14 @@ bool BufferView::cursor_move(CursorMovement which, bool allow_eol) moved = true; } } - m_target_column = 0; + m_target_screen_column = 0; + m_target_virtual_column = 0; break; case CursorMovement::SCREEN_ROW_UP: - /* TODO */ -#if 0 - moved = move_cursor_screen_row_up(); -#endif + moved = move_cursor_screen_row_up(allow_eol); break; case CursorMovement::SCREEN_ROW_DOWN: - /* TODO */ -#if 0 - moved = move_cursor_screen_row_down(); -#endif + moved = move_cursor_screen_row_down(allow_eol); break; } if (moved) @@ -173,7 +173,7 @@ bool BufferView::cursor_move(CursorMovement which, bool allow_eol) break; case CursorMovement::UP: case CursorMovement::DOWN: - move_forward_to_column(m_target_column, allow_eol); + move_forward_to_column(m_target_virtual_column, allow_eol); determine_new_cursor_screen_row(); break; case CursorMovement::SOL: @@ -249,7 +249,8 @@ int BufferView::calculate_rows_in_cursor_line(std::shared_ptr m_cursor_screen_column = it.screen_column(); if (m_update_target_column) { - m_target_column = m_cursor_virtual_column; + m_target_screen_column = m_cursor_screen_column; + m_target_virtual_column = m_cursor_virtual_column; } m_cursor_row_offset = it.row_offset(); } @@ -311,3 +312,65 @@ void BufferView::move_forward_to_column(int to_column, bool allow_eol) } } } + +bool BufferView::move_cursor_screen_row_up(bool allow_eol) +{ + if (m_cursor_row_offset > 0) + { + /* There are screen rows above the current in the current line. */ + auto start_of_line = std::make_shared(*m_iterator); + start_of_line->go_start_of_line(); + move_forward_to_screen_position(start_of_line, m_cursor_row_offset - 1, allow_eol); + } + else + { + auto previous_line = std::make_shared(*m_iterator); + if (!previous_line->go_previous_line()) + { + return false; + } + int target_row_offset = calculate_rows_in_line(previous_line) - 1; + move_forward_to_screen_position(previous_line, target_row_offset, allow_eol); + } + return true; +} + +bool BufferView::move_cursor_screen_row_down(bool allow_eol) +{ + if (m_rows_in_cursor_line > (m_cursor_row_offset + 1)) + { + /* There are screen rows below the current in the current line. */ + auto start_of_line = std::make_shared(*m_iterator); + start_of_line->go_start_of_line(); + move_forward_to_screen_position(start_of_line, m_cursor_row_offset + 1, allow_eol); + } + else + { + auto next_line = std::make_shared(*m_iterator); + if (!next_line->go_next_line()) + { + return false; + } + move_forward_to_screen_position(next_line, 0, allow_eol); + } + return true; +} + +void BufferView::move_forward_to_screen_position( + std::shared_ptr line, int target_row_offset, + bool allow_eol) +{ + for (auto it = horiz_iter(line); it.is_valid(); it++) + { + if ((allow_eol || (!it.is_eol())) && + ((it.row_offset() < target_row_offset) || + (it.screen_column() <= m_target_screen_column))) + { + *m_iterator = *it.iterator(); + } + else + { + break; + } + } +} diff --git a/src/core/BufferView.h b/src/core/BufferView.h index 44364c0..658124e 100644 --- a/src/core/BufferView.h +++ b/src/core/BufferView.h @@ -104,7 +104,9 @@ protected: int m_cursor_screen_column; int m_cursor_virtual_column; int m_cursor_row_offset; - int m_target_column; + int m_rows_in_cursor_line; + int m_target_screen_column; + int m_target_virtual_column; bool m_update_target_column; int effective_scroll_offset() @@ -119,6 +121,11 @@ protected: std::shared_ptr line, std::list>> & backward_lines); void move_forward_to_column(int to_column, bool allow_eol); + bool move_cursor_screen_row_up(bool allow_eol); + bool move_cursor_screen_row_down(bool allow_eol); + void move_forward_to_screen_position( + std::shared_ptr line, int target_row_offset, + bool allow_eol); }; #endif diff --git a/test/src/test_BufferView.cc b/test/src/test_BufferView.cc index 11d5ef6..316ab8c 100644 --- a/test/src/test_BufferView.cc +++ b/test/src/test_BufferView.cc @@ -467,3 +467,56 @@ TEST(BufferViewTest, sets_target_column_when_traversing_left_and_right) EXPECT_EQ(1, LineNumber(iterator)); EXPECT_EQ(C('a'), **iterator); } + +TEST(BufferViewTest, moves_cursor_by_screen_row) +{ + auto b = buffer1(); + auto iterator = b->add_iterator(); + BufferView bv(b, iterator, Cwd); + bv.resize(4, 4); + for (int i = 0; i < 6; i++) + { + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, false)); + bv.update(); + EXPECT_EQ(1 + i, LineNumber(iterator)); + } + EXPECT_EQ(C('6'), **iterator); + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, false)); + bv.update(); + EXPECT_EQ(6, LineNumber(iterator)); + EXPECT_EQ(C('d'), **iterator); + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, false)); + bv.update(); + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, false)); + bv.update(); + EXPECT_EQ(C('f'), **iterator); + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, false)); + bv.update(); + EXPECT_EQ(C('7'), **iterator); + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_UP, false)); + bv.update(); + EXPECT_EQ(C('f'), **iterator); + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_UP, false)); + bv.update(); + EXPECT_EQ(C('b'), **iterator); + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_UP, false)); + bv.update(); + EXPECT_EQ(C('5'), **iterator); +} + +TEST(BufferViewTest, cursor_move_screen_row_up_and_down_returns_false_at_top_and_bottom) +{ + auto b = buffer1(); + auto iterator = b->add_iterator(); + BufferView bv(b, iterator, Cwd); + bv.resize(4, 4); + EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_UP, false)); + bv.update(); + EXPECT_EQ(0, LineNumber(iterator)); + for (int i = 0; i < 16; i++) + { + EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, false)); + bv.update(); + } + EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::SCREEN_ROW_DOWN, false)); +}