From 821ce04915fa7f42fee77f626685ab983387073b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Sat, 31 Oct 2020 23:06:43 +0100 Subject: [PATCH] degesch: implement autocompletion for /set It was super annoying to just slightly modify strings and string arrays, now you can have existing values filled in. complete_word() looks a bit cleaner now as well. --- degesch.c | 110 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/degesch.c b/degesch.c index 4eeea23..8c035c6 100644 --- a/degesch.c +++ b/degesch.c @@ -12358,15 +12358,12 @@ completion_locate (struct completion *self, size_t offset) self->location = i - 1; } -static bool -completion_matches (struct completion *self, int word, const char *pattern) +static char * +completion_word (struct completion *self, int word) { hard_assert (word >= 0 && word < (int) self->words_len); - char *text = xstrndup (self->line + self->words[word].start, + return xstrndup (self->line + self->words[word].start, self->words[word].end - self->words[word].start); - bool result = !fnmatch (pattern, text, 0); - free (text); - return result; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -12461,6 +12458,66 @@ complete_option (struct app_context *ctx, struct completion *data, strv_free (&options); } +static void +complete_set_value (struct config_item *item, const char *word, + struct strv *output) +{ + struct str serialized = str_make (); + config_item_write (item, false, &serialized); + if (!strncmp (serialized.str, word, strlen (word))) + strv_append_owned (output, str_steal (&serialized)); + else + str_free (&serialized); +} + +static void +complete_set_value_array (struct config_item *item, const char *word, + struct strv *output) +{ + if (!item->schema || item->schema->type != CONFIG_ITEM_STRING_ARRAY) + return; + + struct strv items = strv_make (); + cstr_split (item->value.string.str, ",", false, &items); + for (size_t i = 0; i < items.len; i++) + { + struct str wrapped = str_make (), serialized = str_make (); + str_append (&wrapped, items.vector[i]); + config_item_write_string (&serialized, &wrapped); + str_free (&wrapped); + + if (!strncmp (serialized.str, word, strlen (word))) + strv_append_owned (output, str_steal (&serialized)); + else + str_free (&serialized); + } + strv_free (&items); +} + +static void +complete_set (struct app_context *ctx, struct completion *data, + const char *word, struct strv *output) +{ + if (data->location == 1) + { + complete_option (ctx, data, word, output); + return; + } + if (data->location != 3) + return; + + char *key = completion_word (data, 1); + struct config_item *item = config_item_get (ctx->config.root, key, NULL); + if (item) + { + char *op = completion_word (data, 2); + if (!strcmp (op, "-=")) complete_set_value_array (item, word, output); + if (!strcmp (op, "=")) complete_set_value (item, word, output); + free (op); + } + free (key); +} + static void complete_topic (struct app_context *ctx, struct completion *data, const char *word, struct strv *output) @@ -12514,33 +12571,30 @@ static char ** complete_word (struct app_context *ctx, struct completion *data, const char *word) { - // First figure out what exactly we need to complete - bool try_commands = false; - bool try_options = false; - bool try_topic = false; - bool try_nicknames = false; - - if (data->location == 0 && completion_matches (data, 0, "/*")) - try_commands = true; - else if (data->location == 1 && completion_matches (data, 0, "/set")) - try_options = true; - else if (data->location == 1 && completion_matches (data, 0, "/help")) - try_commands = try_options = true; - else if (data->location == 1 && completion_matches (data, 0, "/topic")) - try_topic = try_nicknames = true; - else - try_nicknames = true; + char *initial = completion_word (data, 0); + // Start with a placeholder for the longest common prefix struct strv words = strv_make (); - - // Add placeholder strv_append_owned (&words, NULL); - if (try_commands) complete_command (ctx, data, word, &words); - if (try_options) complete_option (ctx, data, word, &words); - if (try_topic) complete_topic (ctx, data, word, &words); - if (try_nicknames) complete_nicknames (ctx, data, word, &words); + if (data->location == 0 && *initial == '/') + complete_command (ctx, data, word, &words); + else if (data->location >= 1 && !strcmp (initial, "/set")) + complete_set (ctx, data, word, &words); + else if (data->location == 1 && !strcmp (initial, "/help")) + { + complete_command (ctx, data, word, &words); + complete_option (ctx, data, word, &words); + } + else if (data->location == 1 && !strcmp (initial, "/topic")) + { + complete_topic (ctx, data, word, &words); + complete_nicknames (ctx, data, word, &words); + } + else + complete_nicknames (ctx, data, word, &words); + cstr_set (&initial, NULL); LIST_FOR_EACH (struct hook, iter, ctx->completion_hooks) { struct completion_hook *hook = (struct completion_hook *) iter;