aboutsummaryrefslogtreecommitdiff
path: root/degesch.c
diff options
context:
space:
mode:
Diffstat (limited to 'degesch.c')
-rw-r--r--degesch.c182
1 files changed, 156 insertions, 26 deletions
diff --git a/degesch.c b/degesch.c
index fe503aa..92669fd 100644
--- a/degesch.c
+++ b/degesch.c
@@ -1474,6 +1474,38 @@ struct irc_hook_vtable
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+struct completion_word
+{
+ size_t start; ///< Offset to start of word
+ size_t end; ///< Offset to end of word
+};
+
+struct completion
+{
+ char *line; ///< The line which is being completed
+
+ struct completion_word *words; ///< Word locations
+ size_t words_len; ///< Number of words
+ size_t words_alloc; ///< Number of words allocated
+
+ size_t location; ///< Which word is being completed
+};
+
+struct completion_hook
+{
+ struct hook super; ///< Common hook fields
+ struct completion_hook_vtable *vtable;
+};
+
+struct completion_hook_vtable
+{
+ /// Tries to add possible completions of "word" to "output"
+ void (*complete) (struct completion_hook *self,
+ struct completion *data, const char *word, struct str_vector *output);
+};
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
struct app_context
{
bool no_colors; ///< Disable attribute printing
@@ -1543,6 +1575,7 @@ struct app_context
struct plugin *plugins; ///< Loaded plugins
struct hook *input_hooks; ///< Input hooks
struct hook *irc_hooks; ///< IRC hooks
+ struct hook *completion_hooks; ///< Autocomplete hooks
}
*g_ctx;
@@ -7644,6 +7677,7 @@ enum lua_hook_type
XLUA_HOOK_DEFUNCT, ///< No longer functional
XLUA_HOOK_INPUT, ///< Input hook
XLUA_HOOK_IRC, ///< IRC hook
+ XLUA_HOOK_COMPLETION, ///< Autocomplete
XLUA_HOOK_TIMER, ///< One-shot timer
};
@@ -7657,6 +7691,7 @@ struct lua_hook
struct hook hook; ///< Hook base structure
struct input_hook input_hook; ///< Input hook
struct irc_hook irc_hook; ///< IRC hook
+ struct completion_hook c_hook; ///< Autocomplete hook
struct poller_timer timer; ///< Timer
}
@@ -7670,10 +7705,13 @@ lua_hook_unhook (lua_State *L)
switch (hook->type)
{
case XLUA_HOOK_INPUT:
- LIST_UNLINK (hook->plugin->ctx->input_hooks, &hook->data.hook);
+ LIST_UNLINK (hook->plugin->ctx->input_hooks, &hook->data.hook);
break;
case XLUA_HOOK_IRC:
- LIST_UNLINK (hook->plugin->ctx->irc_hooks, &hook->data.hook);
+ LIST_UNLINK (hook->plugin->ctx->irc_hooks, &hook->data.hook);
+ break;
+ case XLUA_HOOK_COMPLETION:
+ LIST_UNLINK (hook->plugin->ctx->completion_hooks, &hook->data.hook);
break;
case XLUA_HOOK_TIMER:
poller_timer_reset (&hook->data.timer);
@@ -7767,6 +7805,95 @@ struct irc_hook_vtable lua_irc_hook_vtable =
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
+lua_plugin_push_completion (lua_State *L, struct completion *data)
+{
+ lua_createtable (L, 0, 3);
+
+ lua_pushstring (L, data->line);
+ lua_setfield (L, -2, "line");
+
+ lua_createtable (L, data->words_len, 0);
+ for (size_t i = 0; i < data->words_len; i++)
+ {
+ lua_pushlstring (L, data->line + data->words[i].start,
+ data->words[i].end - data->words[i].start);
+ lua_rawseti (L, -2, i + 1);
+ }
+ lua_setfield (L, -2, "words");
+
+ lua_pushinteger (L, data->location);
+ lua_setfield (L, -2, "location");
+}
+
+static bool
+lua_completion_hook_process_value (lua_State *L, struct str_vector *output,
+ struct error **e)
+{
+ if (lua_type (L, -1) != LUA_TSTRING)
+ FAIL ("%s: %s", "invalid type", lua_typename (L, lua_type (L, -1)));
+
+ size_t len;
+ const char *value = lua_tolstring (L, -1, &len);
+ if (!utf8_validate (value, len))
+ FAIL ("must be valid UTF-8");
+
+ str_vector_add (output, value);
+ return true;
+}
+
+static bool
+lua_completion_hook_process (lua_State *L, struct str_vector *output,
+ struct error **e)
+{
+ if (lua_isnil (L, -1))
+ return true;
+ if (!lua_istable (L, -1))
+ FAIL ("must return either a table or nil");
+
+ bool success = true;
+ for (lua_Integer i = 1; success && lua_rawgeti (L, -1, i); i++)
+ if ((success = lua_completion_hook_process_value (L, output, e)))
+ lua_pop (L, 1);
+ lua_pop (L, 1);
+ return success;
+}
+
+static void
+lua_completion_hook_complete (struct completion_hook *self,
+ struct completion *data, const char *word, struct str_vector *output)
+{
+ struct lua_hook *hook =
+ CONTAINER_OF (self, struct lua_hook, data.c_hook);
+ struct lua_plugin *plugin = hook->plugin;
+ lua_State *L = plugin->L;
+
+ lua_rawgeti (L, LUA_REGISTRYINDEX, hook->ref_callback);
+ lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
+ lua_plugin_push_completion (L, data); // 2: data
+
+ lua_plugin_push_buffer (plugin, plugin->ctx->current_buffer);
+ lua_setfield (L, -2, "buffer");
+
+ lua_pushstring (L, word); // 3: word
+
+ struct error *e = NULL;
+ if (lua_plugin_call (plugin, 3, 1, &e))
+ {
+ lua_completion_hook_process (L, output, &e);
+ lua_pop (L, 1);
+ }
+ if (e)
+ lua_plugin_log_error (plugin, "autocomplete hook", e);
+}
+
+struct completion_hook_vtable lua_completion_hook_vtable =
+{
+ .complete = lua_completion_hook_complete,
+};
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static void
lua_timer_hook_dispatch (void *user_data)
{
struct lua_hook *hook = user_data;
@@ -7833,6 +7960,18 @@ lua_plugin_hook_irc (lua_State *L)
}
static int
+lua_plugin_hook_completion (lua_State *L)
+{
+ struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
+ struct lua_hook *hook = lua_plugin_push_hook
+ (plugin, 1, XLUA_HOOK_COMPLETION, luaL_optinteger (L, 2, 0));
+ hook->data.c_hook.vtable = &lua_completion_hook_vtable;
+ plugin->ctx->completion_hooks =
+ hook_insert (plugin->ctx->completion_hooks, &hook->data.hook);
+ return 1;
+}
+
+static int
lua_plugin_hook_timer (lua_State *L)
{
struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
@@ -8115,7 +8254,8 @@ lua_plugin_setup_config (lua_State *L)
if (lua_type (L, -2) != LUA_TSTRING
|| lua_type (L, -1) != LUA_TTABLE)
return luaL_error (L, "%s: %s -> %s", "invalid types",
- lua_typename (L, -2), lua_typename (L, -1));
+ lua_typename (L, lua_type (L, -2)),
+ lua_typename (L, lua_type (L, -1)));
lua_plugin_add_config_schema (plugin, subtree, lua_tostring (L, -2));
}
@@ -8519,12 +8659,13 @@ lua_plugin_connect (lua_State *L)
static luaL_Reg lua_plugin_library[] =
{
- { "hook_input", lua_plugin_hook_input },
- { "hook_irc", lua_plugin_hook_irc },
- { "hook_timer", lua_plugin_hook_timer },
- { "setup_config", lua_plugin_setup_config },
- { "connect", lua_plugin_connect },
- { NULL, NULL },
+ { "hook_input", lua_plugin_hook_input },
+ { "hook_irc", lua_plugin_hook_irc },
+ { "hook_completion", lua_plugin_hook_completion },
+ { "hook_timer", lua_plugin_hook_timer },
+ { "setup_config", lua_plugin_setup_config },
+ { "connect", lua_plugin_connect },
+ { NULL, NULL },
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -10440,23 +10581,6 @@ process_input (struct app_context *ctx, char *user_input)
// The amount of crap that goes into this is truly insane.
// It's mostly because of Editline's total ignorance of this task.
-struct completion_word
-{
- size_t start; ///< Offset to start of word
- size_t end; ///< Offset to end of word
-};
-
-struct completion
-{
- char *line; ///< The line which is being completed
-
- struct completion_word *words; ///< Word locations
- size_t words_len; ///< Number of words
- size_t words_alloc; ///< Number of words allocated
-
- size_t location; ///< Which word is being completed
-};
-
static void
completion_init (struct completion *self)
{
@@ -10698,6 +10822,12 @@ complete_word (struct app_context *ctx, struct completion *data,
if (try_topic) complete_topic (ctx, data, word, &words);
if (try_nicknames) complete_nicknames (ctx, data, word, &words);
+ LIST_FOR_EACH (struct hook, iter, ctx->completion_hooks)
+ {
+ struct completion_hook *hook = (struct completion_hook *) iter;
+ hook->vtable->complete (hook, data, word, &words);
+ }
+
if (words.len == 1)
{
// Nothing matched