summaryrefslogtreecommitdiffstats
path: root/src/vlib/unix
diff options
context:
space:
mode:
authorYoann Desmouceaux <ydesmouc@cisco.com>2017-05-18 11:00:48 +0200
committerChris Luke <chris_luke@comcast.com>2017-05-23 17:55:11 +0000
commit3060e07b71fd4dd6203769d73c4f8f21709f9b9c (patch)
tree5f1750c2c0e2c97fd141064655ecebeb609682d1 /src/vlib/unix
parent8c4611b39162da9753caaf654741faa115eaf612 (diff)
Add TAB-based auto-completion to the CLI
Hitting tab: - in the middle of a uniquely defined subcommand will expand the subcommand - in the middle of a non-uniquely defined (or empty) subcommand will display all possible subcommands, and possibly expand to the lowest common prefix Change-Id: Ib858eefdb0353cd2c3aad472799d15cd537455a0 Signed-off-by: Yoann Desmouceaux <ydesmouc@cisco.com>
Diffstat (limited to 'src/vlib/unix')
-rw-r--r--src/vlib/unix/cli.c153
1 files changed, 153 insertions, 0 deletions
diff --git a/src/vlib/unix/cli.c b/src/vlib/unix/cli.c
index 88e2453cff2..cf524213f5b 100644
--- a/src/vlib/unix/cli.c
+++ b/src/vlib/unix/cli.c
@@ -1230,6 +1230,8 @@ unix_cli_line_process_one (unix_cli_main_t * cm,
u8 input, unix_cli_parse_action_t action)
{
u8 *prev;
+ u8 *save = 0;
+ u8 **possible_commands;
int j, delta;
switch (action)
@@ -1553,6 +1555,157 @@ unix_cli_line_process_one (unix_cli_main_t * cm,
break;
case UNIX_CLI_PARSE_ACTION_TAB:
+ if (cf->cursor < vec_len (cf->current_command))
+ {
+ /* if we are in the middle of a line, complete only if
+ * the cursor points to whitespace */
+ if (isspace (cf->current_command[cf->cursor]))
+ {
+ /* save and clear any input that is after the cursor */
+ vec_resize (save, vec_len (cf->current_command) - cf->cursor);
+ clib_memcpy (save, cf->current_command + cf->cursor,
+ vec_len (cf->current_command) - cf->cursor);
+ _vec_len (cf->current_command) = cf->cursor;
+ }
+ else
+ {
+ unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
+ break;
+ }
+ }
+ possible_commands =
+ vlib_cli_get_possible_completions (cf->current_command);
+ if (vec_len (possible_commands) == 1)
+ {
+ u32 j = cf->cursor;
+ u8 *completed = possible_commands[0];
+
+ /* find the last word of current_command */
+ while (j >= 1 && !isspace (cf->current_command[j - 1]))
+ {
+ j--;
+ unix_vlib_cli_output_raw (cf, uf, (u8 *) "\b", 1);
+ }
+ _vec_len (cf->current_command) = j;
+
+ /* replace it with the newly expanded command */
+ vec_append (cf->current_command, completed);
+
+ /* echo to the terminal */
+ unix_vlib_cli_output_raw (cf, uf, completed, vec_len (completed));
+
+ /* add one trailing space if needed */
+ if (vec_len (save) == 0)
+ {
+ vec_add1 (cf->current_command, ' ');
+ unix_vlib_cli_output_raw (cf, uf, (u8 *) " ", 1);
+ }
+
+ cf->cursor = vec_len (cf->current_command);
+
+ }
+ else if (vec_len (possible_commands) >= 2)
+ {
+ u8 **possible_command;
+ uword max_command_len = 0, min_command_len = ~0;
+ u32 i, j;
+
+ vec_foreach (possible_command, possible_commands)
+ {
+ if (vec_len (*possible_command) > max_command_len)
+ {
+ max_command_len = vec_len (*possible_command);
+ }
+ if (vec_len (*possible_command) < min_command_len)
+ {
+ min_command_len = vec_len (*possible_command);
+ }
+ }
+
+ unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
+
+ i = 0;
+ vec_foreach (possible_command, possible_commands)
+ {
+ if (i + max_command_len >= cf->width)
+ {
+ unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
+ i = 0;
+ }
+ unix_vlib_cli_output_raw (cf, uf, *possible_command,
+ vec_len (*possible_command));
+ for (j = vec_len (*possible_command); j < max_command_len + 2;
+ j++)
+ {
+ unix_vlib_cli_output_raw (cf, uf, (u8 *) " ", 1);
+ }
+ i += max_command_len + 2;
+ }
+
+ unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
+
+ /* rewrite prompt */
+ unix_cli_cli_prompt (cf, uf);
+ unix_vlib_cli_output_raw (cf, uf, cf->current_command,
+ vec_len (cf->current_command));
+
+ /* count length of last word */
+ j = cf->cursor;
+ i = 0;
+ while (j >= 1 && !isspace (cf->current_command[j - 1]))
+ {
+ j--;
+ i++;
+ }
+
+ /* determine smallest common command */
+ for (; i < min_command_len; i++)
+ {
+ u8 common = '\0';
+ int stop = 0;
+ vec_foreach (possible_command, possible_commands)
+ {
+ if (common == '\0')
+ {
+ common = (*possible_command)[i];
+ }
+ else if (common != (*possible_command)[i])
+ {
+ stop = 1;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ vec_add1 (cf->current_command, common);
+ cf->cursor++;
+ unix_vlib_cli_output_raw (cf, uf, (u8 *) & common, 1);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
+ }
+
+ if (vec_len (save) > 0)
+ {
+ /* restore remaining input if tab was hit in the middle of a line */
+ unix_vlib_cli_output_raw (cf, uf, save, vec_len (save));
+ for (j = 0; j < vec_len (save); j++)
+ {
+ unix_vlib_cli_output_raw (cf, uf, (u8 *) "\b", 1);
+ }
+ vec_append (cf->current_command, save);
+ vec_free (save);
+ }
+ vec_free (possible_commands);
+
+ break;
case UNIX_CLI_PARSE_ACTION_YANK:
/* TODO */
break;