From 3f1c3ee80e46f80d7f1642009c4f9dffa404d935 Mon Sep 17 00:00:00 2001 From: Max Schillinger Date: Mon, 18 Mar 2024 10:08:40 +0100 Subject: Add command completion with tab key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the command prompt, press to get a list of all available commands and pick one (using vis-menu). This works also after typing the first letters of a command (p.e. `:la`). Co-authored-by: Matěj Cepl --- lua/plugins/complete-filename.lua | 18 ++++++++++++++---- vis-cmds.c | 9 +++++++++ vis-lua.c | 36 ++++++++++++++++++++++++++++++++++++ vis.h | 8 ++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/lua/plugins/complete-filename.lua b/lua/plugins/complete-filename.lua index 604361c..db0f2a7 100644 --- a/lua/plugins/complete-filename.lua +++ b/lua/plugins/complete-filename.lua @@ -29,9 +29,19 @@ local complete_filename = function(expand) prefix = home .. prefix:sub(j + 1) end - local cmdfmt = "vis-complete --file '%s'" - if expand then cmdfmt = "vis-open -- '%s'*" end - local status, out, err = vis:pipe(cmdfmt:format(prefix:gsub("'", "'\\''"))) + local status, out, err + if prefix:sub(1, 1) == ":" then + status, out, err = vis:complete_command(prefix:sub(2)) + if out then + out = out:gsub("\n$", ""):sub(#prefix) .. " " + end + pos = range.start + #prefix + expand = false + else + local cmdfmt = "vis-complete --file '%s'" + if expand then cmdfmt = "vis-open -- '%s'*" end + status, out, err = vis:pipe(cmdfmt:format(prefix:gsub("'", "'\\''"))) + end if status ~= 0 or not out then if err then vis:info(err) end return @@ -55,4 +65,4 @@ end, "Complete file name") -- complete file path at primary selection location using vis-open(1) vis:map(vis.modes.INSERT, "", function() complete_filename(true); -end, "Complete file name (expands path)") +end, "Complete file name (expands path) or command") diff --git a/vis-cmds.c b/vis-cmds.c index 482508f..b543683 100644 --- a/vis-cmds.c +++ b/vis-cmds.c @@ -693,6 +693,15 @@ static bool print_cmd(const char *key, void *value, void *data) { return text_appendf(data, " %-30s %s\n", usage, help ? help : ""); } +static bool print_cmd_name(const char *key, void *value, void *data) { + CommandDef *cmd = value; + return buffer_appendf(data, "%s\n", cmd->name); +} + +void vis_print_cmds(Vis *vis, Buffer *buf) { + map_iterate(vis->cmds, print_cmd_name, buf); +} + static bool print_option(const char *key, void *value, void *txt) { char desc[256]; const OptionDef *opt = value; diff --git a/vis-lua.c b/vis-lua.c index 695ebdb..99d9746 100644 --- a/vis-lua.c +++ b/vis-lua.c @@ -1146,6 +1146,41 @@ static int command_register(lua_State *L) { return 1; } +/*** + * Let user pick a command matching the given prefix. + * + * The editor core will be blocked while the external process is running. + * + * @function complete_command + * @tparam string prefix the prefix of the command to be completed + * @treturn int code the exit status of the executed command + * @treturn string stdout the data written to stdout + * @treturn string stderr the data written to stderr + */ +static int complete_command(lua_State *L) { + Vis *vis = obj_ref_check(L, 1, "vis"); + const char *prefix = luaL_checkstring(L, 2); + char *out = NULL, *err = NULL; + char cmd[32]; + int max_prefix_len = sizeof(cmd) - sizeof("grep '^' | vis-menu -b"); // including NUL + snprintf(cmd, sizeof(cmd), "grep '^%.*s' | vis-menu -b", max_prefix_len, prefix); + + Buffer buf = {0}; + vis_print_cmds(vis, &buf); + int status = vis_pipe_buf_collect(vis, buffer_content0(&buf), (const char*[]){cmd, NULL}, &out, &err, false); + + lua_pushinteger(L, status); + if (out) lua_pushstring(L, out); + else lua_pushnil(L); + if (err) lua_pushstring(L, err); + else lua_pushnil(L); + + free(out); + free(err); + buffer_release(&buf); + return 3; +} + /*** * Push keys to input queue and interpret them. * @@ -1559,6 +1594,7 @@ static const struct luaL_Reg vis_lua[] = { { "option_register", option_register }, { "option_unregister", option_unregister }, { "command_register", command_register }, + { "complete_command", complete_command }, { "feedkeys", feedkeys }, { "insert", insert }, { "replace", replace }, diff --git a/vis.h b/vis.h index 2e66b77..01bf1a0 100644 --- a/vis.h +++ b/vis.h @@ -14,6 +14,7 @@ typedef struct Win Win; #include "text-regex.h" #include "libutf.h" #include "array.h" +#include "buffer.h" #ifndef CONFIG_HELP #define CONFIG_HELP 1 @@ -1229,6 +1230,13 @@ bool vis_option_unregister(Vis *vis, const char *name); */ bool vis_prompt_cmd(Vis *vis, const char *cmd); +/** + * Write newline separated list of available commands to ``buf`` + * @param vis The editor instance. + * @param buf The buffer to write to. + */ +void vis_print_cmds(Vis*, Buffer *buf); + /** * Pipe a given file range to an external process. * @param vis The editor instance. -- cgit v1.2.3