From eae61cdc42263db399cf8bbea30788745e40914c Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Tue, 24 Mar 2026 09:19:53 +0000 Subject: [PATCH 1/3] dw now doesn't run at end of file --- src/action.h | 10 +++++----- src/model.cpp | 5 ++++- src/model.h | 2 +- tests/fixture/test_file_2.txt | 1 + tests/model_test.cpp | 22 ++++++++++++---------- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/action.h b/src/action.h index 2bed2d28..de549b22 100644 --- a/src/action.h +++ b/src/action.h @@ -144,14 +144,14 @@ template auto logger = spdlog::get("basic_logger"); if (logger != nullptr) { logger->info("Action called: DelCurrentWord"); } - const WordPos word = v->get_active_model()->current_word(); + const std::optional word = v->get_active_model()->current_word(); + if (!word.has_value()) { break; } v->get_active_model()->undo_stack.push_back(Change( - ActionType::DelCurrentWord, v->get_active_model()->current_line, word.start_pos, - word.text)); - - v->get_active_model()->delete_current_word(word); + ActionType::DelCurrentWord, v->get_active_model()->current_line, + word.value().start_pos, word.value().text)); + v->get_active_model()->delete_current_word(word.value()); } break; case ActionType::EndOfLine: { diff --git a/src/model.cpp b/src/model.cpp index 55c960d4..fbf2fb72 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -443,11 +443,14 @@ void Model::delete_current_line() { } } -[[nodiscard]] const WordPos Model::current_word() const { +[[nodiscard]] const std::optional Model::current_word() const { WordPos ret = {"", 0, 0}; const std::string* cur_line = &buf.at(current_line); uint_t start = current_char; + // "Start" is already at the end of the line + if (start == cur_line->size()) { return {}; } + while (start && is_letter(cur_line->at(start))) { start--; } diff --git a/src/model.h b/src/model.h index d07f2f81..a3a8e111 100644 --- a/src/model.h +++ b/src/model.h @@ -66,7 +66,7 @@ struct Model { void move_line_up(); void set_read_only(std::string_view); void delete_current_line(); - [[nodiscard]] const WordPos current_word() const; + [[nodiscard]] const std::optional current_word() const; void delete_current_word(const WordPos); [[nodiscard]] std::vector search_text(const std::string&) const; void search_and_replace(const std::string&); diff --git a/tests/fixture/test_file_2.txt b/tests/fixture/test_file_2.txt index e69de29b..5b3b4a79 100644 --- a/tests/fixture/test_file_2.txt +++ b/tests/fixture/test_file_2.txt @@ -0,0 +1 @@ +testtest diff --git a/tests/model_test.cpp b/tests/model_test.cpp index 3cbc3797..7bf1130d 100644 --- a/tests/model_test.cpp +++ b/tests/model_test.cpp @@ -458,7 +458,7 @@ TEST_CASE("undo", "[model]") { m.current_char = 1; m.undo_stack.push_back(Change(ActionType::DelCurrentWord, 1, 0, "line")); - m.delete_current_word(m.current_word()); + m.delete_current_word(m.current_word().value()); REQUIRE(m.buf.at(1) == " two"); REQUIRE(m.undo(24)); @@ -550,7 +550,7 @@ TEST_CASE("redo", "[model]") { m.current_char = 1; m.undo_stack.push_back(Change(ActionType::DelCurrentWord, 1, 0, "line")); - m.delete_current_word(m.current_word()); + m.delete_current_word(m.current_word().value()); REQUIRE(m.buf.at(1) == " two"); REQUIRE(m.undo(24)); @@ -614,19 +614,21 @@ TEST_CASE("current_word", "[model]") { m.current_line = 1; m.current_char = 2; - const WordPos ret = m.current_word(); - REQUIRE(ret.text == "line"); - REQUIRE(ret.start_pos == 0); + const std::optional ret = m.current_word(); + REQUIRE(ret.has_value()); + REQUIRE(ret.value().text == "line"); + REQUIRE(ret.value().start_pos == 0); } SECTION("Search to end of line") { m.current_line = 1; m.current_char = 6; - const WordPos ret = m.current_word(); + const std::optional ret = m.current_word(); + REQUIRE(ret.has_value()); REQUIRE(m.buf.at(m.current_line).at(m.current_char) == 'w'); - REQUIRE(ret.text == "two"); - REQUIRE(ret.start_pos == 5); + REQUIRE(ret.value().text == "two"); + REQUIRE(ret.value().start_pos == 5); } } @@ -635,13 +637,13 @@ TEST_CASE("delete_current_word", "[model]") { m.current_line = 1; m.current_char = 2; - m.delete_current_word(m.current_word()); + m.delete_current_word(m.current_word().value()); REQUIRE(m.buf.at(1) == " two"); m.current_line = 2; m.current_char = 7; - m.delete_current_word(m.current_word()); + m.delete_current_word(m.current_word().value()); REQUIRE(m.buf.at(2) == "line "); } From 218137739b39a62bb3038b5ebb2a1c8596a46415 Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Tue, 24 Mar 2026 11:12:40 +0000 Subject: [PATCH 2/3] Ensure unsaved marker and new test --- src/model.cpp | 1 + tests/integration/normal_mode_test.py | 9 +++++++++ tests/integration/setup.py | 1 + 3 files changed, 11 insertions(+) diff --git a/src/model.cpp b/src/model.cpp index fbf2fb72..a89abc34 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -470,6 +470,7 @@ void Model::delete_current_line() { } void Model::delete_current_word(const WordPos pos) { + unsaved = true; buf.at(pos.lineno).erase(pos.start_pos, pos.text.size()); } diff --git a/tests/integration/normal_mode_test.py b/tests/integration/normal_mode_test.py index 4001ad9a..76fe9759 100644 --- a/tests/integration/normal_mode_test.py +++ b/tests/integration/normal_mode_test.py @@ -523,6 +523,15 @@ def test_dw_key(r: TmuxRunner): assert "Lorem ipsum" not in r.lines()[0] +@setup("tests/fixture/test_file_1.txt") +def test_dw_key_end_of_file(r: TmuxRunner): + r.type_str("Gwwel") + r.type_str("dw") + + assert r.MODIFIED_MARKER not in r.statusbar_parts() + assert "newline" in r.lines()[2] + + @setup("tests/fixture/test_file_1.txt") def test_e_key(r: TmuxRunner): assert r.statusbar_parts()[-1] == "1:1" diff --git a/tests/integration/setup.py b/tests/integration/setup.py index eb129fa4..6d7b1ad8 100644 --- a/tests/integration/setup.py +++ b/tests/integration/setup.py @@ -15,6 +15,7 @@ class TmuxRunner(Runner): CMD_KEY: Final[str] = "\\;" SELECTED_LINE_ANSI: Final[str] = "\x1b[38;2;255;221;51m" + MODIFIED_MARKER: Final[str] = "[X]" filename: str def __init__(self, *args, **kwargs) -> None: From 60485d0439fcc19788843f058f08bc1ead4ae0c2 Mon Sep 17 00:00:00 2001 From: Ttibsi Date: Tue, 24 Mar 2026 11:18:10 +0000 Subject: [PATCH 3/3] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7028c740..c71c14de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ### main / head +* Resolved crash when the user tries to `delete word` past the end of a line + ### v0.0.3 | 17/Mar/2026 * Use `;e` to open a different file from within iris * Keyboard commands `tt`, `tn`, `tp` to create and switch between tabs