degesch: Lua: add autocomplete hooks
This commit is contained in:
parent
91f3bd60df
commit
f39e2a4bc8
166
degesch.c
166
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
|
||||
}
|
||||
|
@ -7675,6 +7710,9 @@ lua_hook_unhook (lua_State *L)
|
|||
case XLUA_HOOK_IRC:
|
||||
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);
|
||||
break;
|
||||
|
@ -7766,6 +7804,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)
|
||||
{
|
||||
|
@ -7832,6 +7959,18 @@ lua_plugin_hook_irc (lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -8521,6 +8661,7 @@ static luaL_Reg lua_plugin_library[] =
|
|||
{
|
||||
{ "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 },
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue