aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Schillinger <maxschillinger@web.de>2024-03-18 10:08:40 +0100
committerRandy Palamar <randy@rnpnr.xyz>2025-11-28 07:41:12 -0700
commit3f1c3ee80e46f80d7f1642009c4f9dffa404d935 (patch)
tree08e3ab9011bcb4396ffca5f51d20c079d44da901
parent12fc09a442939d0af09d700c7a8074cca9b1694e (diff)
downloadvis-3f1c3ee80e46f80d7f1642009c4f9dffa404d935.tar.gz
vis-3f1c3ee80e46f80d7f1642009c4f9dffa404d935.tar.xz
Add command completion with tab key
In the command prompt, press <tab> 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<tab>`). Co-authored-by: Matěj Cepl <mcepl@cepl.eu>
-rw-r--r--lua/plugins/complete-filename.lua18
-rw-r--r--vis-cmds.c9
-rw-r--r--vis-lua.c36
-rw-r--r--vis.h8
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, "<C-x><C-o>", 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
@@ -1147,6 +1147,41 @@ static int command_register(lua_State *L) {
}
/***
+ * 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.
*
* The keys are processed as if they were read from the keyboard.
@@ -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
@@ -1230,6 +1231,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.
* @param file The file to pipe.