From 3b0e6357ad27e9499c1d1988e4990cff2100f221 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Fri, 10 Apr 2020 11:36:23 -0500 Subject: [PATCH] Make Ctrl-G in repl show docstring for symbol. Can be used to browse docs without poluting your repl session. --- src/boot/boot.janet | 6 +- src/mainclient/shell.c | 159 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 150 insertions(+), 15 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index cb914cad..82f5d806 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -467,7 +467,7 @@ (for-template i start stop 1 < + body)) (defmacro eachk - "loop over each key in ds. returns nil." + "Loop over each key in ds. Returns nil." [x ds & body] (keys-template x ds false body)) @@ -1495,9 +1495,9 @@ (defn doc-format "Reformat text to wrap at a given line." - [text] + [text &opt width] - (def maxcol (- (dyn :doc-width 80) 8)) + (def maxcol (- (or width (dyn :doc-width 80)) 8)) (var buf @" ") (var word @"") (var current 0) diff --git a/src/mainclient/shell.c b/src/mainclient/shell.c index 65834376..31befdac 100644 --- a/src/mainclient/shell.c +++ b/src/mainclient/shell.c @@ -515,6 +515,147 @@ static void check_specials(JanetByteView src) { check_cmatch(src, "while"); } +static void resolve_format(JanetTable *entry) { + int is_macro = janet_truthy(janet_table_get(entry, janet_ckeywordv("macro"))); + Janet refv = janet_table_get(entry, janet_ckeywordv("ref")); + int is_ref = janet_checktype(refv, JANET_ARRAY); + Janet value = janet_wrap_nil(); + if (is_ref) { + JanetArray *a = janet_unwrap_array(refv); + if (a->count) value = a->data[0]; + } else { + value = janet_table_get(entry, janet_ckeywordv("value")); + } + if (is_macro) { + fprintf(stderr, " macro\n"); + gbl_lines_below++; + } else if (is_ref) { + janet_eprintf(" var (%t)\n", value); + gbl_lines_below++; + } else { + janet_eprintf(" %t\n", value); + gbl_lines_below++; + } + Janet sm = janet_table_get(entry, janet_ckeywordv("source-map")); + Janet path = janet_get(sm, janet_wrap_integer(0)); + Janet line = janet_get(sm, janet_wrap_integer(1)); + Janet col = janet_get(sm, janet_wrap_integer(2)); + if (janet_checktype(path, JANET_STRING) && janet_truthy(line) && janet_truthy(col)) { + janet_eprintf(" %S on line %v, column %v\n", janet_unwrap_string(path), line, col); + gbl_lines_below++; + } +} + +static void doc_format(JanetString doc, int32_t width) { + int32_t maxcol = width - 8; + uint8_t wordbuf[256] = {0}; + int32_t wordp = 0; + int32_t current = 0; + if (maxcol > 200) maxcol = 200; + fprintf(stderr, " "); + for (int32_t i = 0; i < janet_string_length(doc); i++) { + uint8_t b = doc[i]; + switch (b) { + default: { + if (maxcol <= current + wordp + 1) { + if (!current) { + fwrite(wordbuf, wordp, 1, stderr); + wordp = 0; + } + fprintf(stderr, "\n "); + gbl_lines_below++; + current = 0; + } + wordbuf[wordp++] = b; + break; + } + case '\t': { + if (maxcol <= current + wordp + 2) { + if (!current) { + fwrite(wordbuf, wordp, 1, stderr); + wordp = 0; + } + fprintf(stderr, "\n "); + gbl_lines_below++; + current = 0; + } + wordbuf[wordp++] = ' '; + wordbuf[wordp++] = ' '; + break; + } + case '\n': + case ' ': { + if (wordp) { + int32_t oldcur = current; + int spacer = maxcol > current + wordp + 1; + if (spacer) current++; + else current = 0; + current += wordp; + if (oldcur) fprintf(stderr, spacer ? " " : "\n "); + if (oldcur && !spacer) gbl_lines_below++; + fwrite(wordbuf, wordp, 1, stderr); + wordp = 0; + } + if (b == '\n') { + fprintf(stderr, "\n "); + gbl_lines_below++; + current = 0; + } + } + } + } + if (wordp) { + int32_t oldcur = current; + int spacer = maxcol > current + wordp + 1; + if (spacer) current++; + else current = 0; + current += wordp + 1; + if (oldcur) fprintf(stderr, spacer ? " " : "\n "); + if (oldcur && !spacer) gbl_lines_below++; + fwrite(wordbuf, wordp, 1, stderr); + wordp = 0; + } +} + +static void find_matches(JanetByteView prefix) { + JanetTable *env = gbl_complete_env; + gbl_match_count = 0; + while (NULL != env) { + JanetKV *kvend = env->data + env->capacity; + for (JanetKV *kv = env->data; kv < kvend; kv++) { + if (!janet_checktype(kv->key, JANET_SYMBOL)) continue; + const uint8_t *sym = janet_unwrap_symbol(kv->key); + check_match(prefix, sym, janet_string_length(sym)); + } + env = env->proto; + } +} + +static void kshowdoc(void) { + if (!gbl_complete_env) return; + while (is_symbol_char_gen(gbl_buf[gbl_pos])) gbl_pos++; + JanetByteView prefix = get_symprefix(); + Janet symbol = janet_symbolv(prefix.bytes, prefix.len); + Janet entry = janet_table_get(gbl_complete_env, symbol); + if (!janet_checktype(entry, JANET_TABLE)) return; + Janet doc = janet_table_get(janet_unwrap_table(entry), janet_ckeywordv("doc")); + if (!janet_checktype(doc, JANET_STRING)) return; + JanetString docs = janet_unwrap_string(doc); + int num_cols = getcols(); + clearlines(); + fprintf(stderr, "\n\n\n"); + gbl_lines_below += 3; + resolve_format(janet_unwrap_table(entry)); + fprintf(stderr, "\n"); + gbl_lines_below += 1; + doc_format(docs, num_cols); + fprintf(stderr, "\n\n"); + gbl_lines_below += 2; + /* Go up to original line (zsh-like autocompletion) */ + fprintf(stderr, "\x1B[%dA", gbl_lines_below); + fflush(stderr); +} + static void kshowcomp(void) { JanetTable *env = gbl_complete_env; if (env == NULL) { @@ -528,19 +669,9 @@ static void kshowcomp(void) { gbl_pos++; JanetByteView prefix = get_symprefix(); - if (prefix.len == 0) return; + if (prefix.len == 0) return; - /* Find all matches */ - gbl_match_count = 0; - while (NULL != env) { - JanetKV *kvend = env->data + env->capacity; - for (JanetKV *kv = env->data; kv < kvend; kv++) { - if (!janet_checktype(kv->key, JANET_SYMBOL)) continue; - const uint8_t *sym = janet_unwrap_symbol(kv->key); - check_match(prefix, sym, janet_string_length(sym)); - } - env = env->proto; - } + find_matches(prefix); check_specials(prefix); @@ -633,6 +764,10 @@ static int line() { case 6: /* ctrl-f */ kright(); break; + case 7: /* ctrl-g */ + kshowdoc(); + refresh(); + break; case 127: /* backspace */ case 8: /* ctrl-h */ kbackspace(1);