Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* Disabled continuous deployment in GHA. See `.github/workflows/build_artifact.yml`
for details

* 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
Expand Down
10 changes: 5 additions & 5 deletions src/action.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,14 @@ template <typename T, typename U>
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<WordPos> 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: {
Expand Down
6 changes: 5 additions & 1 deletion src/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,11 +447,14 @@ void Model::delete_current_line() {
}
}

[[nodiscard]] const WordPos Model::current_word() const {
[[nodiscard]] const std::optional<WordPos> 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--;
}
Expand All @@ -471,6 +474,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());
}

Expand Down
2 changes: 1 addition & 1 deletion src/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<WordPos> current_word() const;
void delete_current_word(const WordPos);
[[nodiscard]] std::vector<std::string> search_text(const std::string&) const;
void search_and_replace(const std::string&);
Expand Down
1 change: 0 additions & 1 deletion tests/fixture/test_file_2.txt
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
test
9 changes: 9 additions & 0 deletions tests/integration/normal_mode_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions tests/integration/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
22 changes: 12 additions & 10 deletions tests/model_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,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));
Expand Down Expand Up @@ -553,7 +553,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));
Expand Down Expand Up @@ -617,19 +617,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<WordPos> 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<WordPos> 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);
}
}

Expand All @@ -638,13 +640,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 ");
}

Expand Down
Loading