diff --git a/src/action.h b/src/action.h index 2321c77..73f4f9e 100644 --- a/src/action.h +++ b/src/action.h @@ -81,16 +81,15 @@ constexpr std::optional parse_action(View* v, const Action& action) if (active->current_char_in_line == 1 && active->current_line > 1) { // At the start of the line, move cursor up int prev_line_len = active->buf.line(active->get_abs_pos() - 1).size(); - active->buf.erase(active->buf.begin() + active->get_abs_pos() - 2, 2); + active->buf.erase(2); v->cursor_up(); for (int i = 0; i <= prev_line_len - 1; i++) { v->cursor_right(); } - active->line_count--; v->render_screen(); } else { // Move cursor backwards - active->buf.erase(active->buf.begin() + active->get_abs_pos() - 1); + active->buf.pop_back(); v->render_line(); v->cursor_left(); } @@ -100,12 +99,11 @@ constexpr std::optional parse_action(View* v, const Action& action) case ActionType::Newline: { log("Action called: Newline"); Model* active = v->get_active_model(); - active->buf.insert(active->buf.begin() + active->get_abs_pos(), "\r\n"); + active->buf.insert("\r\n"); v->cursor_down(); while (active->current_char_in_line > 1) { v->cursor_left(); } - active->line_count++; v->render_screen(); return {}; } diff --git a/src/controller.cpp b/src/controller.cpp index 3b42b85..68d14cb 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -5,7 +5,7 @@ #include "action.h" #include "constants.h" #include "file_io.h" -#include "gapvector.h" +#include "gapbuffer.h" #include "logger.h" Controller::Controller() : term_size(rawterm::get_term_size()), view(View(this, term_size)) { @@ -45,7 +45,7 @@ void Controller::create_view(const std::string& file_name) { models.emplace_back(); } else { log("Creating view from file: " + file_name); - std::optional> file_chars = open_file(file_name); + std::optional file_chars = open_file(file_name); if (file_chars.has_value()) { models.emplace_back(file_chars.value(), file_name); diff --git a/src/file_io.cpp b/src/file_io.cpp index a4c19d0..6250bf5 100644 --- a/src/file_io.cpp +++ b/src/file_io.cpp @@ -4,8 +4,8 @@ #include #include -[[nodiscard]] std::optional> open_file(const std::string& file) { - auto ret = Gapvector(1024); +[[nodiscard]] std::optional open_file(const std::string& file) { + auto ret = Gapbuffer(1024); char ch; std::ifstream ifs(file); @@ -26,34 +26,38 @@ ret.pop_back(); } + for (unsigned int i = 0; i <= ret.size(); i++) { + ret.retreat(); + } + return ret; } -[[nodiscard]] std::optional shell_exec(const std::string& cmd, bool output) { - namespace fs = std::filesystem; - std::string tmp_dir = fs::temp_directory_path(); - int retcode = std::system( - (cmd + ">" + tmp_dir + "/iris_cmd_out.txt 2> " + tmp_dir + "/iris_cmd_err.txt").c_str()); - - if (output) { - auto stdout_contents = open_file(tmp_dir + "/iris_cmd_out.txt"); - - if (retcode) { - auto stderr_contents = open_file(tmp_dir + "/iris_cmd_err.txt"); - if (stderr_contents.has_value()) { - return Response {"", stderr_contents.value().to_str(), retcode}; - } - return Response {"", "No STDERR contents found", retcode}; - } - - return Response {stdout_contents.value().to_str(), "", 0}; - - } else { - return {}; - } -} +// [[nodiscard]] std::optional shell_exec(const std::string& cmd, bool output) { +// namespace fs = std::filesystem; +// std::string tmp_dir = fs::temp_directory_path(); +// int retcode = std::system( +// (cmd + ">" + tmp_dir + "/iris_cmd_out.txt 2> " + tmp_dir + "/iris_cmd_err.txt").c_str()); +// +// if (output) { +// auto stdout_contents = open_file(tmp_dir + "/iris_cmd_out.txt"); +// +// if (retcode) { +// auto stderr_contents = open_file(tmp_dir + "/iris_cmd_err.txt"); +// if (stderr_contents.has_value()) { +// return Response {"", stderr_contents.value().to_str(), retcode}; +// } +// return Response {"", "No STDERR contents found", retcode}; +// } +// +// return Response {stdout_contents.value().to_str(), "", 0}; +// +// } else { +// return {}; +// } +// } -[[nodiscard]] std::size_t write_to_file(const std::string& file, Gapvector<> chars) { +[[nodiscard]] std::size_t write_to_file(const std::string& file, Gapbuffer chars) { if (file == "NO FILE") { return -1; } diff --git a/src/file_io.h b/src/file_io.h index ad2e372..ada6c68 100644 --- a/src/file_io.h +++ b/src/file_io.h @@ -4,7 +4,7 @@ #include #include -#include "gapvector.h" +#include "gapbuffer.h" struct Response { std::string stdout; @@ -16,8 +16,8 @@ struct Response { } }; -[[nodiscard]] std::optional> open_file(const std::string&); -[[nodiscard]] std::optional shell_exec(const std::string&, bool); -[[nodiscard]] std::size_t write_to_file(const std::string&, Gapvector<>); +[[nodiscard]] std::optional open_file(const std::string&); +// [[nodiscard]] std::optional shell_exec(const std::string&, bool); +[[nodiscard]] std::size_t write_to_file(const std::string&, Gapbuffer); #endif // FILE_IO_H diff --git a/src/main.cpp b/src/main.cpp index a1e8f69..b3add47 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include "controller.h" @@ -36,15 +35,19 @@ int main(int argc, char* argv[]) { rawterm::enable_raw_mode(); rawterm::enable_signals(); - CPPTRACE_TRY { + // TODO: When we upgrade to c++23, use std::stacktrace here + // CPPTRACE_TRY { + try { Controller c; c.create_view(file); c.start_action_engine(); - } - CPPTRACE_CATCH(const std::exception& e) { + // } CPPTRACE_CATCH(const std::exception& e) { + } catch (const std::exception& e) { rawterm::exit_alt_screen(); - log(Level::ERROR, cpptrace::demangle(typeid(e).name()) + " " + e.what()); - cpptrace::from_current_exception().print(); + log(Level::ERROR, e.what()); + throw e; + // log(Level::ERROR, cpptrace::demangle(typeid(e).name()) + " " + e.what()); + // cpptrace::from_current_exception().print(); } rawterm::exit_alt_screen(); diff --git a/src/model.cpp b/src/model.cpp index 2d96edb..e42ca69 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -4,15 +4,15 @@ #include "file_io.h" -Model::Model() : buf(Gapvector()), line_count(0) {}; +Model::Model() : buf(Gapbuffer()) {}; // TODO: readonly and modified -Model::Model(Gapvector<> file_chars, const std::string& filename) - : buf(file_chars), file_name(filename), line_count(std::count(buf.begin(), buf.end(), '\n')) {}; +Model::Model(Gapbuffer file_chars, const std::string& filename) + : buf(file_chars), file_name(filename) {}; // 0-based position of the current character, used for insertion/deletion [[nodiscard]] int Model::get_abs_pos() const { - int char_pos = buf.find_ith_char('\n', current_line - 1); + int char_pos = buf.find('\n', current_line - 1); char_pos += current_char_in_line; if (current_line == 1) { char_pos--; @@ -21,21 +21,32 @@ Model::Model(Gapvector<> file_chars, const std::string& filename) } [[nodiscard]] char Model::get_current_char() const { - return buf.at(get_abs_pos()); + return buf.at(buf.pos()); } [[nodiscard]] char Model::get_next_char() const { - return buf.at(get_abs_pos() + 1); + return buf.at(buf.pos() + 1); } [[nodiscard]] const std::string Model::get_current_line() const { - // The -1 is because we want the abs pos of the last char, not - // the cursor - return buf.line(std::max(get_abs_pos() - 1, 0)); + return buf.line(buf.pos()); } void Model::insert_char(char c) { - buf.insert(buf.begin() + get_abs_pos(), c); + buf.push_back(c); + current_char_in_line++; +} + +void Model::line_up() { + for (unsigned int i = 0; i < buf.line(buf.pos()).size(); i++) { + buf.retreat(); + } +} + +void Model::line_down() { + for (unsigned int i = 0; i < buf.line(buf.pos()).size(); i++) { + buf.advance(); + } } // TODO: Display "saved x bytes" diff --git a/src/model.h b/src/model.h index 35bd03e..823382d 100644 --- a/src/model.h +++ b/src/model.h @@ -3,24 +3,25 @@ #include -#include "gapvector.h" +#include "gapbuffer.h" struct Model { - Gapvector<> buf; + Gapbuffer buf; std::string file_name = ""; - int line_count; - int current_line = 1; + unsigned int current_line = 1; int current_char_in_line = 1; bool readonly = false; bool modified = false; int vertical_file_offset = 0; Model(); - Model(Gapvector<>, const std::string&); + Model(Gapbuffer, const std::string&); [[nodiscard]] int get_abs_pos() const; [[nodiscard]] char get_current_char() const; [[nodiscard]] char get_next_char() const; [[nodiscard]] const std::string get_current_line() const; + void line_up(); + void line_down(); void insert_char(char); int save_file() const; }; diff --git a/src/view.cpp b/src/view.cpp index ad0188d..d92ed7e 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -10,8 +10,7 @@ #include "constants.h" #include "controller.h" -#include "gapvector.h" -#include "logger.h" +#include "gapbuffer.h" View::View(Controller* controller, const rawterm::Pos dims) : ctrlr_ptr(controller), view_size(dims), cur(rawterm::Cursor()) { @@ -29,7 +28,7 @@ void View::add_model(Model* m) { active_model = viewable_models.size(); if (LINE_NUMBERS) { - line_number_offset = std::to_string(m->line_count).size() + 1; + line_number_offset = std::to_string(m->buf.line_count()).size() + 1; } } @@ -49,7 +48,7 @@ void View::render_screen() { int remaining_rows = view_size.vertical - 2; rawterm::Pos starting_cur_pos = cur; std::string screen = ""; - int line_count = viewable_models.at(active_model - 1)->vertical_file_offset + 1; + int line_count = get_active_model()->vertical_file_offset + 1; const int max_line_width = view_size.horizontal - line_number_offset - 4; rawterm::clear_screen(); @@ -70,33 +69,35 @@ void View::render_screen() { if (get_active_model()->buf.size() > 0) { // Find starting point in gapvector - unsigned int gv_counter = - get_active_model()->buf.find_ith_char('\n', get_active_model()->vertical_file_offset); + int buf_playhead = + get_active_model()->buf.find('\n', get_active_model()->vertical_file_offset); - if (gv_counter != 0) { - gv_counter++; + if (buf_playhead != 0) { + buf_playhead++; } // To truncate lines int horizontal_counter = 0; while (remaining_rows) { - if (gv_counter == get_active_model()->buf.size()) { + if (buf_playhead == static_cast(get_active_model()->buf.size())) { screen += "\r\n"; break; } - char c = get_active_model()->buf.at(gv_counter); + char c = get_active_model()->buf.at(buf_playhead); screen += c; // Truncate line horizontal_counter++; if (horizontal_counter >= max_line_width) { screen += "\u00BB\r\n"; - Gapvector<>* buf_ptr = &get_active_model()->buf; - gv_counter += std::distance( - buf_ptr->begin() + gv_counter, - std::find(buf_ptr->begin() + gv_counter, buf_ptr->end(), '\n')); + Gapbuffer* buf_ptr = &get_active_model()->buf; + + // Skip characters being cut off + buf_playhead += std::distance( + buf_ptr->begin() + buf_playhead, + std::find(buf_ptr->begin() + buf_playhead, buf_ptr->end(), '\n')); remaining_rows--; line_count++; @@ -107,8 +108,8 @@ void View::render_screen() { std::format("{:>{}}\u2502", line_count, line_number_offset), COLOR_UI_BG); } - if (gv_counter < get_active_model()->buf.size()) { - gv_counter++; // Skip the newline + if (buf_playhead < static_cast(get_active_model()->buf.size())) { + buf_playhead++; // Skip the newline } continue; } @@ -124,7 +125,7 @@ void View::render_screen() { } } - gv_counter++; + buf_playhead++; } } else { // Display an empty buffer @@ -217,23 +218,25 @@ void View::render_line() { cur.move({cur.vertical, 1}); + std::string line = ""; if (LINE_NUMBERS) { - std::cout << rawterm::set_foreground( + line = rawterm::set_foreground( std::format("{:>{}}\u2502", get_active_model()->current_line, line_number_offset), COLOR_UI_BG); } - std::string line = ""; try { - line = get_active_model()->get_current_line(); + std::string line_chars = get_active_model()->get_current_line(); + line += line_chars.substr(0, horizontal_draw_space); } catch (const std::runtime_error&) { } - std::cout << line.substr(0, horizontal_draw_space); // Truncate if (line.size() > horizontal_draw_space) { - std::cout << "\u00BB"; + line += "\u00BB"; } + + std::cout << line; cur.move(cur_pos); } @@ -247,7 +250,8 @@ void View::cursor_left() { const int left_most_pos = (LINE_NUMBERS ? line_number_offset + 2 : 0); if (cur.horizontal > left_most_pos) { cur.move_left(); - viewable_models.at(active_model - 1)->current_char_in_line--; + get_active_model()->current_char_in_line--; + get_active_model()->buf.retreat(); draw_status_bar(); } } @@ -261,35 +265,36 @@ void View::cursor_up() { int vertical_offset = (open_files.size() > 1 ? 1 : 0); if (cur.vertical - 1 == vertical_offset) { // scroll view - viewable_models.at(active_model - 1)->vertical_file_offset--; + get_active_model()->vertical_file_offset--; render_screen(); } else { // move cursor cur.move_up(); } - viewable_models.at(active_model - 1)->current_line--; + get_active_model()->line_up(); + get_active_model()->current_line--; draw_status_bar(); } void View::cursor_down() { // Check if we're at the bottom of the file - if (viewable_models.at(active_model - 1)->current_line == - viewable_models.at(active_model - 1)->line_count) { + if (get_active_model()->current_line == get_active_model()->buf.line_count()) { return; } int vertical_offset = (open_files.size() > 1 ? 3 : 2); if (cur.vertical + 1 > view_size.vertical - vertical_offset) { // scroll view - viewable_models.at(active_model - 1)->vertical_file_offset++; + get_active_model()->vertical_file_offset++; render_screen(); } else { // move cursor cur.move_down(); } - viewable_models.at(active_model - 1)->current_line++; + get_active_model()->line_down(); + get_active_model()->current_line++; draw_status_bar(); } @@ -300,6 +305,7 @@ void View::cursor_right() { auto trigger = [this]() { cur.move_right(); + get_active_model()->buf.advance(); get_active_model()->current_char_in_line++; draw_status_bar(); }; diff --git a/tests/file_io_test.cpp b/tests/file_io_test.cpp index 0ca5173..314b5f4 100644 --- a/tests/file_io_test.cpp +++ b/tests/file_io_test.cpp @@ -5,56 +5,60 @@ #include -#include "gapvector.h" +#include "gapbuffer.h" TEST_CASE("open_file", "[FILESYSTEM]") { - Gapvector<> gv = Gapvector<>( + std::string contents = "This is some text\r\n" " here is a newline and tab\r\n" - "and another newline"); + "and another newline"; + + const auto buf = Gapbuffer(contents); auto actual = open_file("tests/fixture/test_file_1.txt"); REQUIRE(actual.has_value() == true); - REQUIRE(actual.value() == gv); + REQUIRE(actual.value() == buf); + REQUIRE(actual.value().pos() == 0); + REQUIRE(actual.value().to_str() == contents); } -TEST_CASE("shell_exec", "[FILESYSTEM]") { - SECTION("Standard accept") { - Response expected = {"hi", "", 0}; - auto out = shell_exec("echo -n 'hi'", true); - - REQUIRE(out.has_value()); - REQUIRE(out.value().stdout == expected.stdout); - REQUIRE(out.value().stderr == expected.stderr); - REQUIRE(out.value().retcode == expected.retcode); - } - - SECTION("No Response needed") { - auto out = shell_exec("echo 'hi'", false); - REQUIRE_FALSE(out.has_value()); - } - - SECTION("Executed command failed") { - auto out = shell_exec("mv", true); - Response r; - r.stderr = "mv: missing file operand\r\nTry 'mv --help' for more information."; - r.retcode = 256; - - REQUIRE(out.value().stdout == r.stdout); - REQUIRE(out.value().stderr == r.stderr); - REQUIRE(out.value().retcode == r.retcode); - } -} +// TEST_CASE("shell_exec", "[FILESYSTEM]") { +// SECTION("Standard accept") { +// Response expected = {"hi", "", 0}; +// auto out = shell_exec("echo -n 'hi'", true); +// +// REQUIRE(out.has_value()); +// REQUIRE(out.value().stdout == expected.stdout); +// REQUIRE(out.value().stderr == expected.stderr); +// REQUIRE(out.value().retcode == expected.retcode); +// } +// +// SECTION("No Response needed") { +// auto out = shell_exec("echo 'hi'", false); +// REQUIRE_FALSE(out.has_value()); +// } +// +// SECTION("Executed command failed") { +// auto out = shell_exec("mv", true); +// Response r; +// r.stderr = "mv: missing file operand\r\nTry 'mv --help' for more information."; +// r.retcode = 256; +// +// REQUIRE(out.value().stdout == r.stdout); +// REQUIRE(out.value().stderr == r.stderr); +// REQUIRE(out.value().retcode == r.retcode); +// } +// } TEST_CASE("write_to_file", "[FILESYSTEM]") { - Gapvector<> gv = Gapvector<>("Hello world"); - size_t bytes = write_to_file("save_test_file.txt", gv); - REQUIRE(bytes == gv.size()); + Gapbuffer buf = Gapbuffer("Hello world"); + size_t bytes = write_to_file("save_test_file.txt", buf); + REQUIRE(bytes == buf.size()); auto contents = open_file("save_test_file.txt"); REQUIRE(contents.has_value()); - REQUIRE(contents.value() == gv); + REQUIRE(contents.value() == buf); std::filesystem::remove("save_test_file.txt"); } diff --git a/tests/logger_test.cpp b/tests/logger_test.cpp index 203b784..e11b68c 100644 --- a/tests/logger_test.cpp +++ b/tests/logger_test.cpp @@ -35,8 +35,7 @@ TEST_CASE("log", "[LOGGER]") { auto file_contents = open_file(log_file); REQUIRE(file_contents.has_value() == true); - std::string contents_str = - std::string(file_contents.value().begin(), file_contents.value().end()); + std::string contents_str = file_contents.value().to_str(); REQUIRE_THAT(contents_str, Catch::Matchers::ContainsSubstring("INFO")); REQUIRE_THAT(contents_str, Catch::Matchers::ContainsSubstring("hello world")); @@ -48,8 +47,7 @@ TEST_CASE("log", "[LOGGER]") { auto file_contents = open_file(log_file); REQUIRE(file_contents.has_value() == true); - std::string contents_str = - std::string(file_contents.value().begin(), file_contents.value().end()); + std::string contents_str = file_contents.value().to_str(); REQUIRE_THAT(contents_str, Catch::Matchers::ContainsSubstring("WARNING")); REQUIRE_THAT(contents_str, Catch::Matchers::ContainsSubstring("hello world")); diff --git a/tests/model_test.cpp b/tests/model_test.cpp index 9e72f98..088e17d 100644 --- a/tests/model_test.cpp +++ b/tests/model_test.cpp @@ -6,11 +6,11 @@ #include #include "file_io.h" -#include "gapvector.h" +#include "gapbuffer.h" TEST_CASE("Constructor", "[MODEL]") { auto m = Model(); - REQUIRE(m.line_count == 0); + REQUIRE(m.buf.line_count() == 0); REQUIRE(m.buf.size() == 0); } @@ -19,9 +19,9 @@ TEST_CASE("Constructor_with_values", "[MODEL]") { "This is some text\n" " here is a newline and tab\n" "and another newline\n"; - auto m = Model(Gapvector<>(expected), "test_file.txt"); + auto m = Model(Gapbuffer(expected), "test_file.txt"); - REQUIRE(m.line_count == 3); + REQUIRE(m.buf.line_count() == 3); REQUIRE(m.buf.size() == expected.size()); REQUIRE(m.file_name == "test_file.txt"); } @@ -31,7 +31,7 @@ TEST_CASE("get_abs_pos", "[MODEL]") { "This is some text\r\n" " here is a newline and tab\r\n" "and another newline"; - auto m = Model(Gapvector<>(expected), "test_file.txt"); + auto m = Model(Gapbuffer(expected), "test_file.txt"); REQUIRE(m.get_abs_pos() == 0); m.current_line++; @@ -42,16 +42,23 @@ TEST_CASE("get_abs_pos", "[MODEL]") { TEST_CASE("get_current_char", "[MODEL]") { std::string expected = - "This is some text\n" - " here is a newline and tab\n" - "and another newline\n"; - auto m = Model(Gapvector<>(expected), "test_file.txt"); + "This is some text\r\n" + " here is a newline and tab\r\n" + "and another newline"; + auto m = Model(Gapbuffer(expected), "test_file.txt"); + + for (unsigned int i = 0; i <= m.buf.size(); i++) { + m.buf.retreat(); + } REQUIRE(m.get_current_char() == 'T'); - m.current_line++; - REQUIRE(m.get_current_char() == ' '); - m.current_char_in_line = 5; + REQUIRE(m.buf.pos() == 0); + + m.buf.advance(); + REQUIRE(m.buf.pos() == 1); REQUIRE(m.get_current_char() == 'h'); + m.line_down(); + REQUIRE(m.get_current_char() == ' '); } TEST_CASE("get_next_char", "[MODEL]") { @@ -59,29 +66,41 @@ TEST_CASE("get_next_char", "[MODEL]") { "This is some text\n" " here is a newline and tab\n" "and another newline\n"; - auto m = Model(Gapvector<>(expected), "test_file.txt"); + auto m = Model(Gapbuffer(expected), "test_file.txt"); + + for (unsigned int i = 0; i <= m.buf.size(); i++) { + m.buf.retreat(); + } REQUIRE(m.get_next_char() == 'h'); - m.current_line++; + m.buf.advance(); + REQUIRE(m.get_next_char() == 'i'); + m.line_down(); REQUIRE(m.get_next_char() == ' '); - m.current_char_in_line = 5; - REQUIRE(m.get_next_char() == 'e'); } TEST_CASE("get_current_line", "[MODEL]") { std::string expected = "#include \r\n\r\nint main"; - auto m = Model(Gapvector<>(expected), "test_file.txt"); + auto m = Model(Gapbuffer(expected), "test_file.txt"); + + for (unsigned int i = 0; i <= m.buf.size(); i++) { + m.buf.retreat(); + } SECTION("At the start of buffer") { REQUIRE(m.current_line == 1); REQUIRE(m.current_char_in_line == 1); - REQUIRE(m.get_current_line() == "#include "); + REQUIRE(m.get_current_line() == "#include \r\n"); } SECTION("At the end of the buffer") { m.current_line++; m.current_line++; m.current_char_in_line = 8; + for (unsigned int i = 0; i <= m.buf.size() - 1; i++) { + m.buf.advance(); + } + REQUIRE(m.current_line == 3); REQUIRE(m.current_char_in_line == 8); REQUIRE(m.get_current_line() == "int main"); @@ -93,19 +112,25 @@ TEST_CASE("insert_char", "[MODEL]") { "This is some text\n" " here is a newline and tab\n" "and another newline\n"; - auto m = Model(Gapvector<>(expected), "test_file.txt"); + auto m = Model(Gapbuffer(expected), "test_file.txt"); + for (unsigned int i = 0; i <= m.buf.size(); i++) { + m.buf.retreat(); + } + REQUIRE(m.buf.at(0) == 'T'); REQUIRE(m.get_current_char() == 'T'); + m.insert_char('a'); - REQUIRE(m.get_current_char() == 'a'); + REQUIRE(m.get_current_char() == 'T'); + REQUIRE(m.buf.at(0) == 'a'); } TEST_CASE("save_file", "[MODEL]") { - Gapvector<> expected = Gapvector("Hello world"); + Gapbuffer expected = Gapbuffer("Hello world"); auto m = Model(expected, "save_test_file.txt"); m.save_file(); - std::optional> contents = open_file("save_test_file.txt"); + std::optional contents = open_file("save_test_file.txt"); REQUIRE(contents.has_value()); REQUIRE(contents.value() == expected); diff --git a/tests/view_test.cpp b/tests/view_test.cpp index 315c252..4ef8d24 100644 --- a/tests/view_test.cpp +++ b/tests/view_test.cpp @@ -11,7 +11,7 @@ #include "constants.h" #include "controller.h" #include "file_io.h" -#include "gapvector.h" +#include "gapbuffer.h" #include "model.h" // TODO: Create a test util function that takes in a lambda to wrap the stdout @@ -34,7 +34,7 @@ TEST_CASE("add_model", "[VIEW]") { "and another newline\n"; auto v = View(&c, rawterm::Pos(24, 80)); - auto m = Model(Gapvector<>(raw), "test_file.txt"); + auto m = Model(Gapbuffer(raw), "test_file.txt"); v.add_model(&m); REQUIRE(v.active_model == 1); @@ -118,7 +118,7 @@ TEST_CASE("render_screen", "[VIEW]") { std::cout.rdbuf(prevcoutbuf); REQUIRE(text.size() == 3); - // REQUIRE(rawterm::raw_str(text.at(0)).size() == 83); // TODO: rawterm 55 + REQUIRE(rawterm::raw_str(text.at(0)).size() == 82); REQUIRE(text.at(0).substr(text.at(0).size() - 3, 2) == "\u00bb"); } } @@ -131,7 +131,7 @@ TEST_CASE("generate_tab_bar", "[VIEW]") { "and another newline\n"; auto v = View(&c, rawterm::Pos(24, 80)); - auto m = Model(Gapvector<>(raw), "test_file.txt"); + auto m = Model(Gapbuffer(raw), "test_file.txt"); v.add_model(&m); auto m2 = @@ -183,12 +183,11 @@ TEST_CASE("render_status_bar", "[VIEW]") { } TEST_CASE("render_line", "[VIEW]") { - Controller c; - auto v = View(&c, rawterm::Pos(24, 80)); - SECTION("Standard line rendering") { + Controller c; auto m = Model( open_file("tests/fixture/test_file_1.txt").value(), "tests/fixture/test_file_1.txt"); + auto v = View(&c, rawterm::Pos(24, 80)); v.add_model(&m); // capture stdout @@ -197,25 +196,28 @@ TEST_CASE("render_line", "[VIEW]") { v.render_line(); std::string text = buffer.str(); + text = rawterm::raw_str(text).substr( + 1, text.size()); // Trim the preceeding \r from clear_line // restore stdout std::cout.rdbuf(prevcoutbuf); - int expected_size = 17; + unsigned int expected_size = 17; if (LINE_NUMBERS) { - expected_size = 23; + expected_size = 24; } - // TODO: expected size is 22 if we only run the one test, but 23 when running all tests? - REQUIRE(rawterm::raw_size(text) == expected_size); - // REQUIRE(text == " 1\u2502This is some text"); // TODO: raw string - rawterm #55 + REQUIRE(text == " 1\u2502This is some text\r\n"); + REQUIRE(text.size() == expected_size); } SECTION("Truncated line") { + Controller c; auto m = Model(); + auto v = View(&c, rawterm::Pos(24, 80)); v.add_model(&m); - for (int i = 0; i < 80; i++) { + for (int i = 0; i <= 80; i++) { m.insert_char('_'); } @@ -225,13 +227,14 @@ TEST_CASE("render_line", "[VIEW]") { v.render_line(); std::string text = rawterm::raw_str(buffer.str()); + text = text.substr(1, text.size()); // Trim the preceeding \r from clear_line // restore stdout std::cout.rdbuf(prevcoutbuf); REQUIRE(text.substr(text.size() - 2, 2) == "\u00BB"); // size is +3 because "\u2502".size() == 3 is "\u00bb".size() == 2 - REQUIRE(text.size() == 84); + REQUIRE(text.size() == 83); } } @@ -325,7 +328,6 @@ TEST_CASE("cursor_up", "[VIEW]") { TEST_CASE("cursor_down", "[VIEW]") { Controller c; - auto v = View(&c, rawterm::Pos(24, 80)); std::stringstream buffer; std::streambuf* prevcoutbuf = std::cout.rdbuf(buffer.rdbuf()); @@ -333,6 +335,7 @@ TEST_CASE("cursor_down", "[VIEW]") { SECTION("Move cursor down") { auto m = Model( open_file("tests/fixture/test_file_2.txt").value(), "tests/fixture/test_file_2.txt"); + auto v = View(&c, rawterm::Pos(24, 80)); v.add_model(&m); v.cursor_down(); @@ -343,6 +346,7 @@ TEST_CASE("cursor_down", "[VIEW]") { SECTION("Move view down (scroll)") { auto m = Model( open_file("tests/fixture/test_file_2.txt").value(), "tests/fixture/test_file_2.txt"); + auto v = View(&c, rawterm::Pos(24, 80)); v.add_model(&m); for (int i = 1; i < 22; i++) { @@ -351,7 +355,12 @@ TEST_CASE("cursor_down", "[VIEW]") { REQUIRE(v.cur == rawterm::Pos(22, 1)); REQUIRE(m.current_line == 22); + std::stringstream buffer; + std::streambuf* prevcoutbuf = std::cout.rdbuf(buffer.rdbuf()); v.cursor_down(); // trigger scrolling + // restore stdout + std::cout.rdbuf(prevcoutbuf); + REQUIRE(v.cur == rawterm::Pos(22, 5)); REQUIRE(m.current_line == 23); @@ -362,35 +371,38 @@ TEST_CASE("cursor_down", "[VIEW]") { SECTION("Already at bottom-most row in file") { auto m = Model( - open_file("tests/fixture/test_file_2.txt").value(), "tests/fixture/test_file_2.txt"); + open_file("tests/fixture/test_file_1.txt").value(), "tests/fixture/test_file_1.txt"); + auto v = View(&c, rawterm::Pos(24, 80)); v.add_model(&m); - for (int i = 1; i < m.line_count; i++) { + for (std::size_t i = 1; i < m.buf.line_count(); i++) { v.cursor_down(); } - REQUIRE(v.cur == rawterm::Pos(22, 5)); - REQUIRE(m.current_line == m.line_count); + REQUIRE(v.cur == rawterm::Pos(3, 1)); + REQUIRE(m.current_line == m.buf.line_count()); v.cursor_down(); - REQUIRE(v.cur == rawterm::Pos(22, 5)); - REQUIRE(m.current_line == m.line_count); + REQUIRE(v.cur == rawterm::Pos(3, 1)); + REQUIRE(m.current_line == m.buf.line_count()); } SECTION("bottom row of file within view (no scrolling required)") { auto m = Model( open_file("tests/fixture/test_file_1.txt").value(), "tests/fixture/test_file_1.txt"); + auto v = View(&c, rawterm::Pos(24, 80)); v.add_model(&m); // Move to end of file v.cursor_down(); v.cursor_down(); - REQUIRE(v.cur == rawterm::Pos(2, 1)); - REQUIRE(m.current_line == 2); + REQUIRE(v.cur == rawterm::Pos(3, 1)); + REQUIRE(m.current_line == 3); // No cursor movement as we're already at end of file v.cursor_down(); - REQUIRE(v.cur == rawterm::Pos(2, 1)); - REQUIRE(m.current_line == 2); + INFO(m.buf.line_count()); + REQUIRE(v.cur == rawterm::Pos(3, 1)); + REQUIRE(m.current_line == 3); } std::cout.rdbuf(prevcoutbuf); @@ -414,9 +426,10 @@ TEST_CASE("cursor_right", "[VIEW]") { } SECTION("Already at right-most position in line") { - for (int i = 1; i <= 20; i++) { + for (int i = 1; i <= 100; i++) { v.cursor_right(); } + REQUIRE(v.cur == rawterm::Pos(1, 17)); REQUIRE(m.current_char_in_line == 17); }