yanglint UPDATE autocompletion improvements
Refactored and no more hints are provided after
a valid value was written/autocompleted.
diff --git a/tools/lint/completion.c b/tools/lint/completion.c
index 13b00de..9e2b366 100644
--- a/tools/lint/completion.c
+++ b/tools/lint/completion.c
@@ -76,38 +76,6 @@
}
/**
- * @brief Check if the last command line argument is an option.
- *
- * @param[in] hint User input.
- * @return 1 if it is an option, 0 otherwise.
- */
-static int
-last_is_opt(const char *hint)
-{
- const char *ptr;
-
- /* last is option */
- if (hint[0] == '-') {
- return 1;
- }
-
- do {
- --hint;
- } while (hint[0] == ' ');
-
- /* last is option argument */
- ptr = strrchr(hint, ' ');
- if (ptr) {
- ++ptr;
- if (ptr[0] == '-') {
- return 1;
- }
- }
-
- return 0;
-}
-
-/**
* @brief Provides completion for module names.
*
* @param[in] hint User input.
@@ -310,29 +278,101 @@
free(module_name);
}
+/**
+ * @brief Get the string before the hint, which autocompletion is for.
+ *
+ * @param[in] buf Complete user input.
+ * @param[in] hint Hint part of the user input.
+ * @return Pointer to the last string.
+ */
+static const char *
+get_last_str(const char *buf, const char *hint)
+{
+ const char *ptr;
+
+ if (buf == hint) {
+ return buf;
+ }
+
+ ptr = hint - 1;
+ while (ptr[0] == ' ') {
+ --ptr;
+ if (buf == ptr) {
+ return buf;
+ }
+ }
+
+ while (ptr[-1] != ' ') {
+ --ptr;
+ if (buf == ptr) {
+ return buf;
+ }
+ }
+
+ return ptr;
+}
+
/* callback */
void
complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
{
+ struct autocomplete {
+ const char *cmd; /**< command */
+ const char *opt; /**< optional option */
+ int last_opt; /**< whether to autocomplete even if an option is last in the hint */
+
+ void (*ln_cb)(const char *, const char *, linenoiseCompletions *); /**< linenoise callback to call */
+ void (*yl_cb)(const char *, char ***, unsigned int *); /**< yanglint callback to call */
+ } ac[] = {
+ {"add", NULL, 1, linenoisePathCompletion, NULL},
+ {"searchpath", NULL, 0, linenoisePathCompletion, NULL},
+ {"data", NULL, 0, linenoisePathCompletion, NULL},
+ {"print", NULL, 0, NULL, get_model_completion},
+ {"feature", NULL, 0, NULL, get_model_completion},
+ {"print", "-P", 1, NULL, get_schema_completion},
+ };
+ size_t cmd_len;
+ const char *last;
char **matches = NULL;
unsigned int match_count = 0, i;
- if (!strncmp(buf, "add ", 4)) {
- linenoisePathCompletion(buf, hint, lc);
- } else if ((!strncmp(buf, "searchpath ", 11) || !strncmp(buf, "data ", 5) ||
- !strncmp(buf, "config ", 7) || !strncmp(buf, "filter ", 7) ||
- !strncmp(buf, "xpath ", 6) || !strncmp(buf, "clear ", 6)) && !last_is_opt(hint)) {
- linenoisePathCompletion(buf, hint, lc);
- } else if ((!strncmp(buf, "print ", 6) || !strncmp(buf, "feature ", 8)) && !last_is_opt(hint)) {
- if (!strncmp(buf, "print -f info -P ", 17)) {
- get_schema_completion(hint, &matches, &match_count);
- } else {
- get_model_completion(hint, &matches, &match_count);
- }
- } else if (!strchr(buf, ' ') && hint[0]) {
+ if (buf == hint) {
+ /* command autocomplete */
get_cmd_completion(hint, &matches, &match_count);
+
+ } else {
+ for (i = 0; i < (sizeof ac / sizeof *ac); ++i) {
+ cmd_len = strlen(ac[i].cmd);
+ if (strncmp(buf, ac[i].cmd, cmd_len) || (buf[cmd_len] != ' ')) {
+ /* not this command */
+ continue;
+ }
+
+ last = get_last_str(buf, hint);
+ if (ac[i].opt && strncmp(ac[i].opt, last, strlen(ac[i].opt))) {
+ /* autocompletion for (another) option */
+ continue;
+ }
+ if (!ac[i].last_opt && (last[0] == '-')) {
+ /* autocompletion for the command, not an option */
+ continue;
+ }
+ if ((last != buf) && (last[0] != '-')) {
+ /* autocompleted */
+ return;
+ }
+
+ /* callback */
+ if (ac[i].ln_cb) {
+ ac[i].ln_cb(buf, hint, lc);
+ } else {
+ ac[i].yl_cb(hint, &matches, &match_count);
+ }
+ break;
+ }
}
+ /* transform matches into autocompletion, if needed */
for (i = 0; i < match_count; ++i) {
linenoiseAddCompletion(lc, matches[i]);
free(matches[i]);