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.
This commit is contained in:
parent
2fe3b95ecd
commit
821ce04915
110
degesch.c
110
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;
|
||||
|
|
Loading…
Reference in New Issue