degesch: implement some autocompletion

This commit is contained in:
Přemysl Eric Janouch 2015-05-07 06:37:50 +02:00
parent 4ecf8d90da
commit 1d7903ae03
1 changed files with 107 additions and 7 deletions

114
degesch.c
View File

@ -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);
if (self->words_len == self->words_alloc)
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
@ -5098,6 +5098,17 @@ completion_locate (struct completion *self, size_t offset)
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
@ -5168,16 +5179,105 @@ utf8_common_prefix (const char **vector, size_t len)
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 **
complete_word (struct app_context *ctx, struct completion *data,
const char *word)
{
// TODO: return a list of matches with the longest common part
// (or a copy of "word" if none) as the first entry
// TODO: if there's only one match, don't bother computing the common part
char **result = xcalloc (2, sizeof *result);
result[0] = xstrdup_printf ("%shue", word);
return result;
// First figure out what exactly do we need to complete
bool try_commands = false;
bool try_options = 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
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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -