#include "gtest/gtest.h" #include "BufferView.h" #define C(x) ((uint32_t)(x)) static std::shared_ptr buffer1() { static const char data[] = "0\n" "1ab\n" "2abc\n" "3\n" "4\n" "5\n" "6abcdefg\n" "7\n" "8\n" "9abcdefghijklmno\n" "10\n" "11\n" "12\n"; return std::make_shared((const uint8_t *)data, strlen(data)); } static std::shared_ptr buffer_empty() { return std::make_shared(nullptr, 0u); } class MyCharacterWidthDeterminer : public CharacterWidthDeterminer { int operator()(uint32_t character) { if (character < 32) { return 2; } else { return 1; } } } Cwd; static int LineNumber(std::shared_ptr iterator) { int line_number = 0; auto i = std::make_shared(*iterator); i->go_start_of_line(); for (;;) { uint32_t c = **i; if ((c < '0') || (c > '9')) break; line_number *= 10; line_number += ((int)c - '0'); i->go_forward(); } return line_number; } TEST(BufferViewTest, does_not_iterate_for_empty_buffer) { std::shared_ptr b = buffer_empty(); std::shared_ptr iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(40, 8); int iter_count = 0; bv.update(); for (auto it = bv.vert_iter(); it.is_valid(); it++) { iter_count++; } EXPECT_EQ(0, iter_count); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); } TEST(BufferViewTest, iterates_through_screen_lines_with_no_wrapping) { std::shared_ptr b = buffer1(); std::shared_ptr iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(40, 8); int iter_count = 0; bv.update(); for (auto it = bv.vert_iter(); it.is_valid(); it++) { EXPECT_EQ(iter_count, LineNumber(it.iterator())); iter_count++; } EXPECT_EQ(8, iter_count); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); } TEST(BufferViewTest, iterates_through_screen_lines_with_wrapping) { std::shared_ptr b = buffer1(); std::shared_ptr iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(3, 10); int iter_count = 0; int expected_row_offsets[] = {0, 1, 2, 4, 5, 6, 7}; bv.update(); for (auto it = bv.vert_iter(); it.is_valid(); it++) { EXPECT_EQ(iter_count, LineNumber(it.iterator())); EXPECT_EQ(expected_row_offsets[iter_count], it.row_offset()); iter_count++; } EXPECT_EQ(7, iter_count); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); } TEST(BufferViewTest, fills_view_with_last_lines_of_buffer_when_cursor_warps_to_last_line) { std::shared_ptr b = buffer1(); std::shared_ptr iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); while (iterator->go_next_line()) { } bv.resize(40, 3); int iter_count = 0; int expected_row_offsets[] = {0, 1, 2}; bv.update(); for (auto it = bv.vert_iter(); it.is_valid(); it++) { EXPECT_EQ(10 + iter_count, LineNumber(it.iterator())); EXPECT_EQ(expected_row_offsets[iter_count], it.row_offset()); iter_count++; } EXPECT_EQ(3, iter_count); EXPECT_EQ(2, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); } TEST(BufferViewTest, adjusts_cursor_row_as_cursor_moves) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(5, 5); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 1 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 2 bv.update(); EXPECT_EQ(2, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 3 bv.update(); EXPECT_EQ(3, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 4 bv.update(); EXPECT_EQ(4, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 5 bv.update(); EXPECT_EQ(4, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 6 bv.update(); EXPECT_EQ(4, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 7 bv.update(); EXPECT_EQ(4, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 6 bv.update(); EXPECT_EQ(2, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 5 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 4 bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 3 bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 2 bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 1 bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 0 bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); } TEST(BufferViewTest, adjusts_cursor_row_as_cursor_moves_with_scroll_offset_1) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(5, 5); bv.set_scroll_offset(1); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 1 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 2 bv.update(); EXPECT_EQ(2, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 3 bv.update(); EXPECT_EQ(3, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 4 bv.update(); EXPECT_EQ(3, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 5 bv.update(); EXPECT_EQ(3, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 6 bv.update(); EXPECT_EQ(3, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::DOWN, false); // to 7 bv.update(); EXPECT_EQ(3, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 6 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 5 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 4 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 3 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 2 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 1 bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::UP, false); // to 0 bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); } TEST(BufferViewTest, moves_view_down_while_traversing_a_wrapped_line_right) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(4, 10); for (int i = 0; i < 9; i++) { EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, false)); bv.update(); } EXPECT_EQ(9, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); EXPECT_EQ(C('9'), **iterator); for (int i = 0; i < 3; i++) { EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, false)); bv.update(); EXPECT_EQ(9, bv.cursor_screen_row()); EXPECT_EQ(1 + i, bv.cursor_screen_column()); EXPECT_EQ(1 + i, bv.cursor_virtual_column()); } for (int i = 0; i < 4; i++) { EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, false)); bv.update(); EXPECT_EQ(9, bv.cursor_screen_row()); EXPECT_EQ(i, bv.cursor_screen_column()); EXPECT_EQ(4 + i, bv.cursor_virtual_column()); } for (int i = 0; i < 4; i++) { EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::RIGHT, false)); bv.update(); EXPECT_EQ(9, bv.cursor_screen_row()); EXPECT_EQ(i, bv.cursor_screen_column()); EXPECT_EQ(8 + i, bv.cursor_virtual_column()); } EXPECT_EQ(C('k'), **iterator); } TEST(BufferViewTest, moves_view_down_while_jumping_to_the_end_of_a_wrapped_line) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(4, 10); for (int i = 0; i < 9; i++) { bv.cursor_move(BufferView::CursorMovement::DOWN, false); bv.update(); } EXPECT_EQ(9, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); EXPECT_EQ(C('9'), **iterator); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::EOL, false)); bv.update(); EXPECT_EQ(9, bv.cursor_screen_row()); EXPECT_EQ(3, bv.cursor_screen_column()); EXPECT_EQ(15, bv.cursor_virtual_column()); EXPECT_EQ(C('o'), **iterator); } TEST(BufferViewTest, moves_view_up_while_traversing_a_wrapped_line_left) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(2, 3); for (int i = 0; i < 4; i++) { EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, false)); bv.update(); } EXPECT_EQ(2, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); for (int i = 0; i < 4; i++) { iterator->go_back(); } /* Cursor at end of line 2 */ bv.set_cursor_screen_row(0); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(1, bv.cursor_screen_column()); EXPECT_EQ(3, bv.cursor_virtual_column()); EXPECT_EQ(C('c'), **iterator); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LEFT, false)); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(2, bv.cursor_virtual_column()); EXPECT_EQ(C('b'), **iterator); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LEFT, false)); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(1, bv.cursor_screen_column()); EXPECT_EQ(1, bv.cursor_virtual_column()); EXPECT_EQ(C('a'), **iterator); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LEFT, false)); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); EXPECT_EQ(C('2'), **iterator); EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::LEFT, false)); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); EXPECT_EQ(C('2'), **iterator); } TEST(BufferViewTest, moves_view_up_while_jumping_to_start_of_a_wrapped_line) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(2, 3); for (int i = 0; i < 4; i++) { bv.cursor_move(BufferView::CursorMovement::DOWN, false); bv.update(); } EXPECT_EQ(2, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); for (int i = 0; i < 4; i++) { iterator->go_back(); } /* Cursor at end of line 2 */ bv.set_cursor_screen_row(0); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(1, bv.cursor_screen_column()); EXPECT_EQ(3, bv.cursor_virtual_column()); EXPECT_EQ(C('c'), **iterator); bv.cursor_move(BufferView::CursorMovement::SOL, false); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); EXPECT_EQ(C('2'), **iterator); } TEST(BufferViewTest, warps_view_when_jumping_to_beginning_and_end_of_buffer) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(4, 4); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LAST_LINE, false)); bv.update(); EXPECT_EQ(12, LineNumber(iterator)); EXPECT_EQ(3, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::LAST_LINE, false)); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::FIRST_LINE, false)); bv.update(); EXPECT_EQ(0, LineNumber(iterator)); EXPECT_EQ(0, bv.cursor_screen_row()); EXPECT_EQ(0, bv.cursor_screen_column()); EXPECT_EQ(0, bv.cursor_virtual_column()); EXPECT_FALSE(bv.cursor_move(BufferView::CursorMovement::FIRST_LINE, false)); } TEST(BufferViewTest, sets_target_column_to_eol_when_jumping_to_SOL_and_EOL) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(4, 4); bv.cursor_move(BufferView::CursorMovement::EOL, false); bv.update(); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, false)); bv.update(); EXPECT_EQ(1, bv.cursor_screen_row()); EXPECT_EQ(C('b'), **iterator); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::UP, false)); bv.update(); EXPECT_EQ(0, bv.cursor_screen_row()); bv.cursor_move(BufferView::CursorMovement::SOL, false); bv.update(); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, false)); EXPECT_EQ(C('1'), **iterator); EXPECT_EQ(1, bv.cursor_screen_row()); } TEST(BufferViewTest, sets_target_column_when_traversing_left_and_right) { auto b = buffer1(); auto iterator = b->add_iterator(); BufferView bv(b, iterator, Cwd); bv.resize(4, 4); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, false)); bv.update(); EXPECT_EQ(1, LineNumber(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(1, LineNumber(iterator)); EXPECT_EQ(C('b'), **iterator); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::DOWN, false)); bv.update(); EXPECT_EQ(2, LineNumber(iterator)); EXPECT_EQ(C('b'), **iterator); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::LEFT, false)); bv.update(); EXPECT_EQ(2, LineNumber(iterator)); EXPECT_EQ(C('a'), **iterator); EXPECT_TRUE(bv.cursor_move(BufferView::CursorMovement::UP, false)); bv.update(); EXPECT_EQ(1, LineNumber(iterator)); EXPECT_EQ(C('a'), **iterator); }