summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.c534
1 files changed, 358 insertions, 176 deletions
diff --git a/main.c b/main.c
index 5570277..479235a 100644
--- a/main.c
+++ b/main.c
@@ -63,202 +63,216 @@ typedef struct {
EditMode mode;
u32 count;
- Str msg;
- Str input_line, input_prompt;
-
- Str search;
- int search_dir;
- u32 search_char;
- int search_char_dir;
- int search_char_incl;
+ /* these unfortunately have to be global so that draw() will know what
+ * to do when a window resizes during input
+ */
+ Str msg;
+ Str input_line, input_prompt;
+ u32 optc, opti;
+ Str *optv;
+ u32 *optvi;
+
+ Str search;
+ int search_dir;
+ u32 search_char;
+ int search_char_dir;
+ int search_char_incl;
} Editor;
Editor e = { 0 };
int ed_buf_open(Editor *e, const char *path) {
- if (e->bufn == ED_BUF_MAX) return -1;
- EditBuf b = { 0 };
- b.arena = arena_init(1L << 30);
- b.txt = FREELIST_NEW(&e->txt_free, &b.arena);
if (path) {
- b.path = str_dup(str_from_cstr(path), &b.arena);
- b.type = ED_BUF_FILE;
- txt_load(b.txt, path);
- } else {
- b.path = S("*scratch*");
- b.type = ED_BUF_SCRATCH;
- txt_load_empty(b.txt);
+ Str s = str_from_cstr(path);
+ for (u32 i = 0; i < e->bufn; i++) {
+ if (str_eql(e->buf[i].path, s)) return i;
+ }
}
- b.cur = txt_end(b.txt);
- e->buf[e->bufn] = b;
- return e->bufn++;
+
+ if (e->bufn == ED_BUF_MAX) return -1;
+ EditBuf b = { 0 };
+ b.arena = arena_init(1L << 30);
+ b.txt = FREELIST_NEW(&e->txt_free, &b.arena);
+ if (path) {
+ b.path = str_dup(str_from_cstr(path), &b.arena);
+ b.type = ED_BUF_FILE;
+ txt_load(b.txt, path);
+ } else {
+ b.path = S("*scratch*");
+ b.type = ED_BUF_SCRATCH;
+ txt_load_empty(b.txt);
+ }
+ b.cur = txt_end(b.txt);
+ e->buf[e->bufn] = b;
+ return e->bufn++;
}
void ed_buf_free(EditBuf *b) {
- txt_free(b->txt);
- arena_free(&b->arena);
+ txt_free(b->txt);
+ arena_free(&b->arena);
}
void ed_buf_change_path(Editor *e, u32 i, Str s) {
- EditBuf *eb = &e->buf[i];
- eb->path = str_dup(s, &eb->arena);
- eb->type = ED_BUF_FILE;
+ EditBuf *eb = &e->buf[i];
+ eb->path = str_dup(s, &eb->arena);
+ eb->type = ED_BUF_FILE;
}
void ed_init(Editor *e) {
- memset(e, 0, sizeof(Editor));
- e->scratch = arena_init(1L << 30);
- e->bufi = ed_buf_open(e, NULL);
- Str s = S("(Scratch buffer, type whatever)");
- e->buf[e->bufi].cur = txt_insert(e->buf[e->bufi].cur, s.s, s.n);
+ memset(e, 0, sizeof(Editor));
+ e->scratch = arena_init(1L << 30);
+ e->bufi = ed_buf_open(e, NULL);
+ Str s = S("(Scratch buffer, type whatever)");
+ e->buf[e->bufi].cur = txt_insert(e->buf[e->bufi].cur, s.s, s.n);
}
void ed_fini(Editor *e) {
- for (u32 i = 0; i < e->bufn; i++) {
- ed_buf_free(&e->buf[i]);
- }
- if (e->search.s) free(e->search.s);
+ for (u32 i = 0; i < e->bufn; i++) {
+ ed_buf_free(&e->buf[i]);
+ }
+ if (e->search.s) free(e->search.s);
}
u32 ed_buf_close(Editor *e, u32 i) {
- ed_buf_free(&e->buf[i]);
- if (i + 1 < e->bufn) {
- MOVE(&e->buf[i], &e->buf[i+1], e->bufn - (i + 1));
- }
- e->bufn--;
- return i > 0 ? i - 1 : 0;
+ ed_buf_free(&e->buf[i]);
+ if (i + 1 < e->bufn) {
+ MOVE(&e->buf[i], &e->buf[i+1], e->bufn - (i + 1));
+ }
+ e->bufn--;
+ return i > 0 ? i - 1 : 0;
}
int ed_buf_save(Editor *e, u32 i) {
- EditBuf *b = &e->buf[i];
- if (b->type == ED_BUF_FILE) {
- return txt_save(b->txt, str_to_cstr(b->path, &e->scratch));
- } else {
- return 0;
- }
+ EditBuf *b = &e->buf[i];
+ if (b->type == ED_BUF_FILE) {
+ return txt_save(b->txt, str_to_cstr(b->path, &e->scratch));
+ } else {
+ return 0;
+ }
}
Str str_printf(Arena *a, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- int n = vsnprintf(NULL, 0, fmt, ap);
- va_end(ap);
- va_start(ap, fmt);
- char *buf = new_arr(a, char, n + 1);
- vsnprintf(buf, n + 1, fmt, ap);
- va_end(ap);
- return (Str) { buf, n };
+ va_list ap;
+ va_start(ap, fmt);
+ int n = vsnprintf(NULL, 0, fmt, ap);
+ va_end(ap);
+ va_start(ap, fmt);
+ char *buf = new_arr(a, char, n + 1);
+ vsnprintf(buf, n + 1, fmt, ap);
+ va_end(ap);
+ return (Str) { buf, n };
}
static inline int is_space(u32 c) {
- return c <= 0x20 && c != 0;
+ return c <= 0x20 && c != 0;
}
TxtLoc next_word(TxtLoc l) {
- while (!at_end(l) && is_space(txt_chr(l))) l = cnext(l);
- while (!at_end(l) && !is_space(txt_chr(l))) l = cnext(l);
- return l;
+ while (!at_end(l) && is_space(txt_chr(l))) l = cnext(l);
+ while (!at_end(l) && !is_space(txt_chr(l))) l = cnext(l);
+ return l;
}
TxtLoc prev_word(TxtLoc l) {
- while (!at_start(l)) {
- TxtLoc n = bprev(l);
- if (!is_space(txt_chr(n))) break;
- l = n;
- }
- while (!at_start(l)) {
- TxtLoc n = bprev(l);
- if (is_space(txt_chr(n))) break;
- l = n;
- }
- return l;
+ while (!at_start(l)) {
+ TxtLoc n = bprev(l);
+ if (!is_space(txt_chr(n))) break;
+ l = n;
+ }
+ while (!at_start(l)) {
+ TxtLoc n = bprev(l);
+ if (is_space(txt_chr(n))) break;
+ l = n;
+ }
+ return l;
}
static inline u32 bracket_opp(u32 c) {
- switch (c) {
- case '{': return '}';
- case '}': return '{';
- case '[': return ']';
- case ']': return '[';
- case '(': return ')';
- case ')': return '(';
- case '<': return '>';
- case '>': return '<';
- default:
- return c;
- }
+ switch (c) {
+ case '{': return '}';
+ case '}': return '{';
+ case '[': return ']';
+ case ']': return '[';
+ case '(': return ')';
+ case ')': return '(';
+ case '<': return '>';
+ case '>': return '<';
+ default:
+ return c;
+ }
}
static inline int bracket_dir(u32 c) {
- switch (c) {
- case '{': case '[': case '(': case '<': return 1;
- case '}': case ']': case ')': case '>': return -1;
- default:
- return 0;
- }
+ switch (c) {
+ case '{': case '[': case '(': case '<': return 1;
+ case '}': case ']': case ')': case '>': return -1;
+ default:
+ return 0;
+ }
}
int match_bracket(TxtLoc l, TxtLoc *out, u32 c) {
- u32 depth = 1;
- u32 o = bracket_opp(c);
- if (txt_chr(l) != c) return 0;
- int dir = bracket_dir(c);
- if (dir < 0) {
- while (!at_start(l) && depth > 0) {
- l = cprev(l);
- u32 x = txt_chr(l);
- if (x == c) depth++;
- else if (x == o) depth--;
- }
- } else if (dir > 0) {
- while (!at_end(l) && depth > 0) {
- l = cnext(l);
- u32 x = txt_chr(l);
- if (x == c) depth++;
- else if (x == o) depth--;
- }
-
- }
- if (depth == 0) {
- *out = l;
- return 1;
- } else {
- return 0;
- }
+ u32 depth = 1;
+ u32 o = bracket_opp(c);
+ if (txt_chr(l) != c) return 0;
+ int dir = bracket_dir(c);
+ if (dir < 0) {
+ while (!at_start(l) && depth > 0) {
+ l = cprev(l);
+ u32 x = txt_chr(l);
+ if (x == c) depth++;
+ else if (x == o) depth--;
+ }
+ } else if (dir > 0) {
+ while (!at_end(l) && depth > 0) {
+ l = cnext(l);
+ u32 x = txt_chr(l);
+ if (x == c) depth++;
+ else if (x == o) depth--;
+ }
+
+ }
+ if (depth == 0) {
+ *out = l;
+ return 1;
+ } else {
+ return 0;
+ }
}
TxtLoc next_func_end(TxtLoc l) {
- l = start_of_line(l);
- while (!at_end(l)) {
- l = next_line_start(l);
- if (txt_chr(l) == '}' && txt_chr(cnext(l)) == '\n') break;
- }
- return l;
+ l = start_of_line(l);
+ while (!at_end(l)) {
+ l = next_line_start(l);
+ if (txt_chr(l) == '}' && txt_chr(cnext(l)) == '\n') break;
+ }
+ return l;
}
TxtLoc prev_func_end(TxtLoc l) {
- l = start_of_line(l);
- while (!at_start(l)) {
- l = prev_line_start(l);
- if (txt_chr(l) == '}' && txt_chr(cnext(l)) == '\n') break;
- }
- return l;
+ l = start_of_line(l);
+ while (!at_start(l)) {
+ l = prev_line_start(l);
+ if (txt_chr(l) == '}' && txt_chr(cnext(l)) == '\n') break;
+ }
+ return l;
}
TxtLoc prev_func(TxtLoc l) {
- match_bracket(prev_func_end(l), &l, '}');
- return l;
+ match_bracket(prev_func_end(l), &l, '}');
+ return l;
}
TxtLoc next_func(TxtLoc l) {
- match_bracket(l, &l, '{');
- match_bracket(next_func_end(l), &l, '}');
- return l;
+ match_bracket(l, &l, '{');
+ match_bracket(next_func_end(l), &l, '}');
+ return l;
}
int empty_line(TxtLoc l) {
- u8 b = txt_byte(start_of_line(l));
- return b == '\n' || b == 0 /* last line of buffer */;
+ u8 b = txt_byte(start_of_line(l));
+ return b == '\n' || b == 0 /* last line of buffer */
+ ;
}
TxtLoc next_par(TxtLoc l) {
@@ -438,11 +452,7 @@ void find_view_window(TxtLoc l, TxtLoc *start, TxtLoc *end, u32 lines) {
*end = b;
}
-void draw(void *ctx) {
- (void)ctx;
- EditBuf *eb = &e.buf[e.bufi];
-
- vui_clear();
+void draw_buf(EditBuf *eb) {
int lmarg = 0;
int x = lmarg, y = 0;
TxtLoc start, end;
@@ -517,48 +527,106 @@ void draw(void *ctx) {
}
}
+void draw_opts(Str *optv, u32 optc, u32 *optvi, u32 opti) {
+ u32 x = 0;
+ u32 y = 0;
+ VuiAttr a = A_DEFAULT | A_ITALIC;
+ vui_clear();
+ x += vui_putsna(0, y, e.input_prompt.s, e.input_prompt.n, a);
+ x += vui_putsna(x, y, e.input_line.s, e.input_line.n, a);
+ vui_curs_pos(x, y);
+ vui_curs_shape(VUI_CURS_BAR);
+ y++;
+ u32 n = LINES;
+ u32 tn = n / 2;
+ u32 top = opti > tn ? opti - tn : 0;
+ u32 bot = optc - top > n ? n : optc - top;
+ for (u32 i = top; i < bot; i++) {
+ VuiAttr a = A_DEFAULT;
+ if (i == opti) a |= A_REVERSE;
+ vui_putsna(2, y++, optv[optvi[i]].s, optv[optvi[i]].n, a);
+ }
+}
+
+void draw(void *ctx) {
+ (void)ctx;
+ if (e.optv) {
+ draw_opts(e.optv, e.optc, e.optvi, e.opti);
+ } else {
+ draw_buf(&e.buf[e.bufi]);
+ }
+}
+
TxtLoc logical_line_start(TxtLoc l) {
l = start_of_line(l);
while (txt_chr(l) == '\t') l = cnext(l);
return l;
}
-Str get_input_line(Str prompt) {
+typedef struct {
+ Arena *a;
+ Str prompt;
+ DYNARR(char) s;
+} LineEditor;
+
+typedef enum {
+ LEDIT_EMPTY,
+ LEDIT_DONE,
+ LEDIT_CONT,
+ LEDIT_UP,
+ LEDIT_DOWN
+} LineEditStatus;
+
+static LineEditor line_editor(Str prompt, Arena *a) {
+ e.input_line = (Str) { 0, 0 };
e.input_prompt = prompt;
- DYNARR(char) s = { 0 };
+ return (LineEditor) { a, prompt, { 0 }};
+}
+
+static LineEditStatus line_edit(LineEditor *le, u32 c) {
+ switch (c) {
+ case '\r':
+ e.input_line = (Str) { 0, 0 };
+ e.input_prompt = (Str) { 0, 0 };
+ return LEDIT_DONE;
+ case KEY_UP:
+ return LEDIT_UP;
+ case KEY_DOWN:
+ return LEDIT_DOWN;
+ case KEY_ESC:
+ e.input_line = (Str) { 0, 0 };
+ e.input_prompt = (Str) { 0, 0 };
+ return LEDIT_EMPTY;
+ case KEY_BKSP:
+ if (le->s.n > 0) le->s.n--;
+ break;
+ case 0x17 /* ^W */:
+ while (le->s.n > 0 && is_space(le->s.v[le->s.n-1])) le->s.n--;
+ while (le->s.n > 0 && !is_space(le->s.v[le->s.n-1])) le->s.n--;
+ break;
+ default:
+ if (c > 0 && c <= KEY_UTF8_MAX && c >= ' ') {
+ u32 n = utf8_encode_len(&c, 1);
+ DA_AFIT(&le->s, le->a, le->s.n + n);
+ utf8_encode(&le->s.v[le->s.n], &c, n);
+ le->s.n += n;
+ }
+ e.opti = 0;
+ break;
+ }
+ e.input_line = (Str) { le->s.v, le->s.n };
+ return LEDIT_CONT;
+}
+
+Str get_input_line(Str prompt) {
+ LineEditor le = line_editor(prompt, &e.scratch);
for (;;) {
draw(NULL);
vui_blit();
- u32 c = vui_key();
- switch (c) {
- case '\r':
- goto done;
- case KEY_ESC:
- e.input_line = (Str) { 0, 0 };
- e.input_prompt = (Str) { 0, 0 };
- return (Str) { 0, 0 };
- case KEY_BKSP:
- if (s.n > 0) s.n--;
- break;
- case 0x17 /* ^W */:
- while (s.n > 0 && is_space(s.v[s.n-1])) s.n--;
- while (s.n > 0 && !is_space(s.v[s.n-1])) s.n--;
- break;
- default:
- if (c > 0 && c <= KEY_UTF8_MAX && c >= ' ') {
- u32 n = utf8_encode_len(&c, 1);
- DA_AFIT(&s, &e.scratch, s.n + n);
- utf8_encode(&s.v[s.n], &c, n);
- s.n += n;
- }
- break;
- }
- e.input_line = (Str) { s.v, s.n };
+ LineEditStatus st = line_edit(&le, vui_key());
+ if (st == LEDIT_EMPTY) return (Str) { 0, 0 };
+ if (st == LEDIT_DONE) return (Str) { le.s.v, le.s.n };
}
-done:;
- e.input_line = (Str) { 0, 0 };
- e.input_prompt = (Str) { 0, 0 };
- return (Str) { s.v, s.n };
}
int search_next_regex(TxtLoc l, Str src, TxtLoc *out) {
@@ -998,12 +1066,119 @@ static TxtLoc indent_dedent_lines(TxtLoc start, TxtLoc end, int n) {
return txt_at(start.t, ofs);
}
+int *opt_sort_scr;
+
+static inline char to_lower(char c) {
+ /* TODO: consider utf-8 support */
+ if (c >= 'A' && c <= 'Z') return c + ('a' - 'A');
+ return c;
+}
+
+int fz_score(Str s, Str p) {
+ if (!p.n || str_eql(s, p)) return 0;
+ int n = 1;
+ u32 pi = 0, si = 0;
+ for (;;) {
+ if (to_lower(p.s[pi]) == to_lower(s.s[si])) {
+ si++;
+ pi++;
+ if (si == s.n) {
+ return pi < p.n ? -1 : n;
+ } else if (pi == p.n) {
+ return n;
+ }
+ } else {
+ si++;
+ n++;
+ if (si == s.n) return -1;
+ }
+ }
+}
+
+void qsort_opt(u32 *dest, int *scr, int n) {
+ int i = 0, j = n - 1;
+ int x = scr[n / 2];
+ do {
+ while (scr[i] < x) i++;
+ while (x < scr[j]) j--;
+ if (i <= j) {
+ int t = dest[i];
+ dest[i] = dest[j];
+ dest[j] = t;
+ t = scr[i];
+ scr[i] = scr[j];
+ scr[j] = t;
+ i++;
+ j--;
+ }
+ } while (i <= j);
+ if (0 < j) qsort_opt(dest, scr, j + 1);
+ if (i+1 < n) qsort_opt(dest + i, scr + i, n - i);
+}
+
+u32 sort_opt_idx(u32 *dest, Str *src, u32 n, Str pat, Arena *scratch) {
+ int *scr = new_arr(scratch, int, n);
+ u32 scrn = 0;
+ for (u32 i = 0; i < n; i++) {
+ int s = fz_score(src[i], pat);
+ if (s != -1) {
+ scr[scrn] = s;
+ dest[scrn] = i;
+ scrn++;
+ }
+ }
+ qsort_opt(dest, scr, scrn);
+ return scrn;
+}
+
+int select_opt(Str *optv, u32 optc, Str prompt) {
+ if (!optc) return -1;
+ e.optc = optc;
+ e.optvi = new_arr(&e.scratch, u32, optc);
+ e.optv = optv;
+ LineEditor le = line_editor(prompt, &e.scratch);
+ int r = -1;
+ for (;;) {
+ Arena s = e.scratch;
+ e.optc = sort_opt_idx(e.optvi, optv, optc, e.input_line, &e.scratch);
+ draw(NULL);
+ vui_blit();
+ e.scratch = s;
+ LineEditStatus st = line_edit(&le, vui_key());
+ if (st == LEDIT_EMPTY) break;
+ if (st == LEDIT_DONE) {
+ r = e.optvi[e.opti];
+ break;
+ }
+ if (st == LEDIT_DOWN) e.opti = (e.opti + 1) % e.optc;
+ if (st == LEDIT_UP) e.opti = e.opti ? e.opti - 1 : e.optc - 1;
+ }
+ e.optc = 0;
+ e.optv = NULL;
+ return r;
+}
+
+#include <dirent.h>
+
+Str select_file_in(const char *path) {
+ DIR *d = opendir(path);
+ if (!d) return (Str) { 0, 0 };
+ struct dirent *de;
+ DYNARR(Str) opt = { 0 };
+ DA_APUSH(&opt, &e.scratch, S(".."));
+ while ((de = readdir(d))) {
+ if (de->d_name[0] == '.') continue;
+ Str n = str_dup(str_from_cstr(de->d_name), &e.scratch);
+ DA_APUSH(&opt, &e.scratch, n);
+ }
+ int o = select_opt(opt.v, opt.n, S("File: "));
+ if (o == -1) return (Str) { 0, 0 };
+ return opt.v[o];
+}
+
Str select_file(void) {
- /* TODO: implement */
- vui_clear();
- vui_blit();
- vui_key();
- return (Str) { 0 };
+ Str s = select_file_in(".");
+ return s;
}
int main(int argc, const char **argv) {
@@ -1166,6 +1341,13 @@ int main(int argc, const char **argv) {
if (s.n > 0)
e.bufi = ed_buf_open(&e, str_to_cstr(s, &e.scratch));
} break;
+ case 'F': {
+ Str s = select_file();
+ if (s.n > 0) {
+ e.bufi = ed_buf_close(&e, e.bufi);
+ e.bufi = ed_buf_open(&e, str_to_cstr(s, &e.scratch));
+ }
+ } break;
case 'm':
build_file(eb->path, 1);
break;