summaryrefslogtreecommitdiffstats
path: root/parser.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2015-09-05 23:23:46 -0700
committerKaz Kylheku <kaz@kylheku.com>2015-09-05 23:23:46 -0700
commit591329b9ec120941bf2852c5e794e5bc2b0d8f22 (patch)
tree09ec9605d7c5ab7de0c58d298ce6585e6083b318 /parser.c
parenta4a89fb9816a5560cc396bcf3c8c5bfc66aae3f2 (diff)
downloadtxr-591329b9ec120941bf2852c5e794e5bc2b0d8f22.tar.gz
txr-591329b9ec120941bf2852c5e794e5bc2b0d8f22.tar.bz2
txr-591329b9ec120941bf2852c5e794e5bc2b0d8f22.zip
linenoise completion for txr symbols
Implement basic tab completion. * lib.c (package_alist, package_name, package_symbols): New functions. * lib.h (package_alist, package_name, package_symbols): Declared. * parser.c (find_matching_syms, provide_completions): New static functions. (repl): Register provide_completions as completion callback with linenoise.
Diffstat (limited to 'parser.c')
-rw-r--r--parser.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/parser.c b/parser.c
index a0bec2dd..804822bf 100644
--- a/parser.c
+++ b/parser.c
@@ -34,6 +34,7 @@
#include <setjmp.h>
#include <wchar.h>
#include <signal.h>
+#include <ctype.h>
#include <errno.h>
#include "config.h"
#include "lib.h"
@@ -342,6 +343,117 @@ val read_eval_stream(val stream, val error_stream, val hash_bang_support)
#if HAVE_TERMIOS
+static void find_matching_syms(lino_completions_t *cpl,
+ val package, val prefix,
+ val line_prefix, val force_qualify)
+{
+ val qualify = tnil(force_qualify || package != user_package);
+ val pkg_name = if2(qualify,
+ if3(package == keyword_package && !force_qualify,
+ lit(""),
+ package_name(package)));
+ val syms;
+
+ for (syms = package_symbols(package); syms; syms = cdr(syms)) {
+ val sym = car(syms);
+ val name = symbol_name(sym);
+
+ if (match_str(name, prefix, zero)) {
+ val compl;
+
+ if (qualify)
+ compl = format(nil, lit("~a~a:~a"), line_prefix, pkg_name, name, nao);
+ else
+ compl = format(nil, lit("~a~a"), line_prefix, name, nao);
+
+ {
+ char *completion = utf8_dup_to(c_str(compl));
+ lino_add_completion(cpl, completion);
+ free(completion);
+ }
+
+ gc_hint(compl);
+ }
+ }
+}
+
+static void provide_completions(const char *data,
+ lino_completions_t *cpl,
+ void *ctx)
+{
+ const char *gly = "!$%&*+-<=>?\\_~/";
+ const char *ptr = data + strlen(data) - 1;
+ const char *sym = 0, *pkg = 0;
+ const char *end;
+ val keyword = nil;
+ val package = nil;
+
+ (void) ctx;
+
+ while ((isalnum(*ptr) || strchr(gly, *ptr)) && (sym = ptr) && ptr > data)
+ ptr--;
+
+ if (!sym)
+ return;
+
+ end = sym;
+
+ if (*ptr == ':') {
+ if (ptr == data) {
+ keyword = t;
+ } else {
+ ptr--;
+
+ while ((isalnum(*ptr) || strchr(gly, *ptr)) && (pkg = ptr) && ptr > data)
+ ptr--;
+
+ if (!pkg)
+ keyword = t;
+ }
+ }
+
+ if (keyword) {
+ package = keyword_package;
+ end = sym - 1;
+ } else if (pkg) {
+ size_t sz = sym - pkg;
+ char *pkg_copy = alloca(sz);
+
+ memcpy(pkg_copy, pkg, sz);
+ pkg_copy[sz - 1] = 0;
+
+ {
+ val package_name = string_utf8(pkg_copy);
+ package = find_package(package_name);
+ }
+
+ end = pkg;
+ }
+
+ {
+ val sym_prefix = string_utf8(sym);
+ size_t lsz = end - data + 1;
+ char *line_pfxu8 = alloca(lsz);
+ memcpy(line_pfxu8, data, lsz);
+ line_pfxu8[lsz - 1] = 0;
+
+ {
+ val line_pfx = string_utf8(line_pfxu8);
+
+ if (package) {
+ find_matching_syms(cpl, package, sym_prefix, line_pfx, null(keyword));
+ } else {
+ val pa;
+
+ for (pa = package_alist(); pa; pa = cdr(pa)) {
+ val pair = car(pa);
+ find_matching_syms(cpl, cdr(pair), sym_prefix, line_pfx, nil);
+ }
+ }
+ }
+ }
+}
+
val repl(val bindings, val in_stream, val out_stream)
{
val ifd = stream_get_prop(in_stream, fd_k);
@@ -361,6 +473,8 @@ val repl(val bindings, val in_stream, val out_stream)
reg_varl(result_hash_sym, result_hash);
+ lino_set_completion_cb(ls, provide_completions, 0);
+
while (!done) {
val prompt = format(nil, lit("~a> "), counter, nao);
val prev_counter = counter;