summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.c616
1 files changed, 331 insertions, 285 deletions
diff --git a/main.c b/main.c
index 17c5671..ceae415 100644
--- a/main.c
+++ b/main.c
@@ -48,7 +48,7 @@ typedef enum {
MODE_REPLACE_MULT
} EditMode;
-static const char *mode_str[] = {
+static const char *mode_key_str[] = {
"N",
"I",
"R",
@@ -79,6 +79,7 @@ typedef struct {
int search_char_incl;
Str homedir;
+ Str yank;
} Editor;
Editor e = { 0 };
@@ -135,6 +136,7 @@ void ed_fini(Editor *e) {
ed_buf_free(&e->buf[i]);
}
if (e->search.s) free(e->search.s);
+ if (e->yank.s) free(e->yank.s);
}
u32 ed_buf_close(Editor *e, u32 i) {
@@ -491,32 +493,39 @@ static Str basename(Str s) {
}
void draw_buf(EditBuf *eb) {
- int lmarg = 0;
- int x = lmarg, y = 0;
+ u32 lmarg = 0;
+ u32 tmarg = 1;
+ u32 bmarg = 0;
+ u32 status_y = 0;
+ int x = lmarg, y = tmarg;
TxtLoc start, end;
- find_view_window(eb->cur, &start, &end, LINES - 1);
+ find_view_window(eb->cur, &start, &end, LINES - (tmarg + bmarg));
VuiAttr norm = A_DEFAULT;
- VuiAttr sel = norm | A_REVERSE;
VuiAttr txt = A_DEFAULT;
- VuiAttr trailing_whitespace = BG_BLUE | FG_DEFAULT;
+ VuiAttr trailsp = BG_BLUE | FG_DEFAULT;
+ VuiAttr sel = norm | A_REVERSE;
- vui_fill(' ', txt);
+ vui_clear();
{
int x = 0;
- int y = LINES-1;
- x += vui_aprintf(x, y, norm, " %s ", mode_str[e.mode]);
+ int y = status_y;
+ x += vui_aprintf(x, y, norm, " %s ", mode_key_str[e.mode]);
for (u32 i = 0; i < e.bufn; i++) {
EditBuf *b = &e.buf[i];
VuiAttr a = i == e.bufi ? sel : norm;
Str p = basename(b->path);
x += vui_aprintf(x, y, a, " %.*s", (int)p.n, p.s);
- if (b->type == ED_BUF_FILE && b->txt->dirty) x += vui_putsa(x, y, "* ", a);
- else vui_chra(x++, y, ' ', a);
+ if (b->type == ED_BUF_FILE && b->txt->dirty) {
+ x += vui_putsa(x, y, "* ", a);
+ } else {
+ vui_chra(x++, y, ' ', a);
+ }
}
int n = COLS;
if (e.msg.n) {
- x += vui_aprintf(x, y, norm, " %.*s", (int)e.msg.n, e.msg.s);
+ x += vui_aprintf(x, y, norm, " %.*s",
+ (int)e.msg.n, e.msg.s);
e.msg = (Str) { 0, 0 };
}
while (x < n) vui_chra(x++, y, ' ', norm);
@@ -538,7 +547,7 @@ void draw_buf(EditBuf *eb) {
x = lmarg;
y++;
} else if (is_space(c)) {
- VuiAttr a = txt_chr(start) == '\n' ? trailing_whitespace : txt;
+ VuiAttr a = txt_chr(start) == '\n' ? trailsp : txt;
if (c == '\t') {
u32 n = 1 + (-(x+1) & 7);
while (n--) vui_chra(x++, y, ' ', a);
@@ -553,6 +562,19 @@ void draw_buf(EditBuf *eb) {
ASSERT(start.i <= eb->txt->ptbl.v[start.p].n);
if (!cur_found) vui_curs_pos(x, y);
+ switch (e.mode) {
+ case MODE_NORMAL:
+ vui_curs_shape(VUI_CURS_BLOCK);
+ break;
+ case MODE_INSERT:
+ vui_curs_shape(VUI_CURS_BAR);
+ break;
+ case MODE_REPLACE_ONE:
+ case MODE_REPLACE_MULT:
+ vui_curs_shape(VUI_CURS_UNDERLINE);
+ break;
+ }
+
if (e.input_line.n > 0 || e.input_prompt.n > 0) {
VuiAttr a = norm;
u32 x = 0;
@@ -561,8 +583,6 @@ void draw_buf(EditBuf *eb) {
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);
- u32 c = COLS;
- while (x < c) vui_chra(x++, y, ' ', a);
}
}
@@ -1310,6 +1330,299 @@ int select_func(Txt *t, TxtLoc *out) {
return 1;
}
+void yank_range(Editor *e, TxtLoc start, TxtLoc end) {
+ if (txt_before(end, start)) {
+ TxtLoc t = start;
+ start = end;
+ end = t;
+ }
+ Str s = txt_collect_range(start, end, &e->scratch);
+ e->yank.s = realloc(e->yank.s, s.n);
+ if (!e->yank.s) FAIL_WITH_MSG("couldn't reallocate clipboard");
+ memcpy(e->yank.s, s.s, s.n);
+ e->yank.n = s.n;
+}
+
+void mode_key_normal(Editor *e, u32 c) {
+ EditBuf *eb = &e->buf[e->bufi];
+ switch (c) {
+ case 'q':
+ e->bufi = ed_buf_close(e, e->bufi);
+ break;
+ case 'Q':
+ while (e->bufn > 0) e->bufi = ed_buf_close(e, e->bufi);
+ break;
+ case 'H':
+ if (e->bufi > 0) e->bufi = e->bufi - 1;
+ else e->bufi = e->bufn - 1;
+ break;
+ case 'L':
+ e->bufi = (e->bufi + 1) % e->bufn;
+ break;
+ case 'z':
+ case 'Z':
+ case 0x13 /* ^S */:
+ ed_buf_save(e, e->bufi);
+ e->msg = str_printf(&e->scratch, "%.02fk written", eb->txt->len / 1024.0);
+ if (c == 'Z') e->bufi = ed_buf_close(e, e->bufi);
+ break;
+ case 'i':
+ e->mode = 1;
+ break;
+ case 'a': {
+ e->mode = 1;
+ TxtLoc l = eb->cur;
+ TxtLoc e = end_of_line(l);
+ l = txt_before(l, e) ? cnext(l) : e;
+ eb->cur = l;
+ } break;
+ case 'I':
+ e->mode = 1;
+ eb->cur = logical_line_start(eb->cur);
+ break;
+ case 'A':
+ e->mode = 1;
+ eb->cur = end_of_line(eb->cur);
+ break;
+ case 'o':
+ e->mode = 1;
+ eb->cur = ins_newline(end_of_line(eb->cur));
+ break;
+ case 'O':
+ e->mode = 1;
+ eb->cur = ins_newline(bprev(start_of_line(eb->cur)));
+ if (at_start(bprev(eb->cur))) eb->cur = bprev(eb->cur);
+ break;
+ case 'p':
+ eb->cur = cprev(txt_insert(cnext(eb->cur), e->yank.s, e->yank.n));
+ break;
+ case 'P':
+ eb->cur = txt_insert(eb->cur, e->yank.s, e->yank.n);
+ for (u32 i = 0; i < e->yank.n; i++) eb->cur = cprev(eb->cur);
+ break;
+ case 'd': {
+ TxtLoc before = eb->cur;
+ if (motion(&eb->cur, vui_key())) {
+ yank_range(e, before, eb->cur);
+ eb->cur = txt_delete_range(before, eb->cur);
+ }
+ } break;
+ case 'y': {
+ TxtLoc l = eb->cur;
+ if (motion(&l, vui_key())) yank_range(e, eb->cur, l);
+ } break;
+ case 'D':
+ yank_range(e, eb->cur, end_of_line(eb->cur));
+ eb->cur = txt_delete_range(eb->cur, end_of_line(eb->cur));
+ break;
+ case 'C':
+ yank_range(e, eb->cur, end_of_line(eb->cur));
+ eb->cur = txt_delete_range(eb->cur, end_of_line(eb->cur));
+ e->mode = 1;
+ break;
+ case 'S': {
+ TxtLoc start = start_of_line(eb->cur);
+ TxtLoc end = end_of_line(eb->cur);
+ yank_range(e, start, end);
+ eb->cur = txt_delete_range(start, end);
+ e->mode = 1;
+ } break;
+ case 'J': {
+ TxtLoc l = end_of_line(eb->cur);
+ if (txt_byte(l) == '\n') {
+ do l = txt_delete_c(cnext(l));
+ while (is_space(txt_chr(l)));
+ l = cprev(txt_insert_c(l, ' '));
+ eb->cur = l;
+ }
+ } break;
+ case 'c': {
+ TxtLoc before = eb->cur;
+ if (motion(&eb->cur, vui_key())) {
+ yank_range(e, before, eb->cur);
+ eb->cur = txt_delete_range(before, eb->cur);
+ e->mode = 1;
+ }
+ break;
+ }
+ case 'x':
+ yank_range(e, eb->cur, cnext(eb->cur));
+ eb->cur = txt_delete_c(cnext(eb->cur));
+ break;
+ case 's':
+ yank_range(e, eb->cur, cnext(eb->cur));
+ eb->cur = txt_delete_c(cnext(eb->cur));
+ e->mode = 1;
+ break;
+ case 'r':
+ e->mode = MODE_REPLACE_ONE;
+ break;
+ case 'R':
+ e->mode = MODE_REPLACE_MULT;
+ break;
+ case 'M': {
+ TxtLoc start = prev_par(eb->cur);
+ TxtLoc end = next_par(eb->cur);
+ if (shell_replace(start, end, "fmt -w80 -u")) {
+ err(1, "shell_replace");
+ }
+ } break;
+
+ case ' ':
+ switch ((u32)vui_key()) {
+ case 'f': {
+ Str s = select_file();
+ 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 's': {
+ int b = select_buf();
+ if (b != -1) e->bufi = b;
+ } break;
+ case 'j': {
+ TxtLoc l;
+ if (select_func(eb->txt, &l)) eb->cur = l;
+ } break;
+ case 'm':
+ build_file(eb->path, 1);
+ break;
+ case 'M':
+ build_file(eb->path, 0);
+ break;
+ case 'r':
+ run_file(eb->path, 1);
+ break;
+ case 'R':
+ run_file(eb->path, 0);
+ break;
+ case 'D':
+ debug_file(eb->path);
+ break;
+ case 'e':
+ shell_run_no_prompt("make clean");
+ break;
+ case 'l':
+ shell_run_no_prompt("git log --oneline %");
+ break;
+ case 'L':
+ shell_run_no_prompt("git log %");
+ break;
+ case 'd':
+ shell_run_no_prompt("git diff --color=always % | less -cr");
+ break;
+ case 'c':
+ shell_run_no_prompt("git add % && git commit %");
+ break;
+ default:
+ /* TODO: flash */
+ break;
+ }
+ break;
+
+ case '<':
+ case '>': {
+ TxtLoc end = eb->cur;
+ VuiKey k = vui_key();
+ if (k == '<' || k == '>' || motion(&end, k)) {
+ if (k == '<' || k == '>') end = next_line_start(end);
+ eb->cur = indent_dedent_lines(eb->cur, end, c == '<' ? -1 : 1);
+ }
+ } break;
+
+ case '=': {
+ Str s = get_input_line(S("File to open: "));
+ if (s.n > 0) e->bufi = ed_buf_open(e, str_to_cstr(s, &e->scratch));
+ } break;
+
+ case '+': {
+ Str s = get_input_line(S("Replace file with: "));
+ 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 '-': {
+ Str s = get_input_line(S("Change path: "));
+ if (s.n > 0) ed_buf_change_path(e, e->bufi, s);
+ } break;
+
+ default:
+ motion(&eb->cur, c);
+ break;
+ }
+}
+
+void mode_key_insert(Editor *e, u32 c) {
+ EditBuf *eb = &e->buf[e->bufi];
+ switch (c) {
+ case KEY_ESC:
+ if (txt_after(eb->cur, start_of_line(eb->cur))) {
+ eb->cur = cprev(eb->cur);
+ }
+ e->mode = 0;
+ break;
+ case KEY_BKSP:
+ eb->cur = txt_delete_c(eb->cur);
+ break;
+ case 0x17 /* ^W */:
+ eb->cur = txt_delete_range(prev_word(eb->cur), eb->cur);
+ break;
+ case 0x05 /* ^E */:
+ eb->cur = txt_delete_range(eb->cur, next_word(eb->cur));
+ break;
+ case 0x0c /* ^L */:
+ vui_redraw();
+ break;
+ case '\r':
+ eb->cur = ins_newline(eb->cur);
+ break;
+ default:
+ if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) eb->cur = txt_insert_c(eb->cur, c);
+ break;
+ }
+}
+
+void edit_key(Editor *e, u32 c) {
+ EditBuf *eb = &e->buf[e->bufi];
+ switch (e->mode) {
+ case MODE_NORMAL:
+ mode_key_normal(e, c);
+ break;
+ case MODE_INSERT:
+ mode_key_insert(e, c);
+ break;
+ case MODE_REPLACE_ONE:
+ if (txt_byte(eb->cur) != '\n' && c != KEY_BKSP && c <= KEY_UTF8_MAX) {
+ eb->cur = cprev(txt_insert_c(txt_delete_c(cnext(eb->cur)), c));
+ }
+ e->mode = MODE_NORMAL;
+ break;
+ case MODE_REPLACE_MULT:
+ if (c == '\x1b') {
+ e->mode = MODE_NORMAL;
+ eb->cur = cprev(eb->cur);
+ } else if (c == KEY_BKSP) {
+ if (txt_byte(bprev(eb->cur)) != '\n')
+ eb->cur = cprev(eb->cur);
+ } else if (c <= KEY_UTF8_MAX) {
+ if (txt_byte(eb->cur) == '\n') {
+ eb->cur = txt_insert_c(eb->cur, c);
+ } else {
+ eb->cur = txt_insert_c(txt_delete_c(cnext(eb->cur)), c);
+ }
+ }
+ break;
+ }
+}
+
int main(int argc, const char **argv) {
ed_init(&e);
e.homedir = str_from_cstr(getenv("HOME"));
@@ -1335,281 +1648,14 @@ int main(int argc, const char **argv) {
vui_init();
vui_curs_vis(1);
vui_redraw_fn(draw);
+ vui_redraw_ctx(&e);
while (e.bufn > 0) {
- EditBuf *eb = &e.buf[e.bufi];
draw(NULL);
arena_reset(&e.scratch); /* must happen after draw, so e.msg can persist */
-
- switch (e.mode) {
- case MODE_NORMAL:
- vui_curs_shape(VUI_CURS_BLOCK);
- break;
- case MODE_INSERT:
- vui_curs_shape(VUI_CURS_BAR);
- break;
- case MODE_REPLACE_ONE:
- case MODE_REPLACE_MULT:
- vui_curs_shape(VUI_CURS_UNDERLINE);
- break;
- }
-
vui_blit();
- u32 c = vui_key();
-
- switch (e.mode) {
- case MODE_NORMAL:
- switch (c) {
- case 'q':
- e.bufi = ed_buf_close(&e, e.bufi);
- break;
- case 'Q':
- while (e.bufn > 0) e.bufi = ed_buf_close(&e, e.bufi);
- break;
- case 'H':
- if (e.bufi > 0) e.bufi = e.bufi - 1;
- else e.bufi = e.bufn - 1;
- break;
- case 'L':
- e.bufi = (e.bufi + 1) % e.bufn;
- break;
- case 'z':
- case 'Z':
- case 0x13 /* ^S */:
- ed_buf_save(&e, e.bufi);
- e.msg = str_printf(&e.scratch, "%.02fk written", eb->txt->len / 1024.0);
- if (c == 'Z') e.bufi = ed_buf_close(&e, e.bufi);
- break;
- case 'i':
- e.mode = 1;
- break;
- case 'a': {
- e.mode = 1;
- TxtLoc l = eb->cur;
- TxtLoc e = end_of_line(l);
- l = txt_before(l, e) ? cnext(l) : e;
- eb->cur = l;
- } break;
- case 'I':
- e.mode = 1;
- eb->cur = logical_line_start(eb->cur);
- break;
- case 'A':
- e.mode = 1;
- eb->cur = end_of_line(eb->cur);
- break;
- case 'o':
- e.mode = 1;
- eb->cur = ins_newline(end_of_line(eb->cur));
- break;
- case 'O':
- e.mode = 1;
- eb->cur = ins_newline(bprev(start_of_line(eb->cur)));
- if (at_start(bprev(eb->cur))) eb->cur = bprev(eb->cur);
- break;
- case 'd': {
- TxtLoc before = eb->cur;
- if (motion(&eb->cur, vui_key())) {
- eb->cur = txt_delete_range(before, eb->cur);
- }
- } break;
- case 'D':
- eb->cur = txt_delete_range(eb->cur, end_of_line(eb->cur));
- break;
- case 'C':
- eb->cur = txt_delete_range(eb->cur, end_of_line(eb->cur));
- e.mode = 1;
- break;
- case 'S': {
- TxtLoc start = start_of_line(eb->cur);
- TxtLoc end = end_of_line(eb->cur);
- eb->cur = txt_delete_range(start, end);
- e.mode = 1;
- } break;
- case 'J': {
- TxtLoc l = end_of_line(eb->cur);
- if (txt_byte(l) == '\n') {
- do l = txt_delete_c(cnext(l));
- while (is_space(txt_chr(l)));
- l = cprev(txt_insert_c(l, ' '));
- eb->cur = l;
- }
- } break;
- case 'c': {
- TxtLoc before = eb->cur;
- if (motion(&eb->cur, vui_key())) {
- eb->cur = txt_delete_range(before, eb->cur);
- e.mode = 1;
- }
- break;
- }
- case 'x':
- eb->cur = txt_delete_c(cnext(eb->cur));
- break;
- case 's':
- eb->cur = txt_delete_c(cnext(eb->cur));
- e.mode = 1;
- break;
- case 'r':
- e.mode = MODE_REPLACE_ONE;
- break;
- case 'R':
- e.mode = MODE_REPLACE_MULT;
- break;
- case 'M': {
- TxtLoc start = prev_par(eb->cur);
- TxtLoc end = next_par(eb->cur);
- if (shell_replace(start, end, "fmt -w80 -u")) {
- err(1, "shell_replace");
- }
- } break;
-
- case ' ':
- switch ((u32)vui_key()) {
- case 'f': {
- Str s = select_file();
- 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 's': {
- int b = select_buf();
- if (b != -1) e.bufi = b;
- } break;
- case 'j': {
- TxtLoc l;
- if (select_func(eb->txt, &l)) eb->cur = l;
- } break;
- case 'm':
- build_file(eb->path, 1);
- break;
- case 'M':
- build_file(eb->path, 0);
- break;
- case 'r':
- run_file(eb->path, 1);
- break;
- case 'R':
- run_file(eb->path, 0);
- break;
- case 'D':
- debug_file(eb->path);
- break;
- case 'e':
- shell_run_no_prompt("make clean");
- break;
- case 'l':
- shell_run_no_prompt("git log --oneline %");
- break;
- case 'L':
- shell_run_no_prompt("git log %");
- break;
- case 'd':
- shell_run_no_prompt("git diff --color=always % | less -cr");
- break;
- case 'c':
- shell_run_no_prompt("git add % && git commit %");
- break;
- default:
- /* TODO: flash */
- break;
- }
- break;
-
- case '<':
- case '>': {
- TxtLoc end = eb->cur;
- VuiKey k = vui_key();
- if (k == '<' || k == '>' || motion(&end, k)) {
- if (k == '<' || k == '>') end = next_line_start(end);
- eb->cur = indent_dedent_lines(eb->cur, end, c == '<' ? -1 : 1);
- }
- } break;
-
- case '=': {
- Str s = get_input_line(S("File to open: "));
- if (s.n > 0) e.bufi = ed_buf_open(&e, str_to_cstr(s, &e.scratch));
- } break;
-
- case '+': {
- Str s = get_input_line(S("Replace file with: "));
- 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 '-': {
- Str s = get_input_line(S("Change path: "));
- if (s.n > 0) ed_buf_change_path(&e, e.bufi, s);
- } break;
-
- default:
- motion(&eb->cur, c);
- break;
- }
- break;
-
- case MODE_INSERT:
- switch (c) {
- case KEY_ESC:
- if (txt_after(eb->cur, start_of_line(eb->cur))) {
- eb->cur = cprev(eb->cur);
- }
- e.mode = 0;
- break;
- case KEY_BKSP:
- eb->cur = txt_delete_c(eb->cur);
- break;
- case 0x17 /* ^W */:
- eb->cur = txt_delete_range(prev_word(eb->cur), eb->cur);
- break;
- case 0x05 /* ^E */:
- eb->cur = txt_delete_range(eb->cur, next_word(eb->cur));
- break;
- case 0x0c /* ^L */:
- vui_redraw();
- break;
- case '\r':
- eb->cur = ins_newline(eb->cur);
- break;
- default:
- if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) eb->cur = txt_insert_c(eb->cur, c);
- break;
- }
- break;
-
- case MODE_REPLACE_ONE:
- if (txt_byte(eb->cur) != '\n' && c != KEY_BKSP && c <= KEY_UTF8_MAX) {
- eb->cur = cprev(txt_insert_c(txt_delete_c(cnext(eb->cur)), c));
- }
- e.mode = MODE_NORMAL;
- break;
-
- case MODE_REPLACE_MULT:
- if (c == '\x1b') {
- e.mode = MODE_NORMAL;
- eb->cur = cprev(eb->cur);
- } else if (c == KEY_BKSP) {
- if (txt_byte(bprev(eb->cur)) != '\n')
- eb->cur = cprev(eb->cur);
- } else if (c <= KEY_UTF8_MAX) {
- if (txt_byte(eb->cur) == '\n') {
- eb->cur = txt_insert_c(eb->cur, c);
- } else {
- eb->cur = txt_insert_c(txt_delete_c(cnext(eb->cur)), c);
- }
- }
- break;
- }
-
- if (e.mode == 1) e.count = 0;
+ edit_key(&e, vui_key());
+ if (e.mode == MODE_NORMAL) e.count = 0;
}
vui_fini();