From af07b0c02bca9e640b274e46502ae01c3bbf8ed1 Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Tue, 28 Apr 2026 09:17:58 +0000 Subject: [PATCH 1/4] untested initial logic --- src/controller.cpp | 7 +++++++ src/model.cpp | 10 ++++++++++ src/model.h | 1 + tests/model_test.cpp | 4 ++++ 4 files changed, 22 insertions(+) diff --git a/src/controller.cpp b/src/controller.cpp index bb7b175..8cce655 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -285,6 +285,7 @@ void Controller::start_action_engine() { redraw_all = redraw.value(); // Add mark + // TODO: Make sure this becomes an action instead } else if (k.value() == rawterm::Key('m')) { auto k2 = rawterm::wait_for_input(); if (k2.isCharInput()) { view.get_active_model()->add_mark(k2.code); } @@ -384,6 +385,12 @@ void Controller::start_action_engine() { if (quit_flag) { break; } parse_action(&view, Action {ActionType::ChangeMode, Mode::Read}); + // go to mark + // TODO: Make sure this becomes an action instead + } else if (k.value() == rawterm::Key('\'')) { + auto k2 = rawterm::wait_for_input(); + if (k2.isCharInput()) { redraw_all = view.get_active_model()->go_to_mark(k2.code); } + // Move to beginning/end of line } else if (k.value() == rawterm::Key('_')) { parse_action(&view, Action {ActionType::StartOfLine}); diff --git a/src/model.cpp b/src/model.cpp index 0921c08..2ef6829 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -564,3 +564,13 @@ void Model::add_mark(const char c) { return map.second.line_pos == idx; }) != marks.end(); } + +[[nodiscard]] bool Model::go_to_mark(const char c) { + try { + const Mark& m = marks.at(c); + current_line = m.line_pos; + current_char = m.char_pos; + + return true; + } catch (const std::out_of_range& e) { return false; } +} diff --git a/src/model.h b/src/model.h index f80433e..5f8fdff 100644 --- a/src/model.h +++ b/src/model.h @@ -88,6 +88,7 @@ struct Model { void dedent_curr_line(); void add_mark(const char); [[nodiscard]] bool is_marked(const std::size_t) const; + [[nodiscard]] bool go_to_mark(const char); }; #endif // MODEL_H diff --git a/tests/model_test.cpp b/tests/model_test.cpp index c9a4f93..69782bb 100644 --- a/tests/model_test.cpp +++ b/tests/model_test.cpp @@ -753,3 +753,7 @@ TEST_CASE("is_marked", "[model]") { REQUIRE(m.is_marked(0)); REQUIRE(!m.is_marked(1)); } + +TEST_CASE("go_to_mark", "[model]") { + REQUIRE(false); +} From d7242a0ab80c51a4e3dfa18de0b5c332e0a3833d Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Tue, 28 Apr 2026 09:41:47 +0000 Subject: [PATCH 2/4] untested integration test --- tests/integration/normal_mode_test.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/integration/normal_mode_test.py b/tests/integration/normal_mode_test.py index 2ec0fe3..27a101a 100644 --- a/tests/integration/normal_mode_test.py +++ b/tests/integration/normal_mode_test.py @@ -590,3 +590,20 @@ def test_m_key(r: TmuxRunner): target_line: str = r.color_screenshot()[3] assert "4\u2502" in target_line assert target_line.startswith("\x1b[38;2;95;250;104m") + + +@setup("tests/fixture/lorem_ipsum.txt") +def test_comma_key(r: TmuxRunner): + r.type_str("j" * 3) + r.type_str("ma") + pos = r.cursor_pos() + + r.type_str("j" * 3) + assert r.cursor_pos != pos + + r.type_str("'a") + assert r.cursor_pos == pos + + target_line: str = r.color_screenshot()[3] + assert "4\u2502" in target_line + assert target_line.startswith(r.SELECTED_LINE_ANSI) From d7ee4bfa8172611f699b00e30ad82dd6be6bee8f Mon Sep 17 00:00:00 2001 From: ttibsi Date: Tue, 28 Apr 2026 19:25:34 +0100 Subject: [PATCH 3/4] passing tests --- src/controller.cpp | 7 ++++++- tests/integration/normal_mode_test.py | 4 ++-- tests/model_test.cpp | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/controller.cpp b/src/controller.cpp index 8cce655..2d8badd 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -389,7 +389,12 @@ void Controller::start_action_engine() { // TODO: Make sure this becomes an action instead } else if (k.value() == rawterm::Key('\'')) { auto k2 = rawterm::wait_for_input(); - if (k2.isCharInput()) { redraw_all = view.get_active_model()->go_to_mark(k2.code); } + if (k2.isCharInput()) { + redraw_all = view.get_active_model()->go_to_mark(k2.code); + + // TODO: rename this method to something like "replace cursor" + view.change_model_cursor(); + } // Move to beginning/end of line } else if (k.value() == rawterm::Key('_')) { diff --git a/tests/integration/normal_mode_test.py b/tests/integration/normal_mode_test.py index 27a101a..7e7dd69 100644 --- a/tests/integration/normal_mode_test.py +++ b/tests/integration/normal_mode_test.py @@ -599,10 +599,10 @@ def test_comma_key(r: TmuxRunner): pos = r.cursor_pos() r.type_str("j" * 3) - assert r.cursor_pos != pos + assert r.cursor_pos() != pos r.type_str("'a") - assert r.cursor_pos == pos + assert r.cursor_pos() == pos target_line: str = r.color_screenshot()[3] assert "4\u2502" in target_line diff --git a/tests/model_test.cpp b/tests/model_test.cpp index 69782bb..34d0c96 100644 --- a/tests/model_test.cpp +++ b/tests/model_test.cpp @@ -755,5 +755,18 @@ TEST_CASE("is_marked", "[model]") { } TEST_CASE("go_to_mark", "[model]") { - REQUIRE(false); + auto m = Model({" foo", "bar"}, ""); + m.current_line = 1; + m.current_char = 1; + m.add_mark('a'); + REQUIRE(m.marks.size() == 1); + + m.current_line = 0; + m.current_char = 0; + + REQUIRE(m.go_to_mark('a')); + REQUIRE(m.current_line == 1); + REQUIRE(m.current_char == 1); + + REQUIRE(!m.go_to_mark('b')); } From 802e3bfdc4483105aa869ef058621cb16f0d8149 Mon Sep 17 00:00:00 2001 From: ttibsi Date: Mon, 4 May 2026 12:33:26 +0100 Subject: [PATCH 4/4] resolve location error --- src/controller.cpp | 6 ++++++ src/view.cpp | 4 ++-- tests/fixture/test_file_2.txt | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/controller.cpp b/src/controller.cpp index 2d8badd..913ef63 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -289,6 +289,8 @@ void Controller::start_action_engine() { } else if (k.value() == rawterm::Key('m')) { auto k2 = rawterm::wait_for_input(); if (k2.isCharInput()) { view.get_active_model()->add_mark(k2.code); } + // TODO: redraw just this line? + redraw_all = true; // add new line and go to insert mode (below) } else if (k.value() == rawterm::Key('o')) { @@ -394,6 +396,10 @@ void Controller::start_action_engine() { // TODO: rename this method to something like "replace cursor" view.change_model_cursor(); + + // TODO: calculate if the marked line wasn't currently on screen + // and redraw if needed + redraw_all = true; } // Move to beginning/end of line diff --git a/src/view.cpp b/src/view.cpp index 8ebd8de..9bf7fe3 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -91,8 +91,8 @@ void View::draw_screen() { for (const auto& [idx, line] : enumerate(viewable_range, start_idx)) { if (LINE_NUMBERS) { rawterm::Color c = COLOR_UI_BG; + if (get_active_model()->is_marked(idx - 1)) { c = COLOR_GREEN; } if (idx == get_active_model()->current_line + 1) { c = COLOR_DARK_YELLOW; } - if (get_active_model()->is_marked(idx)) { c = COLOR_GREEN; } screen += rawterm::set_foreground(std::format("{:>{}}\u2502", idx, line_number_offset), c); @@ -231,8 +231,8 @@ void View::draw_line(const Draw_Line_dir::values redraw_prev) { if (LINE_NUMBERS) { // TODO: refactor lineno colour into it's own view method rawterm::Color c = COLOR_UI_BG; - if (idx == get_active_model()->current_line) { c = COLOR_DARK_YELLOW; } if (get_active_model()->is_marked(idx)) { c = COLOR_GREEN; } + if (idx == get_active_model()->current_line) { c = COLOR_DARK_YELLOW; } line += rawterm::set_foreground(std::format("{:>{}}\u2502", idx + 1, line_number_offset), c); diff --git a/tests/fixture/test_file_2.txt b/tests/fixture/test_file_2.txt index e69de29..5b3b4a7 100644 --- a/tests/fixture/test_file_2.txt +++ b/tests/fixture/test_file_2.txt @@ -0,0 +1 @@ +testtest