1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-28 01:10:26 +00:00

Add special forms and sort completions.

Also fix case when no completion is needed.
This commit is contained in:
Calvin Rose 2020-01-18 00:17:08 -06:00
parent 6471b4d100
commit a8e4c4bed0
2 changed files with 117 additions and 64 deletions

View File

@ -35,7 +35,7 @@ Janet janet_line_getter(int32_t argc, Janet *argv) {
janet_arity(argc, 0, 3); janet_arity(argc, 0, 3);
const char *str = (argc >= 1) ? (const char *) janet_getstring(argv, 0) : ""; const char *str = (argc >= 1) ? (const char *) janet_getstring(argv, 0) : "";
JanetBuffer *buf = (argc >= 2) ? janet_getbuffer(argv, 1) : janet_buffer(10); JanetBuffer *buf = (argc >= 2) ? janet_getbuffer(argv, 1) : janet_buffer(10);
gbl_complete_env = (argc >= 3) ? janet_gettable(argv, 2) : janet_current_fiber()->env; gbl_complete_env = (argc >= 3) ? janet_gettable(argv, 2) : NULL;
janet_line_get(str, buf); janet_line_get(str, buf);
gbl_complete_env = NULL; gbl_complete_env = NULL;
return janet_wrap_buffer(buf); return janet_wrap_buffer(buf);
@ -98,6 +98,7 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
/* static state */ /* static state */
#define JANET_LINE_MAX 1024 #define JANET_LINE_MAX 1024
#define JANET_MATCH_MAX 256
#define JANET_HISTORY_MAX 100 #define JANET_HISTORY_MAX 100
static JANET_THREAD_LOCAL int gbl_israwmode = 0; static JANET_THREAD_LOCAL int gbl_israwmode = 0;
static JANET_THREAD_LOCAL const char *gbl_prompt = "> "; static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
@ -111,6 +112,8 @@ static JANET_THREAD_LOCAL int gbl_history_count = 0;
static JANET_THREAD_LOCAL int gbl_historyi = 0; static JANET_THREAD_LOCAL int gbl_historyi = 0;
static JANET_THREAD_LOCAL int gbl_sigint_flag = 0; static JANET_THREAD_LOCAL int gbl_sigint_flag = 0;
static JANET_THREAD_LOCAL struct termios gbl_termios_start; static JANET_THREAD_LOCAL struct termios gbl_termios_start;
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
static JANET_THREAD_LOCAL int gbl_match_count = 0;
/* Unsupported terminal list from linenoise */ /* Unsupported terminal list from linenoise */
static const char *badterms[] = { static const char *badterms[] = {
@ -358,91 +361,141 @@ static int is_symbol_char_gen(uint8_t c) {
c == '_'); c == '_');
} }
/* TODO - check against special forms and print in alphabetical order */ static JanetByteView get_symprefix(void) {
static void kshowcomp(void) {
JanetTable *env = gbl_complete_env;
/* Calculate current partial symbol. Maybe we could actually hook up the Janet /* Calculate current partial symbol. Maybe we could actually hook up the Janet
* parser here...*/ * parser here...*/
int i; int i;
int32_t sym_len = 0; JanetByteView ret;
ret.len = 0;
for (i = gbl_pos - 1; i >= 0; i--) { for (i = gbl_pos - 1; i >= 0; i--) {
uint8_t c = (uint8_t) gbl_buf[i]; uint8_t c = (uint8_t) gbl_buf[i];
if (!is_symbol_char_gen(c)) break; if (!is_symbol_char_gen(c)) break;
sym_len++; ret.len++;
}
/* Will be const for duration of match checking */
ret.bytes = (const uint8_t *)(gbl_buf + i + 1);
return ret;
} }
char *sym_start = gbl_buf + i + 1;
if (sym_len == 0) return; static int compare_bytes(JanetByteView a, JanetByteView b) {
int32_t minlen = a.len < b.len ? a.len : b.len;
int result = strncmp((const char *) a.bytes, (const char *) b.bytes, minlen);
if (result) return result;
return a.len < b.len ? -1 : a.len > b.len ? 1 : 0;
}
int32_t common_prefix_len = 0; static void check_match(JanetByteView src, const uint8_t *testsym, int32_t testlen) {
int32_t max_test_len = 0; JanetByteView test;
int32_t match_count = 0; test.bytes = testsym;
const uint8_t *first_match = NULL; test.len = testlen;
if (src.len > test.len || strncmp((const char *) src.bytes, (const char *) test.bytes, src.len)) return;
JanetByteView mm = test;
for (int i = 0; i < gbl_match_count; i++) {
if (compare_bytes(mm, gbl_matches[i]) < 0) {
JanetByteView temp = mm;
mm = gbl_matches[i];
gbl_matches[i] = temp;
}
}
if (gbl_match_count == JANET_MATCH_MAX) return;
gbl_matches[gbl_match_count++] = mm;
}
/* First pass, find match count and common prefix */ static void check_cmatch(JanetByteView src, const char *cstr) {
while (NULL != env) { check_match(src, (const uint8_t *) cstr, (int32_t) strlen(cstr));
JanetKV *kvend = env->data + env->capacity; }
for (JanetKV *kv = env->data; kv < kvend; kv++) {
if (janet_checktype(kv->key, JANET_NIL)) continue; static JanetByteView longest_common_prefix(void) {
const uint8_t *test_str; JanetByteView bv;
int32_t test_len; if (gbl_match_count == 0) {
if (!janet_bytes_view(kv->key, &test_str, &test_len)) continue; bv.len = 0;
if (test_len <= sym_len) continue; bv.bytes = NULL;
if (strncmp((char *)test_str, sym_start, sym_len)) continue;
/* Found a prefix match */
match_count++;
if (NULL == first_match) {
common_prefix_len = max_test_len = test_len;
first_match = test_str;
} else { } else {
if (max_test_len < test_len) max_test_len = test_len; bv = gbl_matches[0];
for (int32_t i = 0; i < common_prefix_len; i++) { for (int i = 0; i < gbl_match_count; i++) {
if (first_match[i] != test_str[i]) { JanetByteView other = gbl_matches[i];
common_prefix_len = i; int32_t minlen = other.len < bv.len ? other.len : bv.len;
for (bv.len = 0; bv.len < minlen; bv.len++) {
if (bv.bytes[bv.len] != other.bytes[bv.len]) {
break; break;
} }
} }
} }
} }
return bv;
}
static void check_specials(JanetByteView src) {
check_cmatch(src, "break");
check_cmatch(src, "def");
check_cmatch(src, "do");
check_cmatch(src, "fn");
check_cmatch(src, "if");
check_cmatch(src, "quasiquote");
check_cmatch(src, "quote");
check_cmatch(src, "set");
check_cmatch(src, "splice");
check_cmatch(src, "unquote");
check_cmatch(src, "var");
check_cmatch(src, "while");
}
/* TODO - check against special forms and print in alphabetical order */
static void kshowcomp(void) {
JanetTable *env = gbl_complete_env;
if (env == NULL) {
insert(' ', 0);
insert(' ', 0);
return;
}
JanetByteView prefix = get_symprefix();
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; env = env->proto;
} }
/* Complete common_prefix_len - sym_len characters */ check_specials(prefix);
for (int i = sym_len; i < common_prefix_len; i++) {
insert(first_match[i], 0); JanetByteView lcp = longest_common_prefix();
for (int i = prefix.len; i < lcp.len; i++) {
insert(lcp.bytes[i], 0);
} }
int32_t maxlen = 0;
for (int i = 0; i < gbl_match_count; i++)
if (gbl_matches[i].len > maxlen)
maxlen = gbl_matches[i].len;
int num_cols = getcols(); int num_cols = getcols();
if (gbl_match_count >= 2) {
norawmode(); norawmode();
/* Second pass, print */ /* Second pass, print */
int col_width = max_test_len + 4; int col_width = maxlen + 4;
int cols = num_cols / col_width; int cols = num_cols / col_width;
if (cols == 0) cols = 1; if (cols == 0) cols = 1;
int current_col = 0; int current_col = 0;
env = gbl_complete_env; for (int i = 0; i < gbl_match_count; i++) {
while (NULL != env) {
JanetKV *kvend = env->data + env->capacity;
for (JanetKV *kv = env->data; kv < kvend; kv++) {
if (janet_checktype(kv->key, JANET_NIL)) continue;
const uint8_t *test_str;
int32_t test_len;
if (!janet_bytes_view(kv->key, &test_str, &test_len)) continue;
if (test_len <= sym_len) continue;
if (strncmp((char *)test_str, sym_start, sym_len)) continue;
/* Found a prefix match */
if (current_col == 0) printf("\n"); if (current_col == 0) printf("\n");
printf("%-*s", col_width, test_str); printf("%-*s", col_width, (const char *) gbl_matches[i].bytes);
current_col = (current_col + 1) % cols; current_col = (current_col + 1) % cols;
} }
env = env->proto;
}
printf("\n"); printf("\n");
rawmode(); rawmode();
} }
}
static int line() { static int line() {
gbl_cols = getcols(); gbl_cols = getcols();