diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c6b4ff..a4cc5b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * `>` now intends the current line. * `<` now dedents current line +* `<` and `>` now are part of the undo/redo system * Implement `;!` to execute a shell command from the command bar * Disabled continuous deployment in GHA. See `.github/workflows/build_artifact.yml` for details diff --git a/src/action.h b/src/action.h index baf0b29..c051e14 100644 --- a/src/action.h +++ b/src/action.h @@ -179,6 +179,9 @@ template auto logger = spdlog::get("basic_logger"); if (logger != nullptr) { logger->info("Action called: IndentLine"); } v->get_active_model()->indent_curr_line(); + + v->get_active_model()->undo_stack.push_back( + Change(ActionType::IndentLine, v->get_active_model()->current_line, ' ')); } break; case ActionType::JumpEndOfWord: { diff --git a/src/controller.cpp b/src/controller.cpp index 8f10763..c98c35d 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -427,7 +427,7 @@ void Controller::start_action_engine() { parse_action(&view, Action {ActionType::DedentLine}); view.draw_line(Draw_Line_dir::None); - // Increment line + // Indent line } else if (k.value() == rawterm::Key('>')) { if (is_readonly_model()) { continue; } parse_action(&view, Action {ActionType::IndentLine}); diff --git a/src/model.cpp b/src/model.cpp index 2ef6829..4a77487 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -344,6 +344,14 @@ void Model::toggle_case() { } } break; + case ActionType::IndentLine: { + dedent_curr_line(); + } break; + + case ActionType::DedentLine: { + indent_curr_line(); + } break; + default: break; }; @@ -410,6 +418,14 @@ void Model::toggle_case() { delete_current_word({cur_change.text.value(), current_char, current_line}); } break; + case ActionType::IndentLine: { + indent_curr_line(); + } break; + + case ActionType::DedentLine: { + dedent_curr_line(); + } break; + default: break; }; diff --git a/tests/integration/normal_mode_test.py b/tests/integration/normal_mode_test.py index 7e7dd69..2bae3f5 100644 --- a/tests/integration/normal_mode_test.py +++ b/tests/integration/normal_mode_test.py @@ -415,6 +415,35 @@ def test_undo_redo_delete_word(r: TmuxRunner): assert word not in r.lines()[0] +@setup("tests/fixture/test_file_1.txt") +def test_undo_redo_indentline(r: TmuxRunner): + r.press('>') + assert r.lines()[0].startswith(" 1\u2502 ") + + # undo + r.press("u") + assert r.lines()[0].startswith(" 1\u2502This") + + # redo + r.press("R") + assert r.lines()[0].startswith(" 1\u2502 ") + + +@setup("tests/fixture/test_file_1.txt") +def test_undo_redo_dedentline(r: TmuxRunner): + r.press('j') + r.press('<') + assert r.lines()[1].startswith(" 2\u2502here") + + # undo + r.press("u") + assert r.lines()[1].startswith(" 2\u2502 ") + + # redo + r.press("R") + assert r.lines()[1].startswith(" 2\u2502here") + + @setup("tests/fixture/test_file_1.txt") def test_upper_j_key(r: TmuxRunner): r.press("J") diff --git a/tests/model_test.cpp b/tests/model_test.cpp index 93f57db..5bab5f5 100644 --- a/tests/model_test.cpp +++ b/tests/model_test.cpp @@ -469,6 +469,43 @@ TEST_CASE("undo", "[model]") { REQUIRE(m.undo(24)); REQUIRE(m.buf.at(1) == "line two"); } + + SECTION("IndentLine") { + m.undo_stack.push_back(Change(ActionType::IndentLine, 0, 0)); + + m.indent_curr_line(); + REQUIRE(m.buf.at(m.current_line).at(0) == ' '); + REQUIRE(m.buf.at(m.current_line).at(1) == ' '); + REQUIRE(m.buf.at(m.current_line).at(2) == ' '); + REQUIRE(m.buf.at(m.current_line).at(3) == ' '); + + REQUIRE(m.undo(24)); + REQUIRE(m.buf.at(m.current_line).at(0) == 'l'); + REQUIRE(m.buf.at(m.current_line).at(1) == 'i'); + REQUIRE(m.buf.at(m.current_line).at(2) == 'n'); + REQUIRE(m.buf.at(m.current_line).at(3) == 'e'); + } + + SECTION("DedentLine") { + m.indent_curr_line(); + REQUIRE(m.buf.at(m.current_line).at(0) == ' '); + REQUIRE(m.buf.at(m.current_line).at(1) == ' '); + REQUIRE(m.buf.at(m.current_line).at(2) == ' '); + REQUIRE(m.buf.at(m.current_line).at(3) == ' '); + + m.undo_stack.push_back(Change(ActionType::DedentLine, 0, 0)); + m.dedent_curr_line(); + REQUIRE(m.buf.at(m.current_line).at(0) == 'l'); + REQUIRE(m.buf.at(m.current_line).at(1) == 'i'); + REQUIRE(m.buf.at(m.current_line).at(2) == 'n'); + REQUIRE(m.buf.at(m.current_line).at(3) == 'e'); + + REQUIRE(m.undo(24)); + REQUIRE(m.buf.at(m.current_line).at(0) == ' '); + REQUIRE(m.buf.at(m.current_line).at(1) == ' '); + REQUIRE(m.buf.at(m.current_line).at(2) == ' '); + REQUIRE(m.buf.at(m.current_line).at(3) == ' '); + } } TEST_CASE("get_current_char", "[model]") { @@ -564,6 +601,59 @@ TEST_CASE("redo", "[model]") { REQUIRE(m.redo(24)); REQUIRE(m.buf.at(1) == " two"); } + + SECTION("IndentLine") { + m.current_line = 0; + m.current_char = 0; + m.undo_stack.push_back(Change(ActionType::IndentLine, 0, 0)); + + m.indent_curr_line(); + REQUIRE(m.buf.at(m.current_line).at(0) == ' '); + REQUIRE(m.buf.at(m.current_line).at(1) == ' '); + REQUIRE(m.buf.at(m.current_line).at(2) == ' '); + REQUIRE(m.buf.at(m.current_line).at(3) == ' '); + + REQUIRE(m.undo(24)); + REQUIRE(m.buf.at(m.current_line).at(0) == 'l'); + REQUIRE(m.buf.at(m.current_line).at(1) == 'i'); + REQUIRE(m.buf.at(m.current_line).at(2) == 'n'); + REQUIRE(m.buf.at(m.current_line).at(3) == 'e'); + + REQUIRE(m.redo(24)); + REQUIRE(m.buf.at(m.current_line).at(0) == ' '); + REQUIRE(m.buf.at(m.current_line).at(1) == ' '); + REQUIRE(m.buf.at(m.current_line).at(2) == ' '); + REQUIRE(m.buf.at(m.current_line).at(3) == ' '); + } + + SECTION("DedentLine") { + m.current_line = 0; + m.current_char = 0; + m.indent_curr_line(); + REQUIRE(m.buf.at(m.current_line).at(0) == ' '); + REQUIRE(m.buf.at(m.current_line).at(1) == ' '); + REQUIRE(m.buf.at(m.current_line).at(2) == ' '); + REQUIRE(m.buf.at(m.current_line).at(3) == ' '); + + m.undo_stack.push_back(Change(ActionType::DedentLine, 0, 0)); + m.dedent_curr_line(); + REQUIRE(m.buf.at(m.current_line).at(0) == 'l'); + REQUIRE(m.buf.at(m.current_line).at(1) == 'i'); + REQUIRE(m.buf.at(m.current_line).at(2) == 'n'); + REQUIRE(m.buf.at(m.current_line).at(3) == 'e'); + + REQUIRE(m.undo(24)); + REQUIRE(m.buf.at(m.current_line).at(0) == ' '); + REQUIRE(m.buf.at(m.current_line).at(1) == ' '); + REQUIRE(m.buf.at(m.current_line).at(2) == ' '); + REQUIRE(m.buf.at(m.current_line).at(3) == ' '); + + REQUIRE(m.redo(24)); + REQUIRE(m.buf.at(m.current_line).at(0) == 'l'); + REQUIRE(m.buf.at(m.current_line).at(1) == 'i'); + REQUIRE(m.buf.at(m.current_line).at(2) == 'n'); + REQUIRE(m.buf.at(m.current_line).at(3) == 'e'); + } } TEST_CASE("move_line_down", "[model]") {