diff --git a/CHANGELOG.md b/CHANGELOG.md index e597e4d..857a30b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### main / head +* 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/controller.cpp b/src/controller.cpp index aceaa74..fbd49da 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -515,6 +515,24 @@ bool Controller::parse_command() { // Empty command if (cmd.size() < 2) { return false; } + // Run shell command + if (cmd.at(1) == '!') { + const std::optional opt_resp = shell_exec(cmd.substr(2, cmd.size())); + if (!opt_resp.has_value()) { return false; } + const Response resp = opt_resp.value(); + if (resp.out.size()) { + std::size_t nlPos = resp.out.find('\n'); + std::string msg = (nlPos == std::string::npos) ? resp.out : resp.out.substr(0, nlPos); + view.display_message(msg, rawterm::Colors::green); + } else { + std::size_t nlPos = resp.err.find('\n'); + std::string msg = (nlPos == std::string::npos) ? resp.err : resp.err.substr(0, nlPos); + view.display_message(msg, rawterm::Colors::red); + } + + return false; + } + if (std::isdigit(cmd.at(1))) { const unsigned int offset = uint32_t(std::stoi(cmd.substr(1, cmd.size()))); view.set_current_line(offset); diff --git a/src/view.cpp b/src/view.cpp index 5a5a1bb..6764243 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -336,6 +336,7 @@ const std::string View::render_status_bar() const { return prev_pos; } +// TODO: Modify this to take a string_view instead void View::display_message(std::string msg, std::optional color) { rawterm::Pos prev_pos = cur; cur.move({view_size.vertical, 1}); diff --git a/tests/integration/command_mode_test.py b/tests/integration/command_mode_test.py index 00680cb..d72f1c8 100644 --- a/tests/integration/command_mode_test.py +++ b/tests/integration/command_mode_test.py @@ -24,7 +24,7 @@ def test_quit_with_modified_buffer(r: TmuxRunner): r.iris_cmd("q") err_line: str = r.color_screenshot()[-1] assert "Unsaved changes. Use `;q!` to discard" in err_line - assert "\x1b[49m" in err_line # red text + assert "255;0;0" in err_line # red text @setup("tests/fixture/test_file_2.txt", multi_file=True) @@ -399,3 +399,19 @@ def test_find_next_str_repeat_input_command(r: TmuxRunner): r.iris_cmd("f") r.await_cursor_pos(12, 10) assert r.await_statusbar_parts()[-1] == "46:7" + + +@setup() +def test_run_shell_cmd_stdout(r: TmuxRunner): + r.iris_cmd("!echo 'hello'") + msg: str = r.color_screenshot()[-1] + assert "hello" in msg + assert "0;128;0" in msg # green text + + +@setup() +def test_run_shell_cmd_stderr(r: TmuxRunner): + r.iris_cmd("!echo 'hello' 1>&2") + msg: str = r.color_screenshot()[-1] + assert "hello" in msg + assert "255;0;0" in msg # red text