You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2017/05/17 17:14:44 UTC

[4/8] incubator-mynewt-core git commit: sys/shell: add support for tab completion

sys/shell: add support for tab completion


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/038dc7f4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/038dc7f4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/038dc7f4

Branch: refs/heads/master
Commit: 038dc7f4cc95805eac9684d9be011d24b8ad3fc4
Parents: 98eebe0
Author: Michał Narajowski <mi...@codecoup.pl>
Authored: Wed May 3 10:59:23 2017 +0200
Committer: Michał Narajowski <mi...@codecoup.pl>
Committed: Wed May 3 12:12:04 2017 +0200

----------------------------------------------------------------------
 sys/shell/src/shell.c | 405 +++++++++++++++++++++++++++++++++++++++++++++
 sys/shell/syscfg.yml  |   3 +
 2 files changed, 408 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/038dc7f4/sys/shell/src/shell.c
----------------------------------------------------------------------
diff --git a/sys/shell/src/shell.c b/sys/shell/src/shell.c
index fa5a8f5..d186aa7 100644
--- a/sys/shell/src/shell.c
+++ b/sys/shell/src/shell.c
@@ -401,6 +401,407 @@ shell(struct os_event *ev)
     os_eventq_put(&avail_queue, ev);
 }
 
+#if MYNEWT_VAL(SHELL_COMPLETION)
+static void
+print_command_params(const int module, const int command)
+{
+    const struct shell_module *shell_module = &shell_modules[module];
+    const struct shell_cmd *shell_cmd = &shell_module->commands[command];
+    int i;
+
+    if (!(shell_cmd->help && shell_cmd->help->params)) {
+        return;
+    }
+
+    for (i = 0; shell_cmd->help->params[i].param_name; i++) {
+        console_printf("%-30s%s\n", shell_cmd->help->params[i].param_name,
+                       shell_cmd->help->params[i].help);
+    }
+}
+
+static int
+get_command_from_module(const char *command, int len, int module)
+{
+    int i;
+    const struct shell_module *shell_module;
+
+    shell_module = &shell_modules[module];
+    for (i = 0; shell_module->commands[i].sc_cmd; i++) {
+        if (!strncmp(command, shell_module->commands[i].sc_cmd, len)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static int
+get_token(char **cur, int *null_terminated)
+{
+    char *str = *cur;
+
+    *null_terminated = 0;
+    /* remove ' ' at the beginning */
+    while (*str && *str == ' ') {
+        str++;
+    }
+
+    if (!str) {
+        *null_terminated = 1;
+        return 0;
+    }
+
+    *cur = str;
+    str = strchr(str, ' ');
+
+    if (str == NULL) {
+        *null_terminated = 1;
+        return strlen(*cur);
+    }
+
+    return str - *cur;
+}
+
+static int
+get_last_token(char **cur)
+{
+    *cur = strrchr(*cur, ' ');
+    if (*cur == NULL) {
+        return 0;
+    }
+    (*cur)++;
+    return strlen(*cur);
+}
+
+static int
+complete_param(char *line, uint8_t len, const char *param_prefix,
+                 int param_len, int module_idx, int command_idx)
+{
+    const char *first_match = NULL;
+    int i, common_chars = -1;
+    const struct shell_cmd *command;
+
+    command = &shell_modules[module_idx].commands[command_idx];
+
+    if (!(command->help && command->help->params)) {
+        return 0;
+    }
+
+    for (i = 0; command->help->params[i].param_name; i++) {
+        int j;
+
+        if (strncmp(param_prefix,
+            command->help->params[i].param_name, param_len)) {
+            continue;
+        }
+
+        if (!first_match) {
+            first_match = command->help->params[i].param_name;
+            continue;
+        }
+
+        /* more commands match, print first match */
+        if (first_match && (common_chars < 0)) {
+            console_printf("\n");
+            console_printf("%s\n", first_match);
+            common_chars = strlen(first_match);
+        }
+
+        /* cut common part of matching names */
+        for (j = 0; j < common_chars; j++) {
+            if (first_match[j] != command->help->params[i].param_name[j]) {
+                break;
+            }
+        }
+
+        common_chars = j;
+
+        console_printf("%s\n", command->help->params[i].param_name);
+    }
+
+    /* no match, do nothing */
+    if (!first_match) {
+        return 0;
+    }
+
+    if (common_chars >= 0) {
+        /* multiple match, restore prompt */
+        console_printf("%s", get_prompt());
+        console_printf("%s", line);
+    } else {
+        common_chars = strlen(first_match);
+    }
+
+    /* complete common part */
+    for (i = param_len; i < common_chars; i++) {
+        console_printf("%c", first_match[i]);
+        line[len++] = first_match[i];
+    }
+
+    return common_chars - param_len;
+}
+
+static int
+complete_command(char *line, uint8_t len, char *command_prefix,
+                 int command_len, int module_idx)
+{
+    const char *first_match = NULL;
+    int i, common_chars = -1, space = 0;
+    const struct shell_module *module;
+
+    module = &shell_modules[module_idx];
+
+    for (i = 0; module->commands[i].sc_cmd; i++) {
+        int j;
+
+        if (strncmp(command_prefix,
+            module->commands[i].sc_cmd, command_len)) {
+            continue;
+        }
+
+        if (!first_match) {
+            first_match = module->commands[i].sc_cmd;
+            continue;
+        }
+
+        /* more commands match, print first match */
+        if (first_match && (common_chars < 0)) {
+            console_printf("\n");
+            console_printf("%s\n", first_match);
+            common_chars = strlen(first_match);
+        }
+
+        /* cut common part of matching names */
+        for (j = 0; j < common_chars; j++) {
+            if (first_match[j] != module->commands[i].sc_cmd[j]) {
+                break;
+            }
+        }
+
+        common_chars = j;
+
+        console_printf("%s\n", module->commands[i].sc_cmd);
+    }
+
+    /* no match, do nothing */
+    if (!first_match) {
+        return 0;
+    }
+
+    if (common_chars >= 0) {
+        /* multiple match, restore prompt */
+        console_printf("%s", get_prompt());
+        console_printf("%s", line);
+    } else {
+        common_chars = strlen(first_match);
+        space = 1;
+    }
+
+    /* complete common part */
+    for (i = command_len; i < common_chars; i++) {
+        console_printf("%c", first_match[i]);
+        line[len++] = first_match[i];
+    }
+
+    /* for convenience add space after command */
+    if (space) {
+        console_printf(" ");
+        line[len] = ' ';
+    }
+
+    return common_chars - command_len + space;
+}
+
+static int
+complete_module(char *line, int len, char *module_prefix, int module_len)
+{
+    int i;
+    const char *first_match = NULL;
+    int common_chars = -1, space = 0;
+
+    if (!module_len) {
+        console_printf("\n");
+        for (i = 0; i < num_of_shell_entities; i++) {
+            console_printf("%s\n", shell_modules[i].module_name);
+        }
+        console_printf("%s", get_prompt());
+        console_printf("%s", line);
+        return 0;
+    }
+
+    for (i = 0; i < num_of_shell_entities; i++) {
+        int j;
+
+        if (strncmp(module_prefix,
+                     shell_modules[i].module_name,
+                     module_len)) {
+            continue;
+        }
+
+        if (!first_match) {
+            first_match = shell_modules[i].module_name;
+            continue;
+        }
+
+        /* more commands match, print first match */
+        if (first_match && (common_chars < 0)) {
+            console_printf("\n");
+            console_printf("%s\n", first_match);
+            common_chars = strlen(first_match);
+        }
+
+        /* cut common part of matching names */
+        for (j = 0; j < common_chars; j++) {
+            if (first_match[j] != shell_modules[i].module_name[j]) {
+                break;
+            }
+        }
+
+        common_chars = j;
+
+        console_printf("%s\n", shell_modules[i].module_name);
+    }
+
+    /* no match, do nothing */
+    if (!first_match) {
+        return 0;
+    }
+
+    if (common_chars >= 0) {
+        /* multiple match, restore prompt */
+        console_printf("%s", get_prompt());
+        console_printf("%s", line);
+    } else {
+        common_chars = strlen(first_match);
+        space = 1;
+    }
+
+    /* complete common part */
+    for (i = module_len; i < common_chars; i++) {
+        console_printf("%c", first_match[i]);
+        line[len++] = first_match[i];
+    }
+
+    /* for convenience add space after command */
+    if (space) {
+        console_printf(" ");
+        line[len] = ' ';
+    }
+
+    return common_chars - module_len + space;
+}
+
+static int
+complete_select(char *line, int len, char *cur, int tok_len)
+{
+    int null_terminated = 0;
+    cur += tok_len + 1;
+    tok_len = get_token(&cur, &null_terminated);
+    if (tok_len == 0) {
+        if (default_module != -1) {
+            return 0;
+        }
+        console_printf("\n");
+        print_modules();
+        console_printf("%s", get_prompt());
+        console_printf("%s", line);
+        return 0;
+    }
+
+    if (null_terminated) {
+        if (default_module == -1) {
+            return complete_module(line, len, cur, tok_len);
+        }
+    }
+    return 0;
+}
+
+static uint8_t
+completion(char *line, uint8_t len)
+{
+    char *cur;
+    int tok_len;
+    int module, command;
+    int null_terminated = 0;
+
+    /*
+     * line to completion is not ended by '\0' as the line that gets from
+     * os_eventq_get function
+     */
+    line[len] = '\0';
+
+    cur = line;
+    tok_len = get_token(&cur, &null_terminated);
+
+    /* empty token - print options */
+    if (tok_len == 0) {
+        console_printf("\n");
+        if (default_module == -1) {
+            print_modules();
+        } else {
+            print_module_commands(default_module);
+        }
+        console_printf("%s", get_prompt());
+        console_printf("%s", line);
+        return 0;
+    }
+
+    /* token can be completed */
+    if (null_terminated) {
+        if (default_module == -1) {
+            return complete_module(line, len, cur, tok_len);
+        }
+        return complete_command(line, len, cur, tok_len, default_module);
+    }
+
+    if (strncmp("select", cur, tok_len) == 0) {
+        return complete_select(line, len, cur, tok_len);
+    }
+
+    if (default_module != -1) {
+        module = default_module;
+    } else {
+        module = get_destination_module(cur, tok_len);
+
+        if (module == -1) {
+            return 0;
+        }
+
+        cur += tok_len + 1;
+        tok_len = get_token(&cur, &null_terminated);
+
+        if (tok_len == 0) {
+            console_printf("\n");
+            print_module_commands(module);
+            console_printf("%s", get_prompt());
+            console_printf("%s", line);
+            return 0;
+        }
+
+        if (null_terminated) {
+            return complete_command(line, len, cur, tok_len, module);
+        }
+    }
+
+
+
+    command = get_command_from_module(cur, tok_len, module);
+    if (command == -1) {
+        return 0;
+    }
+
+    cur += tok_len;
+    tok_len = get_last_token(&cur);
+    if (tok_len == 0) {
+        console_printf("\n");
+        print_command_params(module, command);
+        console_printf("%s", get_prompt());
+        console_printf("%s", line);
+        return 0;
+    }
+    return complete_param(line, len, cur, tok_len, module, command);
+}
+#endif /* MYNEWT_VAL(SHELL_COMPLETION) */
+
 void
 shell_register_app_cmd_handler(shell_cmd_func_t handler)
 {
@@ -492,6 +893,10 @@ shell_init(void)
     prompt = SHELL_PROMPT;
     console_set_queues(&avail_queue, os_eventq_dflt_get());
 
+#if MYNEWT_VAL(SHELL_COMPLETION)
+    console_set_completion_cb(completion);
+#endif
+
 #if MYNEWT_VAL(SHELL_OS_MODULE)
     shell_os_register(shell_register);
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/038dc7f4/sys/shell/syscfg.yml
----------------------------------------------------------------------
diff --git a/sys/shell/syscfg.yml b/sys/shell/syscfg.yml
index b3092f5..e4d78f8 100644
--- a/sys/shell/syscfg.yml
+++ b/sys/shell/syscfg.yml
@@ -41,6 +41,9 @@ syscfg.defs:
     SHELL_MAX_COMPAT_COMMANDS:
         description: 'Max number of compatibility commands'
         value: 10
+    SHELL_COMPLETION:
+        description: 'Include completion functionality'
+        value: 1
 
     SHELL_OS_MODULE:
         description: 'Include shell os module'