rework completion iterators
authorOlivier Matz <zer0@droids-corp.org>
Fri, 21 Feb 2020 20:54:27 +0000 (21:54 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Fri, 21 Feb 2020 20:55:34 +0000 (21:55 +0100)
examples/parse-yaml/parse-yaml.c
include/ecoli_complete.h
src/ecoli_complete.c
src/ecoli_editline.c
src/ecoli_node_sh_lex.c
src/ecoli_test.c

index d1c1a54..ff4ec74 100644 (file)
@@ -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;
index ebb5ad0..c36019a 100644 (file)
@@ -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
index 620ef18..b1afb33 100644 (file)
@@ -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)
index 6a6e23b..42083f8 100644 (file)
@@ -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) {
index 2461620..c6b6867 100644 (file)
@@ -15,6 +15,7 @@
 #include <ecoli_string.h>
 #include <ecoli_test.h>
 #include <ecoli_strvec.h>
+#include <ecoli_htable.h>
 #include <ecoli_node.h>
 #include <ecoli_parse.h>
 #include <ecoli_complete.h>
@@ -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;
 }
index 4429e67..3e7d625 100644 (file)
@@ -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 */