From: Olivier Matz Date: Fri, 21 Feb 2020 20:54:27 +0000 (+0100) Subject: rework completion iterators X-Git-Url: http://git.droids-corp.org/?p=protos%2Flibecoli.git;a=commitdiff_plain;h=67b4d8d2b2593e3e64a848ce548bc0fd76bc440a rework completion iterators --- diff --git a/examples/parse-yaml/parse-yaml.c b/examples/parse-yaml/parse-yaml.c index d1c1a54..ff4ec74 100644 --- a/examples/parse-yaml/parse-yaml.c +++ b/examples/parse-yaml/parse-yaml.c @@ -221,7 +221,6 @@ complete_words(const struct ec_node *node, int argc, char *argv[]) { struct ec_comp *comp = NULL; struct ec_strvec *strvec = NULL; - struct ec_comp_iter *iter = NULL; struct ec_comp_item *item = NULL; size_t count; @@ -239,13 +238,8 @@ complete_words(const struct ec_node *node, int argc, char *argv[]) count = ec_comp_count(comp, EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL); - iter = ec_comp_iter(comp, - EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL); - if (iter == NULL) - goto fail; - - while ((item = ec_comp_iter_next(iter)) != NULL) { - + EC_COMP_FOREACH(item, comp, EC_COMP_UNKNOWN | EC_COMP_FULL | + EC_COMP_PARTIAL) { /* only one match, display it fully */ if (count == 1) { printf("%s\n", ec_comp_item_get_str(item)); @@ -256,7 +250,6 @@ complete_words(const struct ec_node *node, int argc, char *argv[]) printf("%s\n", ec_comp_item_get_display(item)); } - ec_comp_iter_free(iter); ec_comp_free(comp); ec_strvec_free(strvec); return 0; diff --git a/include/ecoli_complete.h b/include/ecoli_complete.h index ebb5ad0..c36019a 100644 --- a/include/ecoli_complete.h +++ b/include/ecoli_complete.h @@ -280,36 +280,53 @@ unsigned int ec_comp_count( * * */ -struct ec_comp_iter { - enum ec_comp_type type; - const struct ec_comp *comp; - struct ec_comp_group *cur_node; - struct ec_comp_item *cur_match; -}; /** + * Get the first completion item matching the type. * + * Also see EC_COMP_FOREACH(). * - * + * @param comp + * The completion list. + * @param type + * A logical OR of flags among EC_COMP_UNKNOWN, EC_COMP_PARTIAL and + * EC_COMP_FULL, to select the type to iterate. + * @return + * A completion item. */ -struct ec_comp_iter * -ec_comp_iter(const struct ec_comp *comp, - enum ec_comp_type type); +struct ec_comp_item * +ec_comp_iter_first(const struct ec_comp *comp, enum ec_comp_type type); /** + * Get the first completion item matching the type. * + * Also see EC_COMP_FOREACH(). * - * + * @param comp + * The completion list. + * @param type + * A logical OR of flags among EC_COMP_UNKNOWN, EC_COMP_PARTIAL and + * EC_COMP_FULL, to select the type to iterate. + * @return + * A completion item. */ -struct ec_comp_item *ec_comp_iter_next( - struct ec_comp_iter *iter); +struct ec_comp_item * +ec_comp_iter_next(struct ec_comp_item *item, enum ec_comp_type type); /** - * - * - * + * Iterate items matching a given type. + * + * @param item + * The item that will be set at each iteration. + * @param comp + * The completion list. + * @param type + * A logical OR of flags among EC_COMP_UNKNOWN, EC_COMP_PARTIAL and + * EC_COMP_FULL, to select the type to iterate. */ -void ec_comp_iter_free(struct ec_comp_iter *iter); - +#define EC_COMP_FOREACH(item, comp, type) \ + for (item = ec_comp_iter_first(comp, type); \ + item != NULL; \ + item = ec_comp_iter_next(item, type)) #endif diff --git a/src/ecoli_complete.c b/src/ecoli_complete.c index 620ef18..b1afb33 100644 --- a/src/ecoli_complete.c +++ b/src/ecoli_complete.c @@ -628,76 +628,55 @@ unsigned int ec_comp_count( return count; } -struct ec_comp_iter * -ec_comp_iter(const struct ec_comp *comp, - enum ec_comp_type type) -{ - struct ec_comp_iter *iter; - - iter = ec_calloc(1, sizeof(*iter)); - if (iter == NULL) - return NULL; - - iter->comp = comp; - iter->type = type; - iter->cur_node = NULL; - iter->cur_match = NULL; - - return iter; -} - -struct ec_comp_item *ec_comp_iter_next( - struct ec_comp_iter *iter) +static struct ec_comp_item * +__ec_comp_iter_next(const struct ec_comp *comp, struct ec_comp_item *item, + enum ec_comp_type type) { - const struct ec_comp *comp; - struct ec_comp_group *cur_node; + struct ec_comp_group *cur_grp; struct ec_comp_item *cur_match; - if (iter == NULL) - return NULL; - comp = iter->comp; - if (comp == NULL) - return NULL; - - cur_node = iter->cur_node; - cur_match = iter->cur_match; - /* first call */ - if (cur_node == NULL) { - TAILQ_FOREACH(cur_node, &comp->groups, next) { - TAILQ_FOREACH(cur_match, &cur_node->items, next) { - if (cur_match != NULL && - cur_match->type & iter->type) - goto found; + if (item == NULL) { + TAILQ_FOREACH(cur_grp, &comp->groups, next) { + TAILQ_FOREACH(cur_match, &cur_grp->items, next) { + if (cur_match->type & type) + return cur_match; } } return NULL; - } else { + } + + cur_grp = item->grp; + cur_match = TAILQ_NEXT(item, next); + while (cur_match != NULL) { + if (cur_match->type & type) + return cur_match; cur_match = TAILQ_NEXT(cur_match, next); - if (cur_match != NULL && - cur_match->type & iter->type) - goto found; - cur_node = TAILQ_NEXT(cur_node, next); - while (cur_node != NULL) { - cur_match = TAILQ_FIRST(&cur_node->items); - if (cur_match != NULL && - cur_match->type & iter->type) - goto found; - cur_node = TAILQ_NEXT(cur_node, next); + } + cur_grp = TAILQ_NEXT(cur_grp, next); + while (cur_grp != NULL) { + TAILQ_FOREACH(cur_match, &cur_grp->items, next) { + if (cur_match->type & type) + return cur_match; } - return NULL; } -found: - iter->cur_node = cur_node; - iter->cur_match = cur_match; + return NULL; +} - return iter->cur_match; +struct ec_comp_item * +ec_comp_iter_next(struct ec_comp_item *item, enum ec_comp_type type) +{ + if (item == NULL) + return NULL; + return __ec_comp_iter_next(item->grp->comp, item, type); } -void ec_comp_iter_free(struct ec_comp_iter *iter) + +struct ec_comp_item * +ec_comp_iter_first(const struct ec_comp *comp, enum ec_comp_type type) { - ec_free(iter); + return __ec_comp_iter_next(comp, NULL, type); } /* LCOV_EXCL_START */ @@ -705,7 +684,6 @@ static int ec_comp_testcase(void) { struct ec_node *node = NULL; struct ec_comp *c = NULL; - struct ec_comp_iter *iter = NULL; struct ec_comp_item *item; FILE *f = NULL; char *buf = NULL; @@ -762,8 +740,7 @@ static int ec_comp_testcase(void) free(buf); buf = NULL; - iter = ec_comp_iter(c, EC_COMP_ALL); - item = ec_comp_iter_next(iter); + item = ec_comp_iter_first(c, EC_COMP_ALL); if (item == NULL) goto fail; @@ -777,7 +754,7 @@ static int ec_comp_testcase(void) !strcmp(ec_node_id(ec_comp_item_get_node(item)), "id_x"), "bad item node\n"); - item = ec_comp_iter_next(iter); + item = ec_comp_iter_next(item, EC_COMP_ALL); if (item == NULL) goto fail; @@ -791,17 +768,15 @@ static int ec_comp_testcase(void) !strcmp(ec_node_id(ec_comp_item_get_node(item)), "id_y"), "bad item node\n"); - item = ec_comp_iter_next(iter); + item = ec_comp_iter_next(item, EC_COMP_ALL); testres |= EC_TEST_CHECK(item == NULL, "should be the last item\n"); - ec_comp_iter_free(iter); ec_comp_free(c); ec_node_free(node); return testres; fail: - ec_comp_iter_free(iter); ec_comp_free(c); ec_node_free(node); if (f != NULL) diff --git a/src/ecoli_editline.c b/src/ecoli_editline.c index 6a6e23b..42083f8 100644 --- a/src/ecoli_editline.c +++ b/src/ecoli_editline.c @@ -311,16 +311,11 @@ void ec_editline_free_completions(char **matches, size_t len) ssize_t ec_editline_get_completions(const struct ec_comp *cmpl, char ***matches_out) { - const struct ec_comp_item *item; - struct ec_comp_iter *iter = NULL; + struct ec_comp_item *item; char **matches = NULL; size_t count = 0; - iter = ec_comp_iter(cmpl, EC_COMP_FULL | EC_COMP_PARTIAL); - if (iter == NULL) - goto fail; - - while ((item = ec_comp_iter_next(iter)) != NULL) { + EC_COMP_FOREACH(item, cmpl, EC_COMP_FULL | EC_COMP_PARTIAL) { char **tmp; tmp = realloc(matches, (count + 1) * sizeof(char *)); @@ -339,24 +334,18 @@ ec_editline_get_completions(const struct ec_comp *cmpl, char ***matches_out) fail: ec_editline_free_completions(matches, count); *matches_out = NULL; - ec_comp_iter_free(iter); return -1; } char * ec_editline_append_chars(const struct ec_comp *cmpl) { - const struct ec_comp_item *item; - struct ec_comp_iter *iter = NULL; + struct ec_comp_item *item; const char *append; char *ret = NULL; size_t n; - iter = ec_comp_iter(cmpl, EC_COMP_FULL | EC_COMP_PARTIAL); - if (iter == NULL) - goto fail; - - while ((item = ec_comp_iter_next(iter)) != NULL) { + EC_COMP_FOREACH(item, cmpl, EC_COMP_FULL | EC_COMP_PARTIAL) { append = ec_comp_item_get_completion(item); if (ret == NULL) { ret = ec_strdup(append); @@ -367,12 +356,10 @@ ec_editline_append_chars(const struct ec_comp *cmpl) ret[n] = '\0'; } } - ec_comp_iter_free(iter); return ret; fail: - ec_comp_iter_free(iter); ec_free(ret); return NULL; @@ -427,9 +414,8 @@ ssize_t ec_editline_get_helps(const struct ec_editline *editline, const char *line, const char *full_line, struct ec_editline_help **helps_out) { - struct ec_comp_iter *iter = NULL; const struct ec_comp_group *grp, *prev_grp = NULL; - const struct ec_comp_item *item; + struct ec_comp_item *item; struct ec_comp *cmpl = NULL; struct ec_pnode *parse = NULL; unsigned int count = 0; @@ -449,12 +435,6 @@ ec_editline_get_helps(const struct ec_editline *editline, const char *line, if (cmpl == NULL) //XXX log error goto fail; - /* let's display one contextual help per node */ - iter = ec_comp_iter(cmpl, - EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL); - if (iter == NULL) - goto fail; - helps = ec_calloc(1, sizeof(*helps)); if (helps == NULL) goto fail; @@ -467,7 +447,9 @@ ec_editline_get_helps(const struct ec_editline *editline, const char *line, goto fail; } - while ((item = ec_comp_iter_next(iter)) != NULL) { + /* let's display one contextual help per node */ + EC_COMP_FOREACH(item, cmpl, + EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL) { struct ec_editline_help *tmp = NULL; /* keep one help per group, skip other items */ @@ -486,14 +468,12 @@ ec_editline_get_helps(const struct ec_editline *editline, const char *line, count++; } - ec_comp_iter_free(iter); ec_comp_free(cmpl); *helps_out = helps; return count; fail: - ec_comp_iter_free(iter); ec_pnode_free(parse); ec_comp_free(cmpl); if (helps != NULL) { diff --git a/src/ecoli_node_sh_lex.c b/src/ecoli_node_sh_lex.c index 2461620..c6b6867 100644 --- a/src/ecoli_node_sh_lex.c +++ b/src/ecoli_node_sh_lex.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -262,10 +263,9 @@ ec_node_sh_lex_complete(const struct ec_node *node, const struct ec_strvec *strvec) { struct ec_node_sh_lex *priv = ec_node_priv(node); - struct ec_comp *tmp_comp = NULL; struct ec_strvec *new_vec = NULL; - struct ec_comp_iter *iter = NULL; struct ec_comp_item *item = NULL; + struct ec_htable *htable = NULL; char *new_str = NULL; const char *str; char missing_quote = '\0'; @@ -279,22 +279,27 @@ ec_node_sh_lex_complete(const struct ec_node *node, if (new_vec == NULL) goto fail; - /* we will store the completions in a temporary struct, because - * we want to update them (ex: add missing quotes) */ - tmp_comp = ec_comp(ec_comp_get_state(comp)); - if (tmp_comp == NULL) + /* let's store the existing full completions in a htable */ + htable = ec_htable(); + if (htable == NULL) goto fail; - ret = ec_complete_child(priv->child, tmp_comp, new_vec); + EC_COMP_FOREACH(item, comp, EC_COMP_FULL) { + if (ec_htable_set(htable, &item, sizeof(item), NULL, NULL) < 0) + goto fail; + } + + /* do the completion */ + ret = ec_complete_child(priv->child, comp, new_vec); if (ret < 0) goto fail; - /* add missing quote for full completions */ + /* add missing quote for any new full completions */ if (missing_quote != '\0') { - iter = ec_comp_iter(tmp_comp, EC_COMP_FULL); - if (iter == NULL) - goto fail; - while ((item = ec_comp_iter_next(iter)) != NULL) { + EC_COMP_FOREACH(item, comp, EC_COMP_FULL) { + if (ec_htable_has_key(htable, &item, sizeof(item))) + continue; + str = ec_comp_item_get_str(item); if (ec_asprintf(&new_str, "%c%s%c", missing_quote, str, missing_quote) < 0) { @@ -319,18 +324,15 @@ ec_node_sh_lex_complete(const struct ec_node *node, } } - ec_comp_iter_free(iter); ec_strvec_free(new_vec); - - ec_comp_merge(comp, tmp_comp); + ec_htable_free(htable); return 0; fail: - ec_comp_free(tmp_comp); - ec_comp_iter_free(iter); ec_strvec_free(new_vec); ec_free(new_str); + ec_htable_free(htable); return -1; } diff --git a/src/ecoli_test.c b/src/ecoli_test.c index 4429e67..3e7d625 100644 --- a/src/ecoli_test.c +++ b/src/ecoli_test.c @@ -129,7 +129,6 @@ int ec_test_check_complete(struct ec_node *tk, enum ec_comp_type type, ...) for (s = va_arg(ap, const char *); s != EC_VA_END; s = va_arg(ap, const char *)) { - struct ec_comp_iter *iter; const struct ec_comp_item *item; if (s == NULL) { @@ -140,8 +139,7 @@ int ec_test_check_complete(struct ec_node *tk, enum ec_comp_type type, ...) count++; /* only check matching completions */ - iter = ec_comp_iter(c, type); - while ((item = ec_comp_iter_next(iter)) != NULL) { + EC_COMP_FOREACH(item, c, type) { const char *str = ec_comp_item_get_str(item); if (str != NULL && strcmp(str, s) == 0) break; @@ -152,7 +150,6 @@ int ec_test_check_complete(struct ec_node *tk, enum ec_comp_type type, ...) "completion <%s> not in list\n", s); ret = -1; } - ec_comp_iter_free(iter); } /* check if we have more completions (or less) than expected */