From 3060e07b71fd4dd6203769d73c4f8f21709f9b9c Mon Sep 17 00:00:00 2001 From: Yoann Desmouceaux Date: Thu, 18 May 2017 11:00:48 +0200 Subject: 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 --- src/vlib/cli.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'src/vlib/cli.c') diff --git a/src/vlib/cli.c b/src/vlib/cli.c index 5a0f7ec3c10..9e14bee219e 100644 --- a/src/vlib/cli.c +++ b/src/vlib/cli.c @@ -40,6 +40,7 @@ #include #include #include +#include /* Root of all show commands. */ /* *INDENT-OFF* */ @@ -235,6 +236,107 @@ unformat_vlib_cli_sub_command (unformat_input_t * i, va_list * args) return is_unique; } +static int +vlib_cli_cmp_strings (void *a1, void *a2) +{ + u8 *c1 = *(u8 **) a1; + u8 *c2 = *(u8 **) a2; + + return vec_cmp (c1, c2); +} + +u8 ** +vlib_cli_get_possible_completions (u8 * str) +{ + vlib_cli_command_t *c; + vlib_cli_sub_command_t *sc; + vlib_main_t *vm = vlib_get_main (); + vlib_cli_main_t *vcm = &vm->cli_main; + uword *match_bitmap = 0; + uword index, is_unique, help_next_level; + u8 **result = 0; + unformat_input_t input; + unformat_init_vector (&input, vec_dup (str)); + c = vec_elt_at_index (vcm->commands, 0); + + /* remove trailing whitespace, except for one of them */ + while (vec_len (input.buffer) >= 2 && + isspace (input.buffer[vec_len (input.buffer) - 1]) && + isspace (input.buffer[vec_len (input.buffer) - 2])) + { + vec_del1 (input.buffer, vec_len (input.buffer) - 1); + } + + /* if input is empty, directly return list of root commands */ + if (vec_len (input.buffer) == 0 || + (vec_len (input.buffer) == 1 && isspace (input.buffer[0]))) + { + vec_foreach (sc, c->sub_commands) + { + vec_add1 (result, (u8 *) sc->name); + } + goto done; + } + + /* add a trailing '?' so that vlib_cli_sub_command_match can find + * all commands starting with the input string */ + vec_add1 (input.buffer, '?'); + + while (1) + { + match_bitmap = vlib_cli_sub_command_match (c, &input); + /* no match: return no result */ + if (match_bitmap == 0) + { + goto done; + } + is_unique = clib_bitmap_count_set_bits (match_bitmap) == 1; + /* unique match: try to step one subcommand level further */ + if (is_unique) + { + /* stop if no more input */ + if (input.index >= vec_len (input.buffer) - 1) + { + break; + } + + index = clib_bitmap_first_set (match_bitmap); + c = get_sub_command (vcm, c, index); + clib_bitmap_free (match_bitmap); + continue; + } + /* multiple matches: stop here, return all matches */ + break; + } + + /* remove trailing '?' */ + vec_del1 (input.buffer, vec_len (input.buffer) - 1); + + /* if we have a space at the end of input, and a unique match, + * autocomplete the next level of subcommands */ + help_next_level = (vec_len (str) == 0) || isspace (str[vec_len (str) - 1]); + /* *INDENT-OFF* */ + clib_bitmap_foreach(index, match_bitmap, { + if (help_next_level && is_unique) { + c = get_sub_command (vcm, c, index); + vec_foreach (sc, c->sub_commands) { + vec_add1 (result, (u8*) sc->name); + } + goto done; /* break doesn't work in this macro-loop */ + } + sc = &c->sub_commands[index]; + vec_add1(result, (u8*) sc->name); + }); + /* *INDENT-ON* */ + +done: + clib_bitmap_free (match_bitmap); + unformat_free (&input); + + vec_sort_with_function (result, vlib_cli_cmp_strings); + return result; +} + static u8 * format_vlib_cli_command_help (u8 * s, va_list * args) { -- cgit 1.2.3-korg