[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] Make readline completers a little more useful
From: |
John Darrington |
Subject: |
[PATCH] Make readline completers a little more useful |
Date: |
Sun, 17 Nov 2019 09:03:44 +0100 |
* src/pk-cmd.c (dot_cmds): Move to global scope.
* src/pk-editor.c (null_completion_function): New function.
* src/pk-file.c (close_completion_function): New function.
* src/pk-info.c (info_completion_function): New function.
* src/pk-set.c (set_completion_function): New function.
* src/pk-repl.c (poke_completion_function): New function,
(poke_getc): New function,
---
src/pk-cmd.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
src/pk-cmd.h | 4 ++++
src/pk-editor.c | 8 +++++++-
src/pk-file.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++---
src/pk-info.c | 32 ++++++++++++++++++++++++++++++-
src/pk-repl.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/pk-set.c | 35 +++++++++++++++++++++++++++++++++-
src/pkl-env.c | 29 ++++++++++++++++++++++++++++
src/pkl-env.h | 2 ++
9 files changed, 265 insertions(+), 6 deletions(-)
diff --git a/src/pk-cmd.c b/src/pk-cmd.c
index 5ff247e..b3af2db 100644
--- a/src/pk-cmd.c
+++ b/src/pk-cmd.c
@@ -784,3 +784,52 @@ pk_cmd_shutdown (void)
pk_trie_free (vm_disas_trie);
pk_trie_free (set_trie);
}
+
+
+/* Return the name of the next command that matches X,LEN. IDX is a
+ pointer to in integer used to index into the set of matches.
+ Returns the name of the next command in the set, or NULL if there
+ are no more. The returned value must be freed by the caller. */
+char *
+dot_command_completer_next (int *idx, const char *x, size_t len)
+{
+ /* Dot commands */
+ for (;;)
+ {
+ struct pk_cmd **c = dot_cmds + *idx;
+ if (*c == &null_cmd)
+ break;
+
+ char *name = xmalloc (strlen ( (*c)->name) + 1);
+ strcpy (name, ".");
+ strcat (name, (*c)->name);
+ if (0 != strncmp (name, x, len))
+ {
+ free (name);
+ (*idx)++;
+ continue;
+ }
+ return name;
+ }
+ return NULL;
+}
+
+
+/* Search for a command which matches cmdname.
+ Returns NULL if no such command exists. */
+struct pk_cmd *
+pk_cmd_find (const char *cmdname)
+{
+ if (cmdname != NULL)
+ {
+ struct pk_cmd **c;
+ for (c = dot_cmds; *c != &null_cmd; ++c)
+ {
+ /* Check if the command name matches.
+ +1 to skip the leading '.' */
+ if (0 == strcmp ((*c)->name, cmdname + 1))
+ return *c;
+ }
+ }
+ return NULL;
+}
diff --git a/src/pk-cmd.h b/src/pk-cmd.h
index fa9107e..fe453c6 100644
--- a/src/pk-cmd.h
+++ b/src/pk-cmd.h
@@ -108,4 +108,8 @@ void pk_cmd_init (void);
void pk_cmd_shutdown (void);
+char *dot_command_completer_next (int *idx, const char *x, size_t len);
+
+struct pk_cmd *pk_cmd_find (const char *cmdname);
+
#endif /* ! PK_H_CMD */
diff --git a/src/pk-editor.c b/src/pk-editor.c
index 5721ec5..9887fa7 100644
--- a/src/pk-editor.c
+++ b/src/pk-editor.c
@@ -121,5 +121,11 @@ pk_cmd_editor (int argc, struct pk_cmd_arg argv[],
uint64_t uflags)
return 1;
}
+static char *
+null_completion_function (const char *x, int state)
+{
+ return NULL;
+}
+
struct pk_cmd editor_cmd =
- {"editor", "", "", 0, NULL, pk_cmd_editor, ".editor"};
+ {"editor", "", "", 0, NULL, pk_cmd_editor, ".editor",
null_completion_function};
diff --git a/src/pk-file.c b/src/pk-file.c
index 54e6524..385968b 100644
--- a/src/pk-file.c
+++ b/src/pk-file.c
@@ -22,11 +22,58 @@
#include <gettext.h>
#define _(str) dgettext (PACKAGE, str)
#include <errno.h>
+#include <readline.h>
#include "ios.h"
#include "poke.h"
#include "pk-cmd.h"
+static void
+count_io_spaces (ios io, void *data)
+{
+ int *i = (int *) data;
+ if (i == NULL)
+ return;
+ (*i)++;
+}
+
+static char *
+close_completion_function (const char *x, int state)
+{
+ static int idx = 0;
+ static int n_ids = 0;
+ if (state == 0)
+ {
+ idx = 0;
+ n_ids = 0;
+ ios_map (count_io_spaces, &n_ids);
+ }
+ else
+ ++idx;
+
+ int len = strlen (x);
+ while (1)
+ {
+ if (idx >= n_ids)
+ break;
+ char buf[16];
+ snprintf (buf, 16, "#%d", idx);
+
+ int match = strncmp (buf, x, len);
+ if (match != 0)
+ {
+ idx++;
+ continue;
+ }
+
+ return strdup (buf);
+ }
+
+ return NULL;
+}
+
+
+
static int
pk_cmd_file (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
{
@@ -199,13 +246,13 @@ pk_cmd_load_file (int argc, struct pk_cmd_arg argv[],
uint64_t uflags)
}
struct pk_cmd file_cmd =
- {"file", "tf", "", 0, NULL, pk_cmd_file, "file (FILENAME|#ID)"};
+ {"file", "tf", "", 0, NULL, pk_cmd_file, "file (FILENAME|#ID)",
rl_filename_completion_function};
struct pk_cmd close_cmd =
- {"close", "?t", "", PK_CMD_F_REQ_IO, NULL, pk_cmd_close, "close [#ID]"};
+ {"close", "?t", "", PK_CMD_F_REQ_IO, NULL, pk_cmd_close, "close [#ID]",
close_completion_function};
struct pk_cmd info_files_cmd =
{"files", "", "", 0, NULL, pk_cmd_info_files, "info files"};
struct pk_cmd load_cmd =
- {"load", "f", "", 0, NULL, pk_cmd_load_file, "load FILENAME"};
+ {"load", "f", "", 0, NULL, pk_cmd_load_file, "load FILENAME",
rl_filename_completion_function};
diff --git a/src/pk-info.c b/src/pk-info.c
index b8e7f03..1c6fe0f 100644
--- a/src/pk-info.c
+++ b/src/pk-info.c
@@ -34,5 +34,35 @@ struct pk_cmd *info_cmds[] =
struct pk_trie *info_trie;
+static char *
+info_completion_function (const char *x, int state)
+{
+ static int idx = 0;
+ if (state == 0)
+ idx = 0;
+ else
+ ++idx;
+
+ size_t len = strlen (x);
+ while (1)
+ {
+ struct pk_cmd **c = info_cmds + idx;
+
+ if (*c == &null_cmd)
+ break;
+
+ if (0 != strncmp ((*c)->name, x, len))
+ {
+ idx++;
+ continue;
+ }
+ return strdup ((*c)->name);
+ }
+
+ return NULL;
+}
+
+
struct pk_cmd info_cmd =
- {"info", "", "", 0, &info_trie, NULL, "info (files|variable|type)"};
+ {"info", "", "", 0, &info_trie, NULL, "info (files|variable|function)",
+ info_completion_function};
diff --git a/src/pk-repl.c b/src/pk-repl.c
index d47724d..f74b04f 100644
--- a/src/pk-repl.c
+++ b/src/pk-repl.c
@@ -35,6 +35,63 @@
#include <signal.h>
#include <unistd.h>
+static char *
+poke_completion_function (const char *x, int state)
+{
+ static int idx = 0;
+ static struct pkl_ast_node_iter iter;
+ pkl_env env = pkl_get_env (poke_compiler);
+ if (state == 0)
+ {
+ pkl_env_iter_begin (env, &iter);
+ idx = 0;
+ }
+ else
+ {
+ if (pkl_env_iter_end (env, &iter))
+ idx++;
+ else
+ pkl_env_iter_next (env, &iter);
+ }
+
+ size_t len = strlen (x);
+ char *function_name;
+ function_name = get_next_matching_cmd (env, &iter, x, len);
+ if (function_name)
+ return function_name;
+
+ function_name = dot_command_completer_next (&idx, x, len);
+ if (function_name)
+ return function_name;
+
+ return NULL;
+}
+
+/* Readline's getc callback.
+ Use this function to update the completer which
+ should be used.
+*/
+static int
+poke_getc (FILE *stream)
+{
+ char *line_to_point = xmalloc (rl_point + 1);
+ int end = rl_point ? rl_point - 1 : 0;
+ strncpy (line_to_point, rl_line_buffer, end);
+ line_to_point[end] = '\0';
+
+ char *tok = strtok (line_to_point, "\t ");
+ if (rl_completion_entry_function == poke_completion_function)
+ {
+ struct pk_cmd *cmd = pk_cmd_find (tok);
+ if (cmd && cmd->completer)
+ rl_completion_entry_function = cmd->completer;
+ }
+ free (line_to_point);
+
+ return rl_getc (stream);
+}
+
+
static void
banner (void)
{
@@ -108,6 +165,7 @@ pk_repl (void)
read_history (poke_history);
}
#endif
+ rl_getc_function = poke_getc;
while (!poke_exit_p)
{
@@ -115,6 +173,7 @@ pk_repl (void)
char *line;
pk_term_flush ();
+ rl_completion_entry_function = poke_completion_function;
line = readline ("(poke) ");
if (line == NULL)
{
diff --git a/src/pk-set.c b/src/pk-set.c
index 8dd07a5..6e88e0b 100644
--- a/src/pk-set.c
+++ b/src/pk-set.c
@@ -307,7 +307,40 @@ struct pk_cmd *set_cmds[] =
&null_cmd
};
+static char *
+set_completion_function (const char *x, int state)
+{
+ static int idx = 0;
+ if (state == 0)
+ idx = 0;
+ else
+ ++idx;
+
+ int len = strlen (x);
+ while (1)
+ {
+ struct pk_cmd **c = set_cmds + idx;
+
+ if (*c == &null_cmd)
+ break;
+
+ char *name = xmalloc (strlen ( (*c)->name) + 1);
+ strcpy (name, (*c)->name);
+
+ int match = strncmp (name, x, len);
+ if (match != 0)
+ {
+ free (name);
+ idx++;
+ continue;
+ }
+ return name;
+ }
+
+ return NULL;
+}
+
struct pk_trie *set_trie;
struct pk_cmd set_cmd =
- {"set", "", "", 0, &set_trie, NULL, "set PROPERTY"};
+ {"set", "", "", 0, &set_trie, NULL, "set PROPERTY", set_completion_function};
diff --git a/src/pkl-env.c b/src/pkl-env.c
index 9b93bfc..15b5e2c 100644
--- a/src/pkl-env.c
+++ b/src/pkl-env.c
@@ -307,3 +307,32 @@ pkl_env_dup_toplevel (pkl_env env)
return new;
}
+
+
+/* Return the name of the next command that are currently
+ in context of ENV and match NAME,LEN. ITER is an iterator
+ into the set of matches. Returns the name of the next
+ command in the set, or NULL if there are no more.
+ The returned value must be freed by the caller. */
+char *
+get_next_matching_cmd (pkl_env env, struct pkl_ast_node_iter *iter,
+ const char *name, size_t len)
+{
+ /* "Normal" commands. */
+ for (;;)
+ {
+ if (pkl_env_iter_end (env, iter))
+ break;
+
+ pkl_ast_node decl_name = PKL_AST_DECL_NAME (iter->node);
+ const char *cmdname = PKL_AST_IDENTIFIER_POINTER (decl_name);
+ if (0 != strncmp (cmdname, name, len))
+ {
+ pkl_env_iter_next (env, iter);
+ continue;
+ }
+ return strdup (cmdname);
+ }
+ return NULL;
+}
+
diff --git a/src/pkl-env.h b/src/pkl-env.h
index c24fddf..4eb917b 100644
--- a/src/pkl-env.h
+++ b/src/pkl-env.h
@@ -134,5 +134,7 @@ void pkl_env_iter_begin (pkl_env env, struct
pkl_ast_node_iter *iter);
void pkl_env_iter_next (pkl_env env, struct pkl_ast_node_iter *iter);
bool pkl_env_iter_end (pkl_env env, const struct pkl_ast_node_iter *iter);
+char *get_next_matching_cmd (pkl_env env, struct pkl_ast_node_iter *iter,
+ const char *name, size_t len);
#endif /* !PKL_ENV_H */
--
2.11.0