From 0915f08d24099ca377f914409ddd33a5faf8e1e2 Mon Sep 17 00:00:00 2001 From: gary7530 Date: Thu, 11 Dec 2025 15:34:16 +0800 Subject: [PATCH 1/6] fix(cli): remove help command --- lib/src/embedded_cli.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/src/embedded_cli.c b/lib/src/embedded_cli.c index 549bb29..eab5fbd 100644 --- a/lib/src/embedded_cli.c +++ b/lib/src/embedded_cli.c @@ -878,12 +878,7 @@ static void parseCommand(EmbeddedCli *cli) { embeddedCliTokenizeArgs(cmdArgs); // currently, output is blank line, so we can just print directly SET_FLAG(impl->flags, CLI_FLAG_DIRECT_PRINT); - // check if help was requested (help is printed when no other options are set) - if (cmdArgs != NULL && (strcmp(cmdArgs, "-h") == 0 || strcmp(cmdArgs, "--help") == 0)) { - printBindingHelp(cli, &impl->bindings[i]); - } else { - impl->bindings[i].binding(cli, cmdArgs, impl->bindings[i].context); - } + impl->bindings[i].binding(cli, cmdArgs, impl->bindings[i].context); UNSET_U8FLAG(impl->flags, CLI_FLAG_DIRECT_PRINT); return; } From f8e30592eefbaf53af2ba3abf259218f7ecb7055 Mon Sep 17 00:00:00 2001 From: gary7530 Date: Mon, 15 Dec 2025 15:49:57 +0800 Subject: [PATCH 2/6] feat: add home, end, and delete cursor functionality --- lib/src/embedded_cli.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/src/embedded_cli.c b/lib/src/embedded_cli.c index 549bb29..85a2dbb 100644 --- a/lib/src/embedded_cli.c +++ b/lib/src/embedded_cli.c @@ -760,6 +760,29 @@ static void onEscapedInput(EmbeddedCli *cli, char c) { impl->cursorPos++; writeToOutput(cli, escSeqCursorLeft); } + + // Home + if (c == 'H' || (c == '~' && (impl->lastChar == '1' || impl->lastChar == '7'))) { + if (impl->cursorPos < impl->cmdSize) { + moveCursor(cli, impl->cmdSize - impl->cursorPos, CURSOR_DIRECTION_BACKWARD); + impl->cursorPos = impl->cmdSize; + } + } + // End + if (c == 'F' || (c == '~' && (impl->lastChar == '4' || impl->lastChar == '8'))) { + if (impl->cursorPos > 0) { + moveCursor(cli, impl->cursorPos, CURSOR_DIRECTION_FORWARD); + impl->cursorPos = 0; + } + } + // Delete + if (c == '~' && impl->lastChar == '3' && impl->cursorPos > 0) { + size_t insertPos = strlen(impl->cmdBuffer) - impl->cursorPos; + memmove(&impl->cmdBuffer[insertPos], &impl->cmdBuffer[insertPos + 1], impl->cursorPos); + --impl->cmdSize; + --impl->cursorPos; + writeToOutput(cli, escSeqDeleteChar); + } } } From 2e21ad3a7bd635a7a3a42b6c96602c3b69764963 Mon Sep 17 00:00:00 2001 From: Bendik Mann <117764379+BendikMann@users.noreply.github.com> Date: Mon, 23 Feb 2026 09:36:53 -0600 Subject: [PATCH 3/6] Changes to DEL behavior, revert of help removal, addition of some tests for new control sequences --- lib/src/embedded_cli.c | 10 ++++++++-- tests/cli/BaseTest.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/src/embedded_cli.c b/lib/src/embedded_cli.c index e91a267..96b9dcf 100644 --- a/lib/src/embedded_cli.c +++ b/lib/src/embedded_cli.c @@ -830,9 +830,10 @@ static void onControlInput(EmbeddedCli *cli, char c) { impl->cursorPos = 0; writeToOutput(cli, impl->invitation); + // backspace or del } else if ((c == '\b' || c == 0x7F) && ((impl->cmdSize - impl->cursorPos) > 0)) { // remove char from screen - writeToOutput(cli, escSeqCursorLeft); // Move cursor to left + if (c == '\b') writeToOutput(cli, escSeqCursorLeft); // Move cursor to left writeToOutput(cli, escSeqDeleteChar); // And remove character // and from buffer size_t insertPos = strlen(impl->cmdBuffer) - impl->cursorPos; @@ -901,7 +902,12 @@ static void parseCommand(EmbeddedCli *cli) { embeddedCliTokenizeArgs(cmdArgs); // currently, output is blank line, so we can just print directly SET_FLAG(impl->flags, CLI_FLAG_DIRECT_PRINT); - impl->bindings[i].binding(cli, cmdArgs, impl->bindings[i].context); + // check if help was requested (help is printed when no other options are set) + if (cmdArgs != NULL && (strcmp(cmdArgs, "-h") == 0 || strcmp(cmdArgs, "--help") == 0)) { + printBindingHelp(cli, &impl->bindings[i]); + } else { + impl->bindings[i].binding(cli, cmdArgs, impl->bindings[i].context); + } UNSET_U8FLAG(impl->flags, CLI_FLAG_DIRECT_PRINT); return; } diff --git a/tests/cli/BaseTest.cpp b/tests/cli/BaseTest.cpp index a7ca3a2..af9f6ed 100644 --- a/tests/cli/BaseTest.cpp +++ b/tests/cli/BaseTest.cpp @@ -157,6 +157,33 @@ TEST_CASE("CLI. Base tests", "[cli]") { REQUIRE(displayed.cursorColumn == 7); } + SECTION("Move cursor to start") { + cli.send(" both\x1B[Hget"); + cli.process(); + auto displayed = cli.getDisplay(); + REQUIRE(displayed.lines.size() == 1); + REQUIRE(displayed.lines[0] == "> get both"); + REQUIRE(displayed.cursorColumn == 5); + } + + SECTION("Move cursor to start then end") { + cli.send("both\x1B[Hget \x1B[F"); + cli.process(); + auto displayed = cli.getDisplay(); + REQUIRE(displayed.lines.size() == 1); + REQUIRE(displayed.lines[0] == "> get both"); + REQUIRE(displayed.cursorColumn == 10); + } + + SECTION("Move cursor back and perform Delete by Control Sequence") { + cli.send("get baoth\x1B[D\x1B[D\x1B[D\x1B\x1B[D\x1B[3~"); + cli.process(); + auto displayed = cli.getDisplay(); + REQUIRE(displayed.lines.size() == 1); + REQUIRE(displayed.lines[0] == "> get both"); + REQUIRE(displayed.cursorColumn == 7); + } + SECTION("Command that is too long") { size_t cmdMax = embeddedCliDefaultConfig()->cmdBufferSize; std::string cmdMaxTest = std::string(cmdMax/2, 'x'); From 7c1b02f3c800f4e687e64e3a032a152d648cd743 Mon Sep 17 00:00:00 2001 From: Bendik Mann <117764379+BendikMann@users.noreply.github.com> Date: Mon, 23 Feb 2026 09:49:32 -0600 Subject: [PATCH 4/6] Change DEL/Backspace behavior, Added Test for Alternative Control Mode Sequences --- lib/src/embedded_cli.c | 12 ++++++++--- tests/cli/BaseTest.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/src/embedded_cli.c b/lib/src/embedded_cli.c index 96b9dcf..2591a44 100644 --- a/lib/src/embedded_cli.c +++ b/lib/src/embedded_cli.c @@ -830,15 +830,21 @@ static void onControlInput(EmbeddedCli *cli, char c) { impl->cursorPos = 0; writeToOutput(cli, impl->invitation); - // backspace or del - } else if ((c == '\b' || c == 0x7F) && ((impl->cmdSize - impl->cursorPos) > 0)) { + } else if (c == '\b' && ((impl->cmdSize - impl->cursorPos) > 0)) { // remove char from screen - if (c == '\b') writeToOutput(cli, escSeqCursorLeft); // Move cursor to left + writeToOutput(cli, escSeqCursorLeft); // Move cursor to left writeToOutput(cli, escSeqDeleteChar); // And remove character // and from buffer size_t insertPos = strlen(impl->cmdBuffer) - impl->cursorPos; memmove(&impl->cmdBuffer[insertPos - 1], &impl->cmdBuffer[insertPos], impl->cursorPos + 1); --impl->cmdSize; + // Delete + } else if (c == 0x7F && ((impl->cmdSize - impl->cursorPos) > 0)){ + size_t insertPos = strlen(impl->cmdBuffer) - impl->cursorPos; + memmove(&impl->cmdBuffer[insertPos], &impl->cmdBuffer[insertPos + 1], impl->cursorPos); + --impl->cmdSize; + --impl->cursorPos; + writeToOutput(cli, escSeqDeleteChar); } else if (c == '\t') { onAutocompleteRequest(cli); } diff --git a/tests/cli/BaseTest.cpp b/tests/cli/BaseTest.cpp index af9f6ed..e3a6525 100644 --- a/tests/cli/BaseTest.cpp +++ b/tests/cli/BaseTest.cpp @@ -166,6 +166,24 @@ TEST_CASE("CLI. Base tests", "[cli]") { REQUIRE(displayed.cursorColumn == 5); } + SECTION("Move cursor to start with 1~ alternative sequence") { + cli.send(" both\x1B[1~get"); + cli.process(); + auto displayed = cli.getDisplay(); + REQUIRE(displayed.lines.size() == 1); + REQUIRE(displayed.lines[0] == "> get both"); + REQUIRE(displayed.cursorColumn == 5); + } + + SECTION("Move cursor to start with 7~ alternative sequence") { + cli.send(" both\x1B[1~get"); + cli.process(); + auto displayed = cli.getDisplay(); + REQUIRE(displayed.lines.size() == 1); + REQUIRE(displayed.lines[0] == "> get both"); + REQUIRE(displayed.cursorColumn == 5); + } + SECTION("Move cursor to start then end") { cli.send("both\x1B[Hget \x1B[F"); cli.process(); @@ -175,6 +193,25 @@ TEST_CASE("CLI. Base tests", "[cli]") { REQUIRE(displayed.cursorColumn == 10); } + SECTION("Move cursor to start then end with 4~ alternative sequence") { + cli.send("both\x1B[Hget \x1B[4~"); + cli.process(); + auto displayed = cli.getDisplay(); + REQUIRE(displayed.lines.size() == 1); + REQUIRE(displayed.lines[0] == "> get both"); + REQUIRE(displayed.cursorColumn == 10); + } + + + SECTION("Move cursor to start then end with 8~ alternative sequence") { + cli.send("both\x1B[Hget \x1B[8~"); + cli.process(); + auto displayed = cli.getDisplay(); + REQUIRE(displayed.lines.size() == 1); + REQUIRE(displayed.lines[0] == "> get both"); + REQUIRE(displayed.cursorColumn == 10); + } + SECTION("Move cursor back and perform Delete by Control Sequence") { cli.send("get baoth\x1B[D\x1B[D\x1B[D\x1B\x1B[D\x1B[3~"); cli.process(); @@ -184,6 +221,15 @@ TEST_CASE("CLI. Base tests", "[cli]") { REQUIRE(displayed.cursorColumn == 7); } + SECTION("Move cursor back and perform Delete") { + cli.send("get baoth\x1B[D\x1B[D\x1B[D\x1B\x1B[D\x7f"); + cli.process(); + auto displayed = cli.getDisplay(); + REQUIRE(displayed.lines.size() == 1); + REQUIRE(displayed.lines[0] == "> get both"); + REQUIRE(displayed.cursorColumn == 7); + } + SECTION("Command that is too long") { size_t cmdMax = embeddedCliDefaultConfig()->cmdBufferSize; std::string cmdMaxTest = std::string(cmdMax/2, 'x'); From 3389f559029a046c6938a9478fab1a6317041ee5 Mon Sep 17 00:00:00 2001 From: Bendik Mann <117764379+BendikMann@users.noreply.github.com> Date: Wed, 25 Feb 2026 08:18:03 -0600 Subject: [PATCH 5/6] Fix DEL usage at the beginning and end of a line and added tests to validate --- lib/src/embedded_cli.c | 4 ++-- tests/cli/BaseTest.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/src/embedded_cli.c b/lib/src/embedded_cli.c index 2591a44..755be4e 100644 --- a/lib/src/embedded_cli.c +++ b/lib/src/embedded_cli.c @@ -776,7 +776,7 @@ static void onEscapedInput(EmbeddedCli *cli, char c) { } } // Delete - if (c == '~' && impl->lastChar == '3' && impl->cursorPos > 0) { + if (c == '~' && impl->lastChar == '3' && impl->cursorPos != 0) { size_t insertPos = strlen(impl->cmdBuffer) - impl->cursorPos; memmove(&impl->cmdBuffer[insertPos], &impl->cmdBuffer[insertPos + 1], impl->cursorPos); --impl->cmdSize; @@ -839,7 +839,7 @@ static void onControlInput(EmbeddedCli *cli, char c) { memmove(&impl->cmdBuffer[insertPos - 1], &impl->cmdBuffer[insertPos], impl->cursorPos + 1); --impl->cmdSize; // Delete - } else if (c == 0x7F && ((impl->cmdSize - impl->cursorPos) > 0)){ + } else if (c == 0x7F && impl->cursorPos != 0){ size_t insertPos = strlen(impl->cmdBuffer) - impl->cursorPos; memmove(&impl->cmdBuffer[insertPos], &impl->cmdBuffer[insertPos + 1], impl->cursorPos); --impl->cmdSize; diff --git a/tests/cli/BaseTest.cpp b/tests/cli/BaseTest.cpp index e3a6525..dc17624 100644 --- a/tests/cli/BaseTest.cpp +++ b/tests/cli/BaseTest.cpp @@ -230,6 +230,35 @@ TEST_CASE("CLI. Base tests", "[cli]") { REQUIRE(displayed.cursorColumn == 7); } + SECTION("Delete at end of section") { + cli.sendLine("set example\x7f"); + cli.process(); + + auto lines = cli.getDisplay().lines; + + REQUIRE(lines[0] == "> set example"); + + } + + SECTION("Delete at end of section with alternative sequence") { + cli.sendLine("set example\x7f"); + cli.process(); + + auto lines = cli.getDisplay().lines; + + REQUIRE(lines[0] == "> set example"); + + } + + SECTION("Delete at start of line should work") { + cli.sendLine("nset\x1B[D\x1B[D\x1B[D\x1B[D\x7f"); + cli.process(); + + auto lines = cli.getDisplay().lines; + + REQUIRE(lines[0] == "> set"); + } + SECTION("Command that is too long") { size_t cmdMax = embeddedCliDefaultConfig()->cmdBufferSize; std::string cmdMaxTest = std::string(cmdMax/2, 'x'); From 459e4ed66956b0d93f6c3fcb238b71fb4154e38e Mon Sep 17 00:00:00 2001 From: Bendik Mann <117764379+BendikMann@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:42:06 -0600 Subject: [PATCH 6/6] Reverted modification to DEL, ASCII backspace and DEL now map to the same thing. Changed tests which checked ASCII DEL to only check control code DEL --- lib/src/embedded_cli.c | 9 +-------- tests/cli/BaseTest.cpp | 16 +++------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/lib/src/embedded_cli.c b/lib/src/embedded_cli.c index 755be4e..ba31a6a 100644 --- a/lib/src/embedded_cli.c +++ b/lib/src/embedded_cli.c @@ -830,7 +830,7 @@ static void onControlInput(EmbeddedCli *cli, char c) { impl->cursorPos = 0; writeToOutput(cli, impl->invitation); - } else if (c == '\b' && ((impl->cmdSize - impl->cursorPos) > 0)) { + } else if ((c == '\b' || c == 0x7F) && ((impl->cmdSize - impl->cursorPos) > 0)) { // remove char from screen writeToOutput(cli, escSeqCursorLeft); // Move cursor to left writeToOutput(cli, escSeqDeleteChar); // And remove character @@ -838,13 +838,6 @@ static void onControlInput(EmbeddedCli *cli, char c) { size_t insertPos = strlen(impl->cmdBuffer) - impl->cursorPos; memmove(&impl->cmdBuffer[insertPos - 1], &impl->cmdBuffer[insertPos], impl->cursorPos + 1); --impl->cmdSize; - // Delete - } else if (c == 0x7F && impl->cursorPos != 0){ - size_t insertPos = strlen(impl->cmdBuffer) - impl->cursorPos; - memmove(&impl->cmdBuffer[insertPos], &impl->cmdBuffer[insertPos + 1], impl->cursorPos); - --impl->cmdSize; - --impl->cursorPos; - writeToOutput(cli, escSeqDeleteChar); } else if (c == '\t') { onAutocompleteRequest(cli); } diff --git a/tests/cli/BaseTest.cpp b/tests/cli/BaseTest.cpp index dc17624..3fb683b 100644 --- a/tests/cli/BaseTest.cpp +++ b/tests/cli/BaseTest.cpp @@ -222,7 +222,7 @@ TEST_CASE("CLI. Base tests", "[cli]") { } SECTION("Move cursor back and perform Delete") { - cli.send("get baoth\x1B[D\x1B[D\x1B[D\x1B\x1B[D\x7f"); + cli.send("get baoth\x1B[D\x1B[D\x1B[D\x1B\x1B[D\x1B[3~"); cli.process(); auto displayed = cli.getDisplay(); REQUIRE(displayed.lines.size() == 1); @@ -231,17 +231,7 @@ TEST_CASE("CLI. Base tests", "[cli]") { } SECTION("Delete at end of section") { - cli.sendLine("set example\x7f"); - cli.process(); - - auto lines = cli.getDisplay().lines; - - REQUIRE(lines[0] == "> set example"); - - } - - SECTION("Delete at end of section with alternative sequence") { - cli.sendLine("set example\x7f"); + cli.sendLine("set example\x1B[3~"); cli.process(); auto lines = cli.getDisplay().lines; @@ -251,7 +241,7 @@ TEST_CASE("CLI. Base tests", "[cli]") { } SECTION("Delete at start of line should work") { - cli.sendLine("nset\x1B[D\x1B[D\x1B[D\x1B[D\x7f"); + cli.sendLine("nset\x1B[D\x1B[D\x1B[D\x1B[D\x1B[3~"); cli.process(); auto lines = cli.getDisplay().lines;