degesch: implement some autocompletion
This commit is contained in:
parent
4ecf8d90da
commit
1d7903ae03
114
degesch.c
114
degesch.c
|
@ -5065,7 +5065,7 @@ completion_add_word (struct completion *self, size_t start, size_t end)
|
||||||
self->words = xcalloc ((self->words_alloc = 4), sizeof *self->words);
|
self->words = xcalloc ((self->words_alloc = 4), sizeof *self->words);
|
||||||
if (self->words_len == self->words_alloc)
|
if (self->words_len == self->words_alloc)
|
||||||
self->words = xrealloc (self->words, (self->words_alloc <<= 1));
|
self->words = xrealloc (self->words, (self->words_alloc <<= 1));
|
||||||
self->words[self->words_len] = (struct completion_word) { start, end };
|
self->words[self->words_len++] = (struct completion_word) { start, end };
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -5098,6 +5098,17 @@ completion_locate (struct completion *self, size_t offset)
|
||||||
self->location = i - 1;
|
self->location = i - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
completion_matches (struct completion *self, int word, const char *pattern)
|
||||||
|
{
|
||||||
|
hard_assert (word >= 0 && word < (int) self->words_len);
|
||||||
|
char *text = 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;
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
struct utf8_iter
|
struct utf8_iter
|
||||||
|
@ -5168,16 +5179,105 @@ utf8_common_prefix (const char **vector, size_t len)
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
complete_command (struct app_context *ctx, struct completion *data,
|
||||||
|
const char *word, struct str_vector *output)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
(void) data;
|
||||||
|
|
||||||
|
const char *prefix = "";
|
||||||
|
if (*word == '/')
|
||||||
|
{
|
||||||
|
word++;
|
||||||
|
prefix = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < N_ELEMENTS (g_command_handlers); i++)
|
||||||
|
{
|
||||||
|
struct command_handler *handler = &g_command_handlers[i];
|
||||||
|
// FIXME: we want an ASCII version
|
||||||
|
if (!strncasecmp (word, handler->name, strlen (word)))
|
||||||
|
str_vector_add_owned (output,
|
||||||
|
xstrdup_printf ("%s%s", prefix, handler->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
complete_option (struct app_context *ctx, struct completion *data,
|
||||||
|
const char *word, struct str_vector *output)
|
||||||
|
{
|
||||||
|
(void) data;
|
||||||
|
|
||||||
|
struct str_vector options;
|
||||||
|
str_vector_init (&options);
|
||||||
|
|
||||||
|
// Wildcard expansion is an interesting side-effect
|
||||||
|
char *x = xstrdup_printf ("%s*", word);
|
||||||
|
dump_matching_options (ctx, x, &options);
|
||||||
|
free (x);
|
||||||
|
|
||||||
|
str_vector_add_vector (output, options.vector);
|
||||||
|
str_vector_free (&options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
complete_nicknames (struct app_context *ctx, struct completion *data,
|
||||||
|
const char *word, struct str_vector *output)
|
||||||
|
{
|
||||||
|
// TODO; if (data->location == 0) --> add colons to nicknames
|
||||||
|
}
|
||||||
|
|
||||||
static char **
|
static char **
|
||||||
complete_word (struct app_context *ctx, struct completion *data,
|
complete_word (struct app_context *ctx, struct completion *data,
|
||||||
const char *word)
|
const char *word)
|
||||||
{
|
{
|
||||||
// TODO: return a list of matches with the longest common part
|
// First figure out what exactly do we need to complete
|
||||||
// (or a copy of "word" if none) as the first entry
|
bool try_commands = false;
|
||||||
// TODO: if there's only one match, don't bother computing the common part
|
bool try_options = false;
|
||||||
char **result = xcalloc (2, sizeof *result);
|
bool try_nicknames = false;
|
||||||
result[0] = xstrdup_printf ("%shue", word);
|
|
||||||
return result;
|
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
|
||||||
|
try_nicknames = true;
|
||||||
|
|
||||||
|
struct str_vector words;
|
||||||
|
str_vector_init (&words);
|
||||||
|
|
||||||
|
// Add placeholder
|
||||||
|
str_vector_add_owned (&words, NULL);
|
||||||
|
|
||||||
|
if (try_commands) complete_command (ctx, data, word, &words);
|
||||||
|
if (try_options) complete_option (ctx, data, word, &words);
|
||||||
|
if (try_nicknames) complete_nicknames (ctx, data, word, &words);
|
||||||
|
|
||||||
|
if (words.len == 1)
|
||||||
|
{
|
||||||
|
// Nothing matched
|
||||||
|
str_vector_free (&words);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (words.len == 2)
|
||||||
|
{
|
||||||
|
words.vector[0] = words.vector[1];
|
||||||
|
words.vector[1] = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t prefix = utf8_common_prefix
|
||||||
|
((const char **) words.vector + 1, words.len - 1);
|
||||||
|
if (!prefix)
|
||||||
|
words.vector[0] = xstrdup (word);
|
||||||
|
else
|
||||||
|
words.vector[0] = xstrndup (words.vector[1], prefix);
|
||||||
|
}
|
||||||
|
return words.vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
Loading…
Reference in New Issue