diff options
| -rw-r--r-- | arena.h | 5 | ||||
| -rw-r--r-- | freelist.h | 31 | ||||
| -rw-r--r-- | main.c | 456 | ||||
| -rw-r--r-- | txt.c | 9 | ||||
| -rw-r--r-- | txt.h | 2 | ||||
| -rw-r--r-- | vui.c | 18 | ||||
| -rw-r--r-- | vui.h | 8 |
7 files changed, 343 insertions, 186 deletions
@@ -21,6 +21,7 @@ void vmem_free(void *ptr, size_t sz); Arena arena_init(size_t sz); void arena_free(Arena *a); +void arena_reset(Arena *a); void *arena_alloc(Arena *a, size_t n, size_t align); void *arena_realloc(Arena *a, void *old, size_t oldsz, size_t newsz, size_t align); @@ -64,6 +65,10 @@ void arena_free(Arena *a) { vmem_free(a->start, a->end - a->start); } +void arena_reset(Arena *a) { + a->beg = a->start; +} + void *arena_alloc(Arena *a, size_t sz, size_t align) { char *p = a->beg + (-(uintptr_t)a->beg & (align - 1)); if (p + sz > a->end) FAIL_WITH_MSG("arena out-of-memory"); diff --git a/freelist.h b/freelist.h new file mode 100644 index 0000000..69ef976 --- /dev/null +++ b/freelist.h @@ -0,0 +1,31 @@ +#ifndef FREELIST_H +#define FREELIST_H + +#include "arena.h" + +#define FREELIST_NEW(fl, a)\ + (__typeof__(*(fl)))freelist_new((void**)(fl), sizeof(**(fl)), _Alignof(__typeof__(**(fl))), a) + +#define FREELIST_FREE(fl, p)\ + freelist_free((void **)(fl), (void *)(p)) + +static inline void *freelist_new(void **fl, size_t sz, size_t align, Arena *a) { + if (*fl) { + void *p = *fl; + void *next = *(void**)p; + *fl = next; + return p; + } else { + /* must pad and align for ptr to next in freelist */ + if (sz < sizeof(void *)) sz = sizeof(void *); + if (align < _Alignof(void *)) align = _Alignof(void *); + return arena_alloc(a, sz, align); + } +} + +static inline void freelist_free(void **fl, void *p) { + *(void **)p = *fl; + *fl = p; +} + +#endif @@ -21,6 +21,7 @@ #include "vui.h" #include "utf8.h" #include "str.h" +#include "freelist.h" #define ED_BUF_MAX 1024 @@ -33,56 +34,58 @@ typedef struct { EditBufType type; Arena arena; Str path; - Txt txt; + Txt *txt; TxtLoc cur; } EditBuf; typedef enum { MODE_NORMAL, - MODE_INSERT + MODE_INSERT, + MODE_REPLACE_ONE, + MODE_REPLACE_MULT } EditMode; typedef struct { Arena scratch; EditBuf buf[ED_BUF_MAX]; u32 bufn, bufi; + Txt *txt_free; EditMode mode; u32 count; } Editor; -Arena scratch = { 0 }; -Txt txt = { 0 }; -TxtLoc cur = { 0 }; -int mode = 0; -u32 count = 0; +Editor e = { 0 }; -int ed_buf_new(Editor *e, const char *path) { +int ed_buf_open(Editor *e, const char *path) { if (e->bufn == ED_BUF_MAX) return -1; - u32 i = e->bufn; 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); + txt_load(b.txt, path); } else { b.path = S("*scratch*"); b.type = ED_BUF_SCRATCH; - txt_load_empty(&b.txt); + txt_load_empty(b.txt); } - b.cur = txt_end(&b.txt); - return i; + b.cur = txt_end(b.txt); + e->buf[e->bufn] = b; + return e->bufn++; } void ed_buf_free(EditBuf *b) { - txt_free(&b->txt); + txt_free(b->txt); arena_free(&b->arena); } void ed_init(Editor *e) { memset(e, 0, sizeof(Editor)); - e->scratch = arena_init(1L << 30), - e->bufi = ed_buf_new(e, NULL); + 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) { @@ -91,6 +94,24 @@ void ed_fini(Editor *e) { } } +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; +} + +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; + } +} + static inline int is_space(u32 c) { return c <= 0x20 && c != 0; } @@ -213,7 +234,7 @@ int shell_replace(TxtLoc start, TxtLoc end, const char *cmd) { { .fd = in, .events = POLLOUT }, { .fd = out, .events = POLLIN } }; - Str wrs = txt_collect_range(start, end, &scratch); + Str wrs = txt_collect_range(start, end, &e.scratch); DYNARR(char) rds = { 0 }; u32 wr_i = 0; while ((pfds[0].fd >= 0 || pfds[1].fd >= 0) && poll(pfds, 2, 200) >= 0) { @@ -233,7 +254,7 @@ int shell_replace(TxtLoc start, TxtLoc end, const char *cmd) { pfds[0].fd = -1; } if (pfds[1].revents & POLLIN) { - DA_AFIT(&rds, &scratch, rds.n + (-rds.n & (chunksz-1)) + chunksz); + DA_AFIT(&rds, &e.scratch, rds.n + (-rds.n & (chunksz-1)) + chunksz); isize r = read(out, &rds.v[rds.n], chunksz); if (r > 0) { rds.n += r; @@ -255,22 +276,29 @@ int shell_replace(TxtLoc start, TxtLoc end, const char *cmd) { * final one until it matches or gets too small. */ if (!txt_range_equal(start, end, (Str){rds.v,rds.n})) { - u32 b = txt_ofs(cur); + EditBuf *eb = &e.buf[e.bufi]; + u32 b = txt_ofs(eb->cur); start = txt_delete_range(start, end); end = txt_insert(start, rds.v, rds.n); - cur = txt_at(start.t, b); + eb->cur = txt_at(start.t, b); } return 0; } -int shell_run(const char *cmd) { - vui_disable(); - int r = system(cmd); - printf("[Press any key to resume editing]"); +#define ANY_KEY_PROMPT "[Press any key to resume editing]" + +static void press_any_key(void) { + printf("%s", ANY_KEY_PROMPT); fflush(stdout); vui_enable(); vui_key(); vui_redraw(); +} + +int shell_run(const char *cmd) { + vui_disable(); + int r = system(cmd); + press_any_key(); return r; } @@ -279,14 +307,14 @@ int shell_run(const char *cmd) { #define ODD_ATTR (FG_CYAN | BG_BLACK) #define EVEN_ATTR (FG_WHITE | BG_BLACK) -void find_view_window(TxtLoc l, TxtLoc *start, TxtLoc *end) { - u32 u = LINES / 2; +void find_view_window(TxtLoc l, TxtLoc *start, TxtLoc *end, u32 lines) { + u32 u = lines / 2; TxtLoc a = l; for (u32 i = 0; i < u; i++) a = prev_newline(a); u32 n = 0; TxtLoc b = a; - while (!at_end(b) && n++ < LINES) b = next_newline(b); - while (!at_start(a) && n++ < LINES) a = prev_newline(a); + while (!at_end(b) && n++ < lines) b = next_newline(b); + while (!at_start(a) && n++ < lines) a = prev_newline(a); if (!at_start(a) && txt_byte(a) == '\n') a = bnext(a); *start = a; *end = b; @@ -294,20 +322,37 @@ void find_view_window(TxtLoc l, TxtLoc *start, TxtLoc *end) { void draw(void *ctx) { (void)ctx; + EditBuf *eb = &e.buf[e.bufi]; + vui_clear(); int lmarg = 0; - int x = lmarg, y = 0; + int x = lmarg, y = 1; TxtLoc start, end; - find_view_window(cur, &start, &end); + find_view_window(eb->cur, &start, &end, LINES - 1); - vui_aprintf(-1, 0, ODD_ATTR, "%u piece(s)", txt.ptbl.n); - for (u32 i = 0; i < txt.ptbl.n; i++) { - TxtPiece *p = &txt.ptbl.v[i]; + vui_aprintf(-1, 0, ODD_ATTR, "%u piece(s)", eb->txt->ptbl.n); + for (u32 i = 0; i < eb->txt->ptbl.n; i++) { + TxtPiece *p = &eb->txt->ptbl.v[i]; VuiAttr a = i&1 ? ODD_ATTR : EVEN_ATTR; vui_aprintf(-1, i+1, a, "%u, %u (%s)", p->ofs, p->n, p->buf == TXT_ADD ? "add" : "src"); } - TxtLoc l = cur; + { + VuiAttr norm = FG_WHITE | BG_BLACK; + VuiAttr sel = FG_BLACK | BG_WHITE; + int x = 0; + for (u32 i = 0; i < e.bufn; i++) { + EditBuf *b = &e.buf[i]; + VuiAttr a = i == e.bufi ? sel : norm; + x += vui_aprintf(x, 0, a, " %.*s", (int)b->path.n, b->path.s); + if (b->type == ED_BUF_FILE && b->txt->dirty) x += vui_putsa(x, 0, "* ", a); + else vui_chra(x++, 0, ' ', a); + } + int n = COLS; + while (x < n) vui_chra(x++, 0, ' ', norm); + } + + TxtLoc l = eb->cur; int cur_found = 0; while (txt_before(start, end)) { if (l.p == start.p && l.i == start.i) { @@ -325,14 +370,16 @@ void draw(void *ctx) { vui_chr(x++, y, c); } } - ASSERT(start.i <= txt.ptbl.v[start.p].n); + + ASSERT(start.i <= eb->txt->ptbl.v[start.p].n); if (!cur_found) vui_curs_pos(x, y); + /* u32 c = txt_chr(l); - vui_printf(-1, -4, "%u", count); - vui_aprintf(-1, -3, mode ? FG_BCYAN : FG_CYAN, "%s", mode ? "INSERT" : "NORMAL"); - vui_printf(-1, -1, "%u - %u.%u - %02x (%c)", txt_ofs(cur), l.p, l.i, c, (c < 0x20 || c > 0x7e) ? ' ' : c); - u32 used = scratch.beg - scratch.start, max = scratch.end - scratch.start; - vui_printf(-1, -2, "scratch %.02f/%.02fk", used/1024.0, max/1024.0); + vui_printf(-1, -4, "%u", e.count); + vui_aprintf(-1, -3, e.mode ? FG_BCYAN : FG_CYAN, "%s", e.mode ? "INSERT" : "NORMAL"); + vui_printf(-1, -1, "%u - %u.%u - %02x (%c)", txt_ofs(eb->cur), l.p, l.i, c, (c < 0x20 || c > 0x7e) ? ' ' : c); + u32 used = e.scratch.beg - e.scratch.start, max = e.scratch.end - e.scratch.start; + vui_printf(-1, -2, "e.scratch %.02f/%.02fk", used/1024.0, max/1024.0);*/ } int motion(TxtLoc *lp, u32 c) { @@ -341,13 +388,13 @@ int motion(TxtLoc *lp, u32 c) { for (;;) { switch (c) { case '0': - if (!count) goto loop; + if (!e.count) goto loop; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - count = (count % 100000000) * 10 + c - '0'; + e.count = (e.count % 100000000) * 10 + c - '0'; break; case KEY_ESC: - count = 0; + e.count = 0; return 0; default: goto loop; @@ -407,11 +454,11 @@ loop: break; case KEY_HOME | KEY_CTRL_BIT: case 'g': - l = txt_start(&txt); + l = txt_start(l.t); break; case KEY_END | KEY_CTRL_BIT: case 'G': - l = txt_end(&txt); + l = txt_end(l.t); break; case 'f': { u32 k = vui_key(); @@ -442,43 +489,15 @@ loop: default: return 0; } - if (count > 1 && (txt_before(l, last_loc) || txt_after(l, last_loc))) { - count--; + if (e.count > 1 && (txt_before(l, last_loc) || txt_after(l, last_loc))) { + e.count--; goto loop; } - count = 0; + e.count = 0; *lp = l; return 1; } -#include <time.h> - -void test_edits(void) { - for (u32 iter = 0; iter < 8192; iter++) { - ASSERT(iter != 385); - u32 n = rand() % 100; - cur = txt_at(&txt, rand() % txt.len); - if (rand() & 1) { - cur = txt_delete(cur, n); - } else { - for (u32 j = 0; j < n; j++) { - const char ch[] = { - 'a','b','c','d','e','f','g','h','j','k','l','i', - 'm','o','p','q','r','s','t','u','v','w','x','y','z','\n', - 'A','B','C','D','E','F','G','H','J','K','L','I', - 'M','O','P','Q','R','S','T','U','V','W','X','Y','Z', - }; - cur = txt_insert_c(cur, ch[rand() % COUNTOF(ch)]); - } - } - if (txt.ptbl.n > 1) { - for (u32 i = 0; i < txt.ptbl.n; i++) { - ASSERT(txt.ptbl.v[i].n > 0); - } - } - } -} - TxtLoc ins_newline(TxtLoc l) { u32 tabs = 0; TxtLoc start = start_of_line(l); @@ -498,8 +517,8 @@ static Str normalize_path(Str s) { return (Str) { 0, 0 }; } Str d = str_from_cstr(pwd); - str_catc(&d, '/', &scratch); - str_cat(&d, s, &scratch); + str_catc(&d, '/', &e.scratch); + str_cat(&d, s, &e.scratch); s = d; } DYNARR(Str) bits = { 0 }; @@ -510,14 +529,14 @@ static Str normalize_path(Str s) { } else if (str_eql(c.head, S("."))) { /* do nothing */ } else if (c.head.n > 0) { - DA_APUSH(&bits, &scratch, c.head); + DA_APUSH(&bits, &e.scratch, c.head); } s = c.tail; } Str path = { 0 }; for (u32 i = 0; i < bits.n; i++) { - str_catc(&path, '/', &scratch); - str_cat(&path, bits.v[i], &scratch); + str_catc(&path, '/', &e.scratch); + str_cat(&path, bits.v[i], &e.scratch); } return path; } @@ -534,14 +553,49 @@ int is_wed_path(Str path) { return str_starts(normalize_path(path), S(WED_DEV_DIR)); } +void str_cat_u32(Str *out, u32 c, Arena *a) { + char buf[32]; + u32 n = 0; + do { + buf[32 - ++n] = (c % 10) + '0'; + c /= 10; + } while (c); + str_cat(out, (Str) { &buf[sizeof(buf) - n], n }, a); +} + +int str_to_u32(Str s, u32 *out) { + u32 x = 0; + for (u32 i = 0; i < s.n; i++) { + char c = s.s[i]; + if (!(c >= '0' && c <= '9')) return -1; + x = (x * 10) + c - '0'; + } + *out = x; + return 0; +} + +void make_edit_args(Str *a) { + for (u32 i = 0; i < e.bufn; i++) { + if (e.buf[i].type != ED_BUF_FILE) continue; + if (i > 0) str_catc(a, ' ', &e.scratch); + str_cat(a, e.buf[i].path, &e.scratch); + str_cat(a, S("@"), &e.scratch); + str_cat_u32(a, txt_ofs(e.buf[i].cur), &e.scratch); + } +} + static void run_file(Str path, int debugp) { path = normalize_path(path); if (is_wed_path(path)) { - vui_fini(); - system(debugp ? "make DEBUG=1" : "make"); - char ofs[32]; - sprintf(ofs, "-b%d", txt_ofs(cur)); - execl(WED_DEV_EXE, WED_DEV_EXE, ofs, str_to_cstr(path, &scratch), 0); + vui_disable(); + if (system(debugp ? "make DEBUG=1" : "make")) { + press_any_key(); + } else { + vui_fini(); + Str cmd = S("exec " WED_DEV_EXE " "); + make_edit_args(&cmd); + execl("/bin/sh", "sh", "-c", str_to_cstr(cmd, &e.scratch), 0); + } } else { shell_run(debugp ? "make DEBUG=1 run" : "make run"); } @@ -550,126 +604,161 @@ static void run_file(Str path, int debugp) { static void debug_file(Str path) { if (is_wed_path(path)) { vui_disable(); - system("make DEBUG=1"); - char ofs[32]; - sprintf(ofs, "-b%d", txt_ofs(cur)); - Str cmd = S("exec gdb -ex run --args " WED_DEV_EXE " "); - str_cat(&cmd, str_from_cstr(ofs), &scratch); - execl("/bin/sh", "sh", "-c", str_to_cstr(cmd, &scratch), 0); + if (system("make DEBUG=1")) { + press_any_key(); + } else { + vui_fini(); + Str cmd = S("exec gdb -ex run --args " WED_DEV_EXE " "); + make_edit_args(&cmd); + execl("/bin/sh", "sh", "-c", str_to_cstr(cmd, &e.scratch), 0); + } } else { Str cmd = S("gdb -ex run --args "); - str_cat(&cmd, path, &scratch); - shell_run(str_to_cstr(cmd, &scratch)); + str_cat(&cmd, path, &e.scratch); + shell_run(str_to_cstr(cmd, &e.scratch)); } } int main(int argc, const char **argv) { - /* TODO: Have a command-line argument to start editing at the given byte offset, - * e.g. wed -b 1027 */ - - scratch = arena_init(128L << 20); - - int start_ofs = -1; - if (argc > 1 && !strncmp(argv[1], "-b", 2)) { - start_ofs = atoi(argv[1] + 2); - if (argc > 2) { - MOVE(&argv[1], &argv[2], argc - 2); + ed_init(&e); + + if (argc > 1) { + for (int i = 1; i < argc; i++) { + Str s = str_from_cstr(argv[i]); + Cut c = str_cut(s, '@'); + e.bufi = ed_buf_open(&e, str_to_cstr(c.head, &e.scratch)); + if (c.tail.n > 1) { + EditBuf *eb = &e.buf[e.bufi]; + u32 ofs; + if (str_to_u32(c.tail, &ofs)) { + fprintf(stderr, "invalid byte offset in %s\n", argv[i]); + return 1; + } + eb->cur = txt_at(eb->txt, ofs); + } } - argc--; + } else { + e.bufi = ed_buf_open(&e, "main.c"); } - const char *path = "main.c"; - if (argc > 1) path = argv[1]; - vui_init(); vui_curs_vis(1); vui_redraw_fn(draw); - if (txt_load(&txt, path)) err(1, "couldn't open file"); - cur = start_ofs < 0 ? txt_end(&txt) : txt_at(&txt, start_ofs); - - for (;;) { - scratch.beg = scratch.start; + while (e.bufn > 0) { + arena_reset(&e.scratch); + EditBuf *eb = &e.buf[e.bufi]; draw(NULL); - vui_curs_shape(mode ? VUI_CURS_BAR : VUI_CURS_BLOCK); + + 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 (mode) { - case 0: + + switch (e.mode) { + case MODE_NORMAL: switch (c) { case 'q': - goto brk; + 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 '[': + if (e.bufi > 0) e.bufi = e.bufi - 1; + else e.bufi = e.bufn - 1; + break; + case ']': + e.bufi = (e.bufi + 1) % e.bufn; + break; case 'z': case 'Z': case 0x13 /* ^S */: - txt_save(&txt, path); + ed_buf_save(&e, e.bufi); //txt_load(&txt, "test.txt"); - if (c == 'Z') goto brk; + if (c == 'Z') e.bufi = ed_buf_close(&e, e.bufi); break; case 'i': - mode = 1; + e.mode = 1; break; case 'a': { - mode = 1; - TxtLoc l = cur; + e.mode = 1; + TxtLoc l = eb->cur; TxtLoc e = end_of_line(l); l = txt_before(l, e) ? cnext(l) : e; - cur = l; + eb->cur = l; } break; case 'I': - mode = 1; - cur = start_of_line(cur); + e.mode = 1; + eb->cur = start_of_line(eb->cur); break; case 'A': - mode = 1; - cur = end_of_line(cur); + e.mode = 1; + eb->cur = end_of_line(eb->cur); break; case 'o': - mode = 1; - cur = ins_newline(end_of_line(cur)); + e.mode = 1; + eb->cur = ins_newline(end_of_line(eb->cur)); break; case 'O': - mode = 1; - cur = ins_newline(bprev(start_of_line(cur))); - if (at_start(bprev(cur))) cur = bprev(cur); + 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 = cur; - if (motion(&cur, vui_key())) { - cur = txt_delete_range(before, cur); + TxtLoc before = eb->cur; + if (motion(&eb->cur, vui_key())) { + eb->cur = txt_delete_range(before, eb->cur); } } break; case 'D': - cur = txt_delete_range(cur, end_of_line(cur)); + eb->cur = txt_delete_range(eb->cur, end_of_line(eb->cur)); break; case 'C': - cur = txt_delete_range(cur, end_of_line(cur)); - mode = 1; + eb->cur = txt_delete_range(eb->cur, end_of_line(eb->cur)); + e.mode = 1; break; case 'S': { - TxtLoc start = start_of_line(cur); - TxtLoc end = end_of_line(cur); - cur = txt_delete_range(start, end); - mode = 1; + 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 'c': { - TxtLoc before = cur; - if (motion(&cur, vui_key())) { - cur = txt_delete_range(before, cur); - mode = 1; + TxtLoc before = eb->cur; + if (motion(&eb->cur, vui_key())) { + eb->cur = txt_delete_range(before, eb->cur); + e.mode = 1; } break; } case 'x': - cur = txt_delete_c(cnext(cur)); + eb->cur = txt_delete_c(cnext(eb->cur)); break; case 's': - cur = txt_delete_c(cnext(cur)); - mode = 1; + 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(cur); - TxtLoc end = next_par(cur); + TxtLoc start = prev_par(eb->cur); + TxtLoc end = next_par(eb->cur); if (shell_replace(start, end, "fmt -w80 -u")) { err(1, "shell_replace"); } @@ -677,19 +766,19 @@ int main(int argc, const char **argv) { case ' ': switch ((u32)vui_key()) { case 'm': - build_file(str_from_cstr(path), 1); + build_file(eb->path, 1); break; case 'M': - build_file(str_from_cstr(path), 0); + build_file(eb->path, 0); break; case 'r': - run_file(str_from_cstr(path), 1); + run_file(eb->path, 1); break; case 'R': - run_file(str_from_cstr(path), 0); + run_file(eb->path, 0); break; case 'D': - debug_file(str_from_cstr(path)); + debug_file(eb->path); break; case 'e': vui_disable(); @@ -703,49 +792,68 @@ int main(int argc, const char **argv) { } break; default: - motion(&cur, c); + motion(&eb->cur, c); break; } break; - case 1: + + case MODE_INSERT: switch (c) { case KEY_ESC: - if (txt_after(cur, start_of_line(cur))) { - cur = cprev(cur); + if (txt_after(eb->cur, start_of_line(eb->cur))) { + eb->cur = cprev(eb->cur); } - mode = 0; + e.mode = 0; break; case KEY_BKSP: - cur = txt_delete_c(cur); + eb->cur = txt_delete_c(eb->cur); break; case 0x17 /* ^W */: - cur = txt_delete_range(prev_word(cur), cur); + eb->cur = txt_delete_range(prev_word(eb->cur), eb->cur); break; case 0x05 /* ^E */: - cur = txt_delete_range(cur, next_word(cur)); + eb->cur = txt_delete_range(eb->cur, next_word(eb->cur)); break; case 0x0c /* ^L */: vui_redraw(); break; case '\r': - cur = ins_newline(cur); + eb->cur = ins_newline(eb->cur); break; default: - if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) cur = txt_insert_c(cur, c); + if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) eb->cur = txt_insert_c(eb->cur, c); break; } break; - } - if (mode == 1) count = 0; - ASSERT(txt_valid_loc(cur)); - if (txt.ptbl.n > 1) { - for (u32 i = 0; i < txt.ptbl.n; i++) { - ASSERT(txt.ptbl.v[i].n > 0); + + 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; } -brk: + vui_fini(); - arena_free(&scratch); + arena_free(&e.scratch); return 0; } @@ -127,6 +127,7 @@ loop: goto loop; } ASSERT(txt_valid_loc(l)); + l.t->dirty = 1; return resolve_loc(l); } @@ -229,6 +230,13 @@ done: return 0; } +void txt_load_empty(Txt *b) { + memset(b, 0, sizeof(Txt)); + txt_buf_fit(b, TXT_SRC, 32); + txt_buf_fit(b, TXT_ADD, 8192); + txt_insert_piece(b, 0, TXT_ADD, 0, b->len); +} + int txt_save(Txt *t, const char *path) { FILE *f = fopen(path, "wb"); if (!f) return -1; @@ -239,6 +247,7 @@ int txt_save(Txt *t, const char *path) { } int e = ferror(f); fclose(f); + t->dirty = 0; return e ? -1 : 0; } @@ -24,6 +24,7 @@ typedef struct { DYNARR(TxtPiece) ptbl; TxtBuf buf[2]; u32 len; + int dirty; } Txt; typedef struct { @@ -37,6 +38,7 @@ TxtLoc txt_split_piece(TxtLoc l); void txt_remove_piece(Txt *b, u32 pi); void txt_insert_piece(Txt *b, u32 pi, TxtBufIdx buf, u32 ofs, u32 n); +void txt_load_empty(Txt *b); int txt_load(Txt *b, const char *path); int txt_save(Txt *b, const char *path); void txt_free(Txt *b); @@ -615,26 +615,28 @@ static void truncate_span(int *x, unsigned *nptr) { *nptr = n; } -void vui_putsna(int x, int y, const char *s, unsigned srcn, VuiAttr a) { +u32 vui_putsna(int x, int y, const char *s, unsigned srcn, VuiAttr a) { u32 n = utf8_decode_len(s, srcn); truncate_span(&x, &n); - if (n < 1 || y < 0 || y >= (int)LINES) return; + if (n < 1 || y < 0 || y >= (int)LINES) return 0; VuiChar *dest = &CHR(x, y); utf8_decode(dest, s, n); for (u32 i = 0; i < n; i++) ASSERT(dest[i] > 0 && dest[i] <= KEY_UTF8_MAX); + u32 r = n; for (uint16_t *pa = &ATTR(x, y); n--;) *pa++ = a; + return r; } -void vui_putsn(int x, int y, const char *s, unsigned n) { - vui_putsna(x, y, s, n, ATTR_DEFAULT); +u32 vui_putsn(int x, int y, const char *s, unsigned n) { + return vui_putsna(x, y, s, n, ATTR_DEFAULT); } -void vui_putsa(int x, int y, const char *s, VuiAttr a) { - vui_putsna(x, y, s, strlen(s), a); +u32 vui_putsa(int x, int y, const char *s, VuiAttr a) { + return vui_putsna(x, y, s, strlen(s), a); } -void vui_puts(int x, int y, const char *s) { - vui_putsn(x, y, s, strlen(s)); +u32 vui_puts(int x, int y, const char *s) { + return vui_putsn(x, y, s, strlen(s)); } int vui_avprintf(int x, int y, VuiAttr a, const char *fmt, va_list ap) { @@ -132,10 +132,10 @@ void vui_scroll(int dx, int dy); void vui_chr(int x, int y, VuiChar c); void vui_chra(int x, int y, VuiChar c, VuiAttr a); -void vui_puts(int x, int y, const char *s); -void vui_putsa(int x, int y, const char *s, VuiAttr a); -void vui_putsn(int x, int y, const char *s, unsigned n); -void vui_putsna(int x, int y, const char *s, unsigned n, VuiAttr a); +unsigned vui_puts(int x, int y, const char *s); +unsigned vui_putsa(int x, int y, const char *s, VuiAttr a); +unsigned vui_putsn(int x, int y, const char *s, unsigned n); +unsigned vui_putsna(int x, int y, const char *s, unsigned n, VuiAttr a); int vui_avprintf(int x, int y, VuiAttr a, const char *fmt, va_list ap); int vui_aprintf(int x, int y, VuiAttr a, const char *fmt, ...); |
