diff options
| -rw-r--r-- | main.c | 616 |
1 files changed, 331 insertions, 285 deletions
@@ -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(); |
