From 80430e5f10277033643f5bee9f656d64a57500c6 Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Mon, 5 Jan 2026 11:39:23 +0000 Subject: [PATCH 1/5] add readonly flag --- src/controller.cpp | 6 +++++- src/controller.h | 2 +- src/main.cpp | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/controller.cpp b/src/controller.cpp index b35c789..8958a5a 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -51,7 +51,7 @@ void Controller::set_mode(Mode m) { } } -void Controller::create_view(const std::string& file_name, unsigned long lineno) { +void Controller::create_view(const std::string& file_name, std::size_t lineno, const bool ro_mode) { if (file_name.empty()) { models.emplace_back(term_size.vertical - 2, "NO NAME"); view.add_model(&models.at(models.size() - 1)); @@ -76,6 +76,10 @@ void Controller::create_view(const std::string& file_name, unsigned long lineno) models.emplace_back(term_size.vertical - 2, (file_name.empty() ? "" : file_name)); view.add_model(&models.at(models.size() - 1)); } + + if (ro_mode) { + view.get_active_model()->readonly = true; + } } } diff --git a/src/controller.h b/src/controller.h index a2ab68a..bda7711 100644 --- a/src/controller.h +++ b/src/controller.h @@ -32,7 +32,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 std::string&, std::size_t, const bool); void start_action_engine(); bool enter_command_mode(); bool parse_command(); diff --git a/src/main.cpp b/src/main.cpp index ecd172d..721009a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,10 +24,12 @@ int main(int argc, char* argv[]) { std::string file = ""; unsigned int lineno = 0; bool print_version = false; + bool readonly = 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_flag("-r,--readonly", readonly, "Force file to be open in readonly mode"); try { app.parse(argc, argv); @@ -60,7 +62,7 @@ int main(int argc, char* argv[]) { try { std::println("Setup..."); Controller c; - c.create_view(file, lineno); + c.create_view(file, lineno, readonly); c.start_action_engine(); } catch (const std::exception& e) { rawterm::exit_alt_screen(); From 9194f8d3c1d4243fd6e45bc2c410e9c6e378e5d0 Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Mon, 5 Jan 2026 11:53:22 +0000 Subject: [PATCH 2/5] refactor out flags --- src/controller.cpp | 21 +++++++++++---------- src/controller.h | 3 ++- src/flags.h | 13 +++++++++++++ src/main.cpp | 23 ++++++++++------------- 4 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 src/flags.h diff --git a/src/controller.cpp b/src/controller.cpp index 8958a5a..4c4a6e1 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -51,33 +51,34 @@ void Controller::set_mode(Mode m) { } } -void Controller::create_view(const std::string& file_name, std::size_t lineno, const bool ro_mode) { - 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); + 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 (ro_mode) { + if (flags.readonly) { view.get_active_model()->readonly = true; } } diff --git a/src/controller.h b/src/controller.h index bda7711..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&, std::size_t, const bool); + 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..8fb4677 --- /dev/null +++ b/src/flags.h @@ -0,0 +1,13 @@ +#ifndef FLAGS_H +#define FLAGS_H + +#include + +struct Flags { + std::string file = ""; + std::size_t lineno = 0; + bool print_version = false; + bool readonly = false; +}; + +#endif // FLAGS_H diff --git a/src/main.cpp b/src/main.cpp index 721009a..21af7f0 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" @@ -15,21 +16,17 @@ // TODO: Save file with given name `;w foo.txt` // TODO: detect if `;e` is opening an already-open file and switch to that buf instead // NOTE: Maybe post a message in the command bar too - // TODO: A way of detecting if the file is already open in another iris // instance + 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; - bool readonly = 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_flag("-r,--readonly", readonly, "Force file to be open in readonly mode"); + 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); @@ -37,12 +34,12 @@ int main(int argc, char* argv[]) { 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; @@ -62,7 +59,7 @@ int main(int argc, char* argv[]) { try { std::println("Setup..."); Controller c; - c.create_view(file, lineno, readonly); + c.create_view(flags); c.start_action_engine(); } catch (const std::exception& e) { rawterm::exit_alt_screen(); From 96ec267d3e95b079400a0465124002a10a68aff6 Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Mon, 5 Jan 2026 12:10:15 +0000 Subject: [PATCH 3/5] Add flags constructors and update unit tests --- src/flags.h | 6 +++++ tests/controller_test.cpp | 57 ++++++++++++++++++++++++++++----------- tests/view_test.cpp | 2 +- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/flags.h b/src/flags.h index 8fb4677..6e27472 100644 --- a/src/flags.h +++ b/src/flags.h @@ -8,6 +8,12 @@ struct Flags { 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/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/view_test.cpp b/tests/view_test.cpp index f08c5d1..8c761cc 100644 --- a/tests/view_test.cpp +++ b/tests/view_test.cpp @@ -594,7 +594,7 @@ TEST_CASE("change_model_cursor", "[view]") { TEST_CASE("set_buffer", "[view]") { Controller c; - c.create_view("tests/fixture/lorem_ipsum.txt", 0); + c.create_view(std::string("tests/fixture/lorem_ipsum.txt")); c.add_model("tests/fixture/test_file_1.txt"); c.view.cursor_down(5); From cabd512de09f24c91256e9ce0298824d10851f0d Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Mon, 5 Jan 2026 12:18:03 +0000 Subject: [PATCH 4/5] integration test --- tests/integration/ui_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/integration/ui_test.py b/tests/integration/ui_test.py index b3a7ce5..ec0e7da 100644 --- a/tests/integration/ui_test.py +++ b/tests/integration/ui_test.py @@ -394,3 +394,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() From a69f7c3740c42c7a53df901b9464075002d0457f Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Thu, 5 Feb 2026 16:11:02 +0000 Subject: [PATCH 5/5] Handle failing pre-commit and missed Flags in tests --- src/controller.cpp | 6 ++---- tests/view_test.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controller.cpp b/src/controller.cpp index 27f52c6..da36b3e 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -59,7 +59,7 @@ void Controller::create_view(const Flags& flags) { 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(flags.file); @@ -78,9 +78,7 @@ void Controller::create_view(const Flags& flags) { view.add_model(&models.at(models.size() - 1)); } - if (flags.readonly) { - view.get_active_model()->readonly = true; - } + if (flags.readonly) { view.get_active_model()->readonly = true; } } } diff --git a/tests/view_test.cpp b/tests/view_test.cpp index 50f89fd..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(std::string("tests/fixture/lorem_ipsum.txt")); + 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();