diff --git a/src/controller.cpp b/src/controller.cpp index bd7a7e0..da36b3e 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -53,29 +53,32 @@ void Controller::set_mode(Mode m) { } } -void Controller::create_view(const std::string& file_name, unsigned long lineno) { - if (file_name.empty()) { +void Controller::create_view(const Flags& flags) { + if (flags.file.empty()) { models.emplace_back(term_size.vertical - 2, "NO NAME"); view.add_model(&models.at(models.size() - 1)); } else { auto logger = spdlog::get("basic_logger"); - if (logger != nullptr) { logger->info("Creating view from file: " + file_name); } + if (logger != nullptr) { logger->info("Creating view from file: " + flags.file); } - opt_lines_t file_chars = open_file(file_name); + opt_lines_t file_chars = open_file(flags.file); if (file_chars.has_value()) { - models.emplace_back(file_chars.value(), file_name); + models.emplace_back(file_chars.value(), flags.file); view.add_model(&models.at(models.size() - 1)); - if (lineno) { - lineno = std::min(lineno, models.at(models.size() - 1).buf.size()); - view.cursor_down(uint32_t(std::min(lineno - 1, file_chars.value().size()))); + if (flags.lineno) { + std::size_t line_num = + std::min(flags.lineno, models.at(models.size() - 1).buf.size()); + view.cursor_down(uint32_t(std::min(line_num - 1, file_chars.value().size()))); view.center_current_line(); } } else { - models.emplace_back(term_size.vertical - 2, (file_name.empty() ? "" : file_name)); + models.emplace_back(term_size.vertical - 2, (flags.file.empty() ? "" : flags.file)); view.add_model(&models.at(models.size() - 1)); } + + if (flags.readonly) { view.get_active_model()->readonly = true; } } } diff --git a/src/controller.h b/src/controller.h index a2ab68a..7eef473 100644 --- a/src/controller.h +++ b/src/controller.h @@ -5,6 +5,7 @@ #include +#include "flags.h" #include "model.h" #include "text_io.h" #include "view.h" @@ -32,7 +33,7 @@ struct Controller { Controller(); void set_mode(Mode m); [[nodiscard]] const std::string get_mode() const; - void create_view(const std::string&, const unsigned long); + void create_view(const Flags&); void start_action_engine(); bool enter_command_mode(); bool parse_command(); diff --git a/src/flags.h b/src/flags.h new file mode 100644 index 0000000..6e27472 --- /dev/null +++ b/src/flags.h @@ -0,0 +1,19 @@ +#ifndef FLAGS_H +#define FLAGS_H + +#include + +struct Flags { + std::string file = ""; + std::size_t lineno = 0; + bool print_version = false; + bool readonly = false; + + Flags() {} + Flags(const std::string& fname) : file(fname) {} + Flags(const std::string& fname, const std::size_t& line) : file(fname), lineno(line) {} + Flags(const std::string& fname, bool ro) : file(fname), readonly(ro) {} + Flags(bool version) : print_version(version) {} +}; + +#endif // FLAGS_H diff --git a/src/main.cpp b/src/main.cpp index 5aed8c7..3cbd150 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include #include "controller.h" +#include "flags.h" #include "text_io.h" #include "version.h" @@ -18,25 +19,23 @@ int main(int argc, char* argv[]) { CLI::App app {"Iris text editor"}; + Flags flags; - std::string file = ""; - unsigned int lineno = 0; - bool print_version = false; - - app.add_option("file", file, "File to open"); - app.add_option("-l,--line", lineno, "Set line number to start on"); - app.add_flag("-v,--version", print_version, "Print version"); + app.add_option("file", flags.file, "File to open"); + app.add_option("-l,--line", flags.lineno, "Set line number to start on"); + app.add_flag("-v,--version", flags.print_version, "Print version"); + app.add_flag("-r,--readonly", flags.readonly, "Force file to be open in readonly mode"); try { app.parse(argc, argv); } catch (const CLI::ParseError& e) { return app.exit(e); } - if (print_version) { + if (flags.print_version) { std::println("{}", version()); return 0; } - auto filename_error = check_filename(file); + auto filename_error = check_filename(flags.file); if (!(filename_error.empty())) { std::println("{}", filename_error); return 0; @@ -54,7 +53,7 @@ int main(int argc, char* argv[]) { try { std::println("Setup..."); Controller c; - c.create_view(file, lineno); + c.create_view(flags); c.start_action_engine(); } catch (const std::exception& e) { rawterm::exit_alt_screen(); diff --git a/tests/controller_test.cpp b/tests/controller_test.cpp index 8f75394..1f4a6a3 100644 --- a/tests/controller_test.cpp +++ b/tests/controller_test.cpp @@ -3,6 +3,8 @@ #include #include +#include "flags.h" + TEST_CASE("Construction", "[controller]") { Controller c; REQUIRE(c.models.capacity() == 8); @@ -29,7 +31,8 @@ TEST_CASE("get_mode", "[controller]") { TEST_CASE("create_view", "[controller]") { SECTION("View with file") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); REQUIRE(c.models.size() == 1); REQUIRE(*c.models.at(0).buf.at(0).begin() == 'T'); @@ -37,7 +40,8 @@ TEST_CASE("create_view", "[controller]") { SECTION("Empty view") { Controller c; - c.create_view("", 0); + Flags f = {}; + c.create_view(f); REQUIRE(c.models.size() == 1); REQUIRE(c.models.at(0).buf.size() == 1); @@ -45,16 +49,28 @@ TEST_CASE("create_view", "[controller]") { SECTION("View with scroll offset") { Controller c; - c.create_view("tests/fixture/lorem_ipsum.txt", 20); + Flags f = {"tests/fixture/lorem_ipsum.txt", std::size_t(20)}; + c.create_view(f); + + REQUIRE(c.models.size() == 1); + REQUIRE(*c.models.at(0).buf.at(0).begin() == 'L'); + } + + SECTION("View set to read only") { + Controller c; + Flags f = {"tests/fixture/lorem_ipsum.txt", true}; + c.create_view(f); REQUIRE(c.models.size() == 1); REQUIRE(*c.models.at(0).buf.at(0).begin() == 'L'); + REQUIRE(c.models.at(0).readonly == true); } } TEST_CASE("is_readonly_model", "[controller]") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); REQUIRE(!c.is_readonly_model()); c.view.get_active_model()->readonly = true; REQUIRE(c.is_readonly_model()); @@ -63,7 +79,8 @@ TEST_CASE("is_readonly_model", "[controller]") { TEST_CASE("quit_app", "[controller]") { SECTION("Only one tab open") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); REQUIRE(!c.quit_app(false)); REQUIRE(c.quit_flag); @@ -71,7 +88,8 @@ TEST_CASE("quit_app", "[controller]") { SECTION("Only one tab and modified buffer") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.models.at(0).insert('!'); REQUIRE(!c.quit_app(false)); @@ -80,7 +98,8 @@ TEST_CASE("quit_app", "[controller]") { SECTION("Multiple tabs open") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.view.tab_new(); REQUIRE(c.quit_app(false)); @@ -89,7 +108,8 @@ TEST_CASE("quit_app", "[controller]") { SECTION("Two tab and this buffer is modified") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.view.tab_new(); c.models.at(1).insert('!'); @@ -99,7 +119,8 @@ TEST_CASE("quit_app", "[controller]") { SECTION("Two tab and other buffer is modified") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.view.tab_new(); c.models.at(0).insert('!'); @@ -111,14 +132,16 @@ TEST_CASE("quit_app", "[controller]") { TEST_CASE("check_for_saved_file", "[controller]") { SECTION("Unsaved") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.models.at(0).insert('!'); REQUIRE(!c.check_for_saved_file(false)); } SECTION("Saved") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); REQUIRE(c.check_for_saved_file(false)); } @@ -131,7 +154,8 @@ TEST_CASE("check_for_saved_file", "[controller]") { TEST_CASE("write_all", "[controller]") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.view.tab_new(); c.models.at(0).insert('!'); @@ -151,7 +175,8 @@ TEST_CASE("write_all", "[controller]") { TEST_CASE("quit_all", "[controller]") { SECTION("No unsaved models") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.view.tab_new(); c.add_model("tests/fixture/lorem_ipsum.txt"); @@ -163,7 +188,8 @@ TEST_CASE("quit_all", "[controller]") { SECTION("Unsaved models") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.view.tab_new(); c.add_model("tests/fixture/lorem_ipsum.txt"); @@ -180,7 +206,8 @@ TEST_CASE("quit_all", "[controller]") { TEST_CASE("display_all_buffers", "[controller]") { Controller c; - c.create_view("tests/fixture/test_file_1.txt", 0); + Flags f = {std::string("tests/fixture/test_file_1.txt")}; + c.create_view(f); c.view.tab_new(); c.add_model("tests/fixture/lorem_ipsum.txt"); diff --git a/tests/integration/ui_test.py b/tests/integration/ui_test.py index 3624930..3536344 100644 --- a/tests/integration/ui_test.py +++ b/tests/integration/ui_test.py @@ -395,3 +395,9 @@ def test_horizontal_scroll_and_change_tab(r: TmuxRunner): r.type_str("tn") assert r.statusbar_parts()[-1] == "1:91" assert r.cursor_pos() == (1, 77) + + +def test_readonly_cli_flag(): + with TmuxRunner("bash", "--norc") as r: + r.press_and_enter("./build/src/iris -r tests/fixture/test_file_1.txt") + assert "[RO]" in r.await_statusbar_parts() diff --git a/tests/view_test.cpp b/tests/view_test.cpp index e89d6c4..6db222a 100644 --- a/tests/view_test.cpp +++ b/tests/view_test.cpp @@ -592,7 +592,8 @@ TEST_CASE("change_model_cursor", "[view]") { TEST_CASE("set_buffer", "[view]") { Controller c; - c.create_view("tests/fixture/lorem_ipsum.txt", 0); + Flags f = {std::string("tests/fixture/lorem_ipsum.txt")}; + c.create_view(f); c.add_model("tests/fixture/test_file_1.txt"); c.view.cursor_down(5); @@ -608,7 +609,8 @@ TEST_CASE("set_buffer", "[view]") { TEST_CASE("render_cursor_coords", "[view]") { Controller c; - c.create_view("tests/fixture/very_long_line.txt", 0); + Flags f = {std::string("tests/fixture/very_long_line.txt")}; + c.create_view(f); c.view.cursor_end_of_line(); const std::string statusbar = c.view.render_cursor_coords();