Skip to content

Commit

Permalink
Make live autocompletion optional (#10)
Browse files Browse the repository at this point in the history
Add config option to disable live autocompletion

Co-authored-by: Arian Suarez <[email protected]>
  • Loading branch information
funbiscuit and asuar078 authored Jun 30, 2022
1 parent ba62216 commit 4ba7596
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 14 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Single-header CLI library intended for use in embedded systems (like STM32 or Ar
* Dynamic or static allocation
* Configurable memory usage
* Command-to-function binding with arguments support
* Live autocompletion (see demo above)
* Live autocompletion (see demo above, can be disabled)
* Tab (jump to end of current autocompletion) and backspace (remove char) support
* History support (navigate with up and down keypress)
* Any byte-stream interface is supported (for example, UART)
Expand Down
22 changes: 16 additions & 6 deletions lib/include/embedded_cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ struct EmbeddedCliConfig {
* Size of buffer for cli and internal structures (in bytes).
*/
uint16_t cliBufferSize;

/**
* Whether autocompletion should be enabled.
* If false, autocompletion is disabled but you still can use 'tab' to
* complete current command manually.
*/
bool enableAutoComplete;
};

/**
Expand All @@ -165,12 +172,15 @@ struct EmbeddedCliConfig {
* Returned structure is always the same so do not free and try to use it
* immediately.
* Default values:
* -rxBufferSize = 64
* -cmdBufferSize = 64
* -historyBufferSize = 128
* -cliBuffer = NULL (use dynamic allocation)
* -cliBufferSize = 0
* -maxBindingCount = 8
* <ul>
* <li>rxBufferSize = 64</li>
* <li>cmdBufferSize = 64</li>
* <li>historyBufferSize = 128</li>
* <li>cliBuffer = NULL (use dynamic allocation)</li>
* <li>cliBufferSize = 0</li>
* <li>maxBindingCount = 8</li>
* <li>enableAutoComplete = true</li>
* </ul>
* @return configuration for cli creation
*/
EmbeddedCliConfig *embeddedCliDefaultConfig(void);
Expand Down
12 changes: 12 additions & 0 deletions lib/src/embedded_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
*/
#define CLI_FLAG_DIRECT_PRINT 0x10u

/**
* Indicates that live autocompletion is enabled
*/
#define CLI_FLAG_AUTOCOMPLETE_ENABLED 0x20u

typedef struct EmbeddedCliImpl EmbeddedCliImpl;
typedef struct AutocompletedCommand AutocompletedCommand;
typedef struct FifoBuf FifoBuf;
Expand Down Expand Up @@ -373,6 +378,7 @@ EmbeddedCliConfig *embeddedCliDefaultConfig(void) {
defaultConfig.cliBuffer = NULL;
defaultConfig.cliBufferSize = 0;
defaultConfig.maxBindingCount = 8;
defaultConfig.enableAutoComplete = true;
return &defaultConfig;
}

Expand Down Expand Up @@ -434,6 +440,9 @@ EmbeddedCli *embeddedCliNew(EmbeddedCliConfig *config) {
if (allocated)
SET_FLAG(impl->flags, CLI_FLAG_ALLOCATED);

if (config->enableAutoComplete)
SET_FLAG(impl->flags, CLI_FLAG_AUTOCOMPLETE_ENABLED);

impl->rxBuffer.size = config->rxBufferSize;
impl->rxBuffer.front = 0;
impl->rxBuffer.back = 0;
Expand Down Expand Up @@ -936,6 +945,9 @@ static AutocompletedCommand getAutocompletedCommand(EmbeddedCli *cli, const char
static void printLiveAutocompletion(EmbeddedCli *cli) {
PREPARE_IMPL(cli);

if (!IS_FLAG_SET(impl->flags, CLI_FLAG_AUTOCOMPLETE_ENABLED))
return;

AutocompletedCommand cmd = getAutocompletedCommand(cli, impl->cmdBuffer);

if (cmd.candidateCount == 0) {
Expand Down
41 changes: 35 additions & 6 deletions tests/CliTestRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ void CliTestRunner::runTests() {
testHelp();
}

SECTION("Autocomplete") {
testAutocomplete();
SECTION("Autocomplete enabled") {
testAutocompleteEnabled();
}

SECTION("Escape sequences") {
Expand All @@ -46,6 +46,35 @@ void CliTestRunner::runTests() {
}
}

void CliTestRunner::testAutocompleteDisabled() {
embeddedCliProcess(cli);

mock.addCommandBinding("set");

SECTION("Live autocomplete disabled") {
mock.sendStr("s");

embeddedCliProcess(cli);

size_t cursor = 0;
auto lines = mock.getLines(false, &cursor);

REQUIRE(lines.size() == 1);
REQUIRE(lines[0] == "> s");
REQUIRE(cursor == 3);

mock.sendStr("\t");

embeddedCliProcess(cli);

lines = mock.getLines(true, &cursor);

REQUIRE(lines.size() == 1);
REQUIRE(lines[0] == "> set");
REQUIRE(cursor == 6);
}
}

void CliTestRunner::testBase() {
auto &commands = mock.getReceivedCommands();

Expand Down Expand Up @@ -179,7 +208,7 @@ void CliTestRunner::testHistory() {

SECTION("When history is present, can navigate") {
std::vector<std::string> cmds = {"command", "get", "reset", "exit"};
for (auto &cmd : cmds) {
for (auto &cmd: cmds) {
mock.sendLine(cmd);
}
embeddedCliProcess(cli);
Expand All @@ -203,7 +232,7 @@ void CliTestRunner::testHistory() {

SECTION("Sending command multiple times, doesn't create duplicates") {
std::vector<std::string> cmds = {"command", "get", "command", "command"};
for (auto &cmd : cmds) {
for (auto &cmd: cmds) {
mock.sendLine(cmd);
}
embeddedCliProcess(cli);
Expand Down Expand Up @@ -231,7 +260,7 @@ void CliTestRunner::testHistory() {

SECTION("Sending command resets history cursor") {
std::vector<std::string> cmds = {"command", "get"};
for (auto &cmd : cmds) {
for (auto &cmd: cmds) {
mock.sendLine(cmd);
}
embeddedCliProcess(cli);
Expand All @@ -258,7 +287,7 @@ void CliTestRunner::testHistory() {
}
}

void CliTestRunner::testAutocomplete() {
void CliTestRunner::testAutocompleteEnabled() {
mock.addCommandBinding("get");
mock.addCommandBinding("set");
mock.addCommandBinding("get-new");
Expand Down
4 changes: 3 additions & 1 deletion tests/CliTestRunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class CliTestRunner {

void runTests();

void testAutocompleteDisabled();

private:
EmbeddedCli *cli;
CliMock mock;
Expand All @@ -19,7 +21,7 @@ class CliTestRunner {

void testHistory();

void testAutocomplete();
void testAutocompleteEnabled();

void testPrinting();

Expand Down
15 changes: 15 additions & 0 deletions tests/EmbeddedCliTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,18 @@ TEST_CASE("EmbeddedCli. Static allocation", "[cli]") {

embeddedCliFree(cli);
}


TEST_CASE("EmbeddedCli. Disabled autocomplete", "[cli]") {
EmbeddedCliConfig *config = embeddedCliDefaultConfig();
config->enableAutoComplete = false;
EmbeddedCli *cli = embeddedCliNew(config);

REQUIRE(cli != nullptr);

CliTestRunner runner(cli);

runner.testAutocompleteDisabled();

embeddedCliFree(cli);
}

0 comments on commit 4ba7596

Please sign in to comment.