Skip to content

Commit

Permalink
close #233 (parent menu shortcut)
Browse files Browse the repository at this point in the history
  • Loading branch information
daniele77 committed Apr 2, 2024
1 parent 090e922 commit 3d7091e
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Fix issue with remote session on ARM platforms (issue [#220](https://github.com/daniele77/cli/issues/220))
- Custom handler for wrong commands (issue [#223](https://github.com/daniele77/cli/issues/223))
- Parent menus does not chain (issue [#234](https://github.com/daniele77/cli/issues/234))
- Parent menu shortcut (issue [#233](https://github.com/daniele77/cli/issues/233))

## [2.1.0] - 2023-06-29

Expand Down
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,36 +164,36 @@ of the library in this way:

## CLI usage

### Commands and shortcuts

At the start of your application, cli is presenting a prompt with the
At the start of your application, the CLI presents a prompt with the
name you provided in the `Cli` constructor.
This means you're in the root menu.
This indicates you're in the root menu.

### Navigation

- **Enter a submenu:** Type the submenu name to enter it.
The prompt will change to reflect the current submenu.
- **Go back to parent menu:** Type the name of the parent menu or `..` to return.
- **Navigate history:** Use up and down arrow keys to navigate through previously entered commands.
- **Exit:** Type `exit` to terminate the CLI application.

To enter in a submenu, just type the submenu name:
you should see the prompt change to reflect the name of the current submenu.
### Commands in any menu

To go back to the parent menu, you must enter the name of the parent menu.
- `help`: Prints a list of available commands with descriptions.
- **Command execution:**
- **Current menu:** Enter the name of a command available in the current menu to execute it.
- **Submenu (full path):** Specify the complete path (separated by spaces) to a command within a submenu to execute it.

In any menu, you can enter:
- `exit`: to exit the cli
- `help`: to print the list of the commands available with a description
- a submenu name: to enter the submenu
- the parent menu name: to return to the parent menu
- a command in the current menu: to exec the command
- a command in a submenu (using the full path): to exec the command
### Autocompletion

You can also use:
- up and down arrow keys: to navigate the history of commands
- tab key: to autocomplete the command / menu
Use the Tab key to get suggestions for completing command or menu names as you type.

### Parameter parsing

The cli interpreter can manage correctly sentences using quote (') and double quote (").
Any character (spaces too) comprises between quotes or double quotes are considered as a single parameter of a command.
The characters ' and " can be used inside a command parameter by escaping them with a backslash.
The CLI interpreter can handle sentences using single quotes (`'`) and double quotes (`"`).
Any character (including spaces) enclosed within quotes is considered a single parameter for a command.
You can use quotes within parameters by escaping them with a backslash (`\`).

Some example:
**Examples:**

cli> echo "this is a single parameter"
this is a single parameter
Expand Down
96 changes: 73 additions & 23 deletions include/cli/cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ namespace cli

bool ExecParent(const std::vector<std::string>& cmdLine, CliSession& session)
{
return HandleCommand({Name(), ".."}, cmdLine, session);
return HandleCommand({Name(), ParentShortcut()}, cmdLine, session);
}

bool ScanCmds(const std::vector<std::string>& cmdLine, CliSession& session)
Expand Down Expand Up @@ -545,43 +545,88 @@ namespace cli
auto result = cli::GetCompletions(cmds, currentLine);
if (parent != nullptr)
{
auto c = parent->GetCompletionRecursive(currentLine);
auto c = parent->GetCompletionWithParent(currentLine);
result.insert(result.end(), std::make_move_iterator(c.begin()), std::make_move_iterator(c.end()));
}
return result;
}

// returns:
// - the completion of this menu command
// - the recursive completions of the subcommands
/**
* Retrieves completion suggestions for the user input recursively.
*
* This function checks if the user input starts with the current command's name. If it
* does, it extracts the remaining part of the input and retrieves suggestions:
* - From subcommands using their `GetCompletionRecursive` function.
* - From the parent command (if available) using its `GetCompletionRecursiveFull` function.
* - (Optional) You can customize the behavior for empty lines to provide top-level commands.
*
* If the input doesn't start with the command name, it delegates to the base class's
* `Command::GetCompletionRecursive` function for handling generic commands.
*
* @param line The user's input string (potentially incomplete command).
* @return A vector containing suggested completions for the user input.
*/
std::vector<std::string> GetCompletionRecursive(const std::string& line) const override
{
if (line.rfind(Name(), 0) == 0) // line starts_with Name()
{
auto rest = line;
rest.erase(0, Name().size());
// trim_left(rest);
rest.erase(rest.begin(), std::find_if(rest.begin(), rest.end(), [](int ch) { return !std::isspace(ch); }));
std::vector<std::string> result;
for (const auto& cmd: *cmds)
{
auto cs = cmd->GetCompletionRecursive(rest);
for (const auto& c: cs)
result.push_back(Name() + ' ' + c); // concat submenu with command
}
if (parent != nullptr)
{
auto cs = parent->GetCompletionRecursive(rest);
for (const auto& c: cs)
result.push_back(Name() + ' ' + c); // concat submenu with command
}
return result;
return GetCompletionRecursiveHelper(line, Name());
}

return Command::GetCompletionRecursive(line);
}

private:

/**
* Retrieves completion suggestions for the user input recursively, including the parent command.
*
* This function is similar to `GetCompletionRecursive` but explicitly includes
* completions from the parent command (if available) using its `GetCompletionRecursiveFull` function.
* This allows navigation within the command hierarchy using "..".
*
* The rest of the functionality remains the same as `GetCompletionRecursive`.
*
* @param line The user's input string (potentially incomplete command).
* @return A vector containing suggested completions for the user input.
*/
std::vector<std::string> GetCompletionWithParent(const std::string& line) const
{
if (line.rfind(Name(), 0) == 0) // line starts_with Name()
{
return GetCompletionRecursiveHelper(line, Name());
}

if (line.rfind(ParentShortcut(), 0) == 0) // line starts_with ..
{
return GetCompletionRecursiveHelper(line, ParentShortcut());
}

return Command::GetCompletionRecursive(line);
}

std::vector<std::string> GetCompletionRecursiveHelper(const std::string& line, const std::string& prefix) const
{
auto rest = line;
rest.erase(0, prefix.size());
// trim_left(rest);
rest.erase(rest.begin(), std::find_if(rest.begin(), rest.end(), [](int ch) { return !std::isspace(ch); }));
std::vector<std::string> result;
for (const auto& cmd: *cmds)
{
auto cs = cmd->GetCompletionRecursive(rest);
for (const auto& c: cs)
result.push_back(prefix + ' ' + c); // concat submenu with command
}
if (parent != nullptr)
{
auto cs = parent->GetCompletionWithParent(rest);
for (const auto& c: cs)
result.push_back(prefix + ' ' + c); // concat submenu with command
}
return result;
}

/**
* Handles a command from the user input.
*
Expand Down Expand Up @@ -631,6 +676,11 @@ namespace cli
return false;
}

static std::string ParentShortcut()
{
return "..";
}

template <typename F, typename R, typename ... Args>
CmdHandler Insert(const std::string& name, const std::string& help, const std::vector<std::string>& parDesc, F& f, R (F::*)(std::ostream& out, Args...) const);

Expand Down

0 comments on commit 3d7091e

Please sign in to comment.