diff options
| -rw-r--r-- | main.c | 185 | ||||
| -rw-r--r-- | txt.c | 65 | ||||
| -rw-r--r-- | txt.h | 6 | ||||
| -rw-r--r-- | vui.c | 5 |
4 files changed, 162 insertions, 99 deletions
@@ -21,7 +21,7 @@ Arena scratch = { 0 }; Txt txt = { 0 }; -u32 cur = 0; +TxtLoc cur = { 0 }; int mode = 0; u32 count = 0; @@ -137,6 +137,8 @@ int shell_replace(TxtLoc start, TxtLoc end, const char *cmd) { end = t; } + u32 cur_ofs = txt_range_len(cur, end); + int in, out; int p = popen2(cmd, &in, &out); if (p < 0) return -1; @@ -151,24 +153,27 @@ int shell_replace(TxtLoc start, TxtLoc end, const char *cmd) { } l = bnext(l); } + txt_write_range(in, start, end); close(in); - txt_delete_range(start, end); + start = txt_delete_range(start, end); DYNARR(char) buf = { 0 }; - DA_AFIT(&buf, &scratch, 1024); + DA_AFIT(&buf, &scratch, 8192); for (;;) { - isize sz = read(out, &buf.v[buf.n], 1024); + isize sz = read(out, &buf.v[buf.n], 8192); if (sz < 0) { close(out); return -1; } if (!sz) break; buf.n += sz; - DA_AFIT(&buf, &scratch, buf.n + 1024); + DA_AFIT(&buf, &scratch, buf.n + (-buf.n & 8191) + 8192); } - - start = txt_insert(start, buf.v, buf.n); close(out); + + end = txt_insert(start, buf.v, buf.n); + cur = end; + while (cur_ofs--) cur = cprev(cur); return 0; } @@ -177,8 +182,7 @@ int shell_replace(TxtLoc start, TxtLoc end, const char *cmd) { #define ODD_ATTR (FG_CYAN | BG_BLACK) #define EVEN_ATTR (FG_WHITE | BG_BLACK) -void find_view_window(Txt *t, u32 curs, TxtLoc *start, TxtLoc *end) { - TxtLoc l = txt_at(t, curs); +void find_view_window(TxtLoc l, TxtLoc *start, TxtLoc *end) { u32 u = LINES / 2; TxtLoc a = l; for (u32 i = 0; i < u; i++) a = prev_newline(a); @@ -197,7 +201,7 @@ void draw(void *ctx) { int lmarg = 0; int x = lmarg, y = 0; TxtLoc start, end; - find_view_window(&txt, cur, &start, &end); + find_view_window(cur, &start, &end); vui_aprintf(-1, 0, ODD_ATTR, "%u piece(s)", txt.ptbl.n); for (u32 i = 0; i < txt.ptbl.n; i++) { @@ -206,7 +210,7 @@ void draw(void *ctx) { vui_aprintf(-1, i+1, a, "%u, %u (%s)", p->ofs, p->n, p->buf == TXT_ADD ? "add" : "src"); } - TxtLoc l = txt_at(&txt, cur); + TxtLoc l = cur; int cur_found = 0; while (txt_before(start, end)) { if (l.p == start.p && l.i == start.i) { @@ -229,104 +233,106 @@ void draw(void *ctx) { 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)", cur, l.p, l.i, c, (c < 0x20 || c > 0x7e) ? ' ' : c); + 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); } -int motion(u32 c) { - u32 last_cur = cur; +int motion(TxtLoc *lp, u32 c) { + TxtLoc l = *lp; + TxtLoc last_loc = l; loop: switch (c) { case KEY_LEFT: case 'h': - cur = move_char_back(&txt, cur); - break; - case KEY_LEFT | KEY_CTRL_BIT: - case 'b': - cur = move_word_back(&txt, cur); + l = cprev(l); break; case KEY_RIGHT: case 'l': - cur = move_char_fwd(&txt, cur); + l = cnext(l); + break; + case KEY_LEFT | KEY_CTRL_BIT: + case 'b': + l = prev_word(l); break; case KEY_RIGHT | KEY_CTRL_BIT: case 'w': - cur = move_word_fwd(&txt, cur); + l = next_word(l); break; case KEY_UP: case 'k': - cur = move_line_up(&txt, cur); + l = prev_line(l); break; case KEY_DOWN: case 'j': - cur = move_line_down(&txt, cur); + l = next_line(l); break; case KEY_UP | KEY_CTRL_BIT: case '{': - cur = move_par_up(&txt, cur); + l = prev_par(l); break; case KEY_DOWN | KEY_CTRL_BIT: case '}': - cur = move_par_down(&txt, cur); + l = next_par(l); break; case KEY_PGUP: - for (u32 i = 0; i < LINES; i += 3) cur = move_line_up(&txt, cur); + for (u32 i = 0; i < LINES; i += 3) l = prev_line(l); break; case KEY_PGDN: - for (u32 i = 0; i < LINES; i += 3) cur = move_line_down(&txt, cur); + for (u32 i = 0; i < LINES; i += 3) l = next_line(l); break; case KEY_HOME: case '^': case '0': - cur = txt_ofs(start_of_line(txt_at(&txt, cur))); + l = start_of_line(l); break; case KEY_END: case '$': - cur = txt_ofs(next_newline(txt_at(&txt, cur))); + l = end_of_line(l); break; case KEY_HOME | KEY_CTRL_BIT: case 'g': - cur = 0; + l = txt_start(&txt); break; case KEY_END | KEY_CTRL_BIT: case 'G': - cur = txt.len; + l = txt_end(&txt); break; case 'f': { - u32 n = vui_key(); - TxtLoc l = cnext(txt_at(&txt, cur)); - for (TxtLoc t = l;;) { - u32 c = txt_chr_next(&t); - if (c == '\n' || c == 0) break; - if (c == n) { - cur = txt_ofs(l); + u32 k = vui_key(); + TxtLoc t = cnext(l); + for (TxtLoc n = l;;) { + u32 x = txt_chr_next(&n); + if (x == '\n' || x == 0) break; + if (x == k) { + l = t; break; } - l = t; + t = n; } } break; case 'F': { - u32 n = vui_key(); - TxtLoc l = bprev(txt_at(&txt, cur)); + u32 k = vui_key(); + TxtLoc t = cprev(l); for (;;) { - u32 c = txt_chr(l); - if (c == '\n') break; - if (c == n) { - cur = txt_ofs(l); + u32 x = txt_chr(l); + if (x == '\n') break; + if (x == k) { + l = t; break; } - l = bprev(l); + t = bprev(t); } } break; default: return 0; } - if (count > 1 && last_cur != cur) { + if (count > 1 && (txt_before(l, last_loc) || txt_after(l, last_loc))) { count--; goto loop; } count = 0; + *lp = l; return 1; } @@ -338,7 +344,7 @@ int main(int argc, const char **argv) { const char *path = "test.txt"; if (argc > 1) path = argv[1]; if (txt_load(&txt, path)) err(1, "couldn't open file"); - cur = txt.len; + cur = txt_end(&txt); for (;;) { scratch.beg = scratch.start; draw(NULL); @@ -362,69 +368,64 @@ int main(int argc, const char **argv) { break; case 'a': { mode = 1; - TxtLoc l = txt_at(&txt, cur); + TxtLoc l = cur; TxtLoc e = end_of_line(l); l = txt_before(l, e) ? cnext(l) : e; - cur = txt_ofs(l); + cur = l; } break; case 'I': mode = 1; - cur = txt_ofs(start_of_line(txt_at(&txt, cur))); + cur = start_of_line(cur); break; case 'A': mode = 1; - cur = txt_ofs(end_of_line(txt_at(&txt, cur))); + cur = end_of_line(cur); break; case 'o': mode = 1; - cur = txt_ofs(end_of_line(txt_at(&txt, cur))); + cur = end_of_line(cur); goto ins_newline; case 'O': mode = 1; - cur = txt_ofs(bprev(start_of_line(txt_at(&txt, cur)))); + cur = bprev(start_of_line(cur)); goto ins_newline; case 'd': { - u32 before = cur; - if (motion(vui_key())) { - del_between(&txt, before, cur); - if (before < cur) cur = before; + TxtLoc before = cur; + if (motion(&cur, vui_key())) { + cur = txt_delete_range(before, cur); } } break; - case 'D': { - u32 end = txt_ofs(end_of_line(txt_at(&txt, cur))); - del_between(&txt, cur, end); - } break; - case 'C': { - u32 end = txt_ofs(end_of_line(txt_at(&txt, cur))); - del_between(&txt, cur, end); + case 'D': + cur = txt_delete_range(cur, end_of_line(cur)); + break; + case 'C': + cur = txt_delete_range(cur, end_of_line(cur)); mode = 1; - } break; + break; case 'S': { - TxtLoc l = start_of_line(txt_at(&txt, cur)); - u32 end = txt_ofs(end_of_line(l)); - del_between(&txt, txt_ofs(l), end); - cur = txt_ofs(l); + TxtLoc start = start_of_line(cur); + TxtLoc end = end_of_line(cur); + cur = txt_delete_range(start, end); mode = 1; } break; case 'c': { - u32 before = cur; - if (motion(vui_key())) { - del_between(&txt, before, cur); - if (before < cur) cur = before; + TxtLoc before = cur; + if (motion(&cur, vui_key())) { + cur = txt_delete_range(before, cur); + mode = 1; } - mode = 1; break; } case 'x': - cur = txt_ofs(txt_delete_c(cnext(txt_at(&txt, cur)))); + cur = txt_delete_c(cnext(cur)); break; case 's': - cur = txt_ofs(txt_delete_c(cnext(txt_at(&txt, cur)))); + cur = txt_delete_c(cnext(cur)); mode = 1; break; case '0': if (!count) { - motion(c); + motion(&cur, c); break; } /* fallthrough */ @@ -433,57 +434,57 @@ int main(int argc, const char **argv) { count = (count % 100000000) * 10 + c - '0'; break; case 'M': { - TxtLoc start = prev_par(txt_at(&txt, cur)); - TxtLoc end = next_par(txt_at(&txt, cur)); + TxtLoc start = prev_par(cur); + TxtLoc end = next_par(cur); if (shell_replace(start, end, "fmt -w80 -u")) { err(1, "shell_replace"); } - if (cur > txt.len) cur = txt.len; }; default: - motion(c); + motion(&cur, c); break; } break; case 1: switch (c) { case KEY_ESC: - if (txt_after(txt_at(&txt, cur), start_of_line(txt_at(&txt, cur)))) { - cur = txt_ofs(cprev(txt_at(&txt, cur))); + if (txt_after(cur, start_of_line(cur))) { + cur = cprev(cur); } mode = 0; break; case KEY_BKSP: - cur = txt_ofs(txt_delete_c(txt_at(&txt, cur))); + cur = txt_delete_c(cur); break; case 0x17 /* ^W */: - cur = del_between(&txt, move_word_back(&txt, cur), cur); + cur = txt_delete_range(prev_word(cur), cur); break; case 0x05 /* ^E */: - cur = del_between(&txt, cur, move_word_fwd(&txt, cur)); + cur = txt_delete_range(cur, next_word(cur)); break; case '\r': { ins_newline:; /* u32 tabs = 0; - TxtLoc start = start_of_line(txt_at(&txt, cur)); + TxtLoc start = start_of_line(cur); for (TxtLoc l = start; txt_byte(l) == '\t'; l = bnext(l)) tabs++; while (txt_byte(txt_at(&txt, cur - 1)) == '\t') { - cur = txt_ofs(txt_delete(txt_at(&txt, cur), 1)); + cur = txt_ofs(txt_delete(cur, 1)); } */ - cur = txt_ofs(txt_insert_c(txt_at(&txt, cur), '\n')); - //while (tabs--) cur = txt_ofs(txt_insert_c(txt_at(&txt, cur), '\t')); + cur = txt_insert_c(cur, '\n'); + //while (tabs--) cur = txt_ofs(txt_insert_c(cur, '\t')); } break; default: - if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) cur = txt_ofs(txt_insert_c(txt_at(&txt, cur), c)); + if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) cur = txt_insert_c(cur, c); break; } break; } if (mode == 1) count = 0; + ASSERT(txt_valid_loc(cur)); } brk: vui_fini(); @@ -58,7 +58,7 @@ static void txt_buf_append(Txt *b, TxtBufIdx bi, const char *s, u32 n) { static isize txt_buf_dedup(Txt *b, TxtBufIdx bi, const char *s, u32 n) { TxtBuf *buf = &b->buf[bi]; - if (buf->n >= n && !memcmp(buf->s + (buf->n - n), s, n)) { + if (buf->n >= n && buf->s && !memcmp(buf->s + (buf->n - n), s, n)) { return buf->n - n; } else { return -1; @@ -128,6 +128,14 @@ static TxtLoc txt_join_or_kill(TxtLoc l) { return l; } +static inline TxtLoc resolve_loc(TxtLoc l) { + if (l.p + 1 < l.t->ptbl.n && l.i == l.t->ptbl.v[l.p].n) { + l.p++; + l.i = 0; + } + return l; +} + TxtLoc txt_insert(TxtLoc l, const char *s, u32 n) { l = txt_add_piece(l.t, l); TxtPiece *p = &l.t->ptbl.v[l.p]; @@ -142,7 +150,7 @@ TxtLoc txt_insert(TxtLoc l, const char *s, u32 n) { l.i = p->n; l = txt_join_or_kill(l); l.i = l.t->ptbl.v[l.p].n; - return l; + return resolve_loc(l); } TxtLoc txt_insert_c(TxtLoc l, u32 ch) { @@ -173,15 +181,28 @@ TxtLoc txt_delete(TxtLoc l, u32 n) { } l = txt_join_or_kill(l); } - return txt_join_or_kill(l); + return resolve_loc(txt_join_or_kill(l)); +} + +u32 txt_range_len(TxtLoc lo, TxtLoc hi) { + u32 n = 0; + while (lo.p < hi.p) { + n += lo.t->ptbl.v[lo.p].n - lo.i; + lo.p++; + lo.i = 0; + } + n += hi.i - lo.i; + return n; } TxtLoc txt_delete_range(TxtLoc lo, TxtLoc hi) { /* TODO: figure out nr. of chars, then delete all at once */ - while (txt_before(lo, hi)) { - hi = txt_delete(hi, 1); + if (txt_before(hi, lo)) { + TxtLoc t = lo; + lo = hi; + hi = t; } - return lo; + return txt_delete(hi, txt_range_len(lo, hi)); } TxtLoc txt_delete_c(TxtLoc l) { @@ -242,8 +263,28 @@ void txt_free(Txt *t) { free(t->ptbl.v); } +void txt_write_range(int fd, TxtLoc lo, TxtLoc hi) { + while (lo.p < hi.p) { + TxtPiece *p = &lo.t->ptbl.v[lo.p]; + write(fd, lo.t->buf[p->buf].s + p->ofs + lo.i, p->n - lo.i); + lo.p++; + lo.i = 0; + } + if (hi.i > lo.i) { + TxtPiece *p = &lo.t->ptbl.v[lo.p]; + write(fd, lo.t->buf[p->buf].s + p->ofs + lo.i, hi.i - lo.i); + } +} + /* navigation */ +int txt_valid_loc(TxtLoc l) { + return l.p < l.t->ptbl.n + && (l.i < l.t->ptbl.v[l.p].n + || (l.p + 1 == l.t->ptbl.n + && l.i <= l.t->ptbl.v[l.p].n)); +} + TxtLoc txt_at(Txt *b, u32 ofs) { for (u32 i = 0; i < b->ptbl.n; i++) { if (ofs < b->ptbl.v[i].n) { @@ -263,13 +304,25 @@ u32 txt_ofs(TxtLoc l) { } int txt_before(TxtLoc a, TxtLoc b) { + ASSERT(txt_valid_loc(a)); + ASSERT(txt_valid_loc(b)); return a.p < b.p || (a.p == b.p && a.i < b.i); } int txt_after(TxtLoc a, TxtLoc b) { + ASSERT(txt_valid_loc(a)); + ASSERT(txt_valid_loc(b)); return a.p > b.p || (a.p == b.p && a.i > b.i); } +TxtLoc txt_start(Txt *t) { + return (TxtLoc) { t, 0, 0 }; +} + +TxtLoc txt_end(Txt *t) { + return (TxtLoc) { t, t->ptbl.n-1, t->ptbl.v[t->ptbl.n-1].n }; +} + TxtLoc bnext(TxtLoc l) { TxtPiece *p = &l.t->ptbl.v[l.p]; if (l.p + 1 < l.t->ptbl.n) { @@ -39,6 +39,8 @@ int txt_load(Txt *b, const char *path); int txt_save(Txt *b, const char *path); void txt_free(Txt *b); +void txt_write_range(int fd, TxtLoc lo, TxtLoc hi); + /* insertion & deletion */ TxtLoc txt_insert(TxtLoc l, const char *s, u32 n); @@ -46,13 +48,17 @@ TxtLoc txt_delete(TxtLoc l, u32 n); TxtLoc txt_delete_range(TxtLoc lo, TxtLoc hi); TxtLoc txt_insert_c(TxtLoc l, u32 ch); TxtLoc txt_delete_c(TxtLoc l); +u32 txt_range_len(TxtLoc lo, TxtLoc hi); /* navigation */ +int txt_valid_loc(TxtLoc l); TxtLoc txt_at(Txt *b, u32 ofs); u32 txt_ofs(TxtLoc l); int txt_before(TxtLoc a, TxtLoc b); int txt_after(TxtLoc a, TxtLoc b); +TxtLoc txt_start(Txt *t); +TxtLoc txt_end(Txt *t); TxtLoc bnext(TxtLoc l); TxtLoc bprev(TxtLoc l); @@ -584,6 +584,7 @@ end: } void vui_chra(int x, int y, VuiChar c, VuiAttr a) { + ASSERT(c > 0 && c <= KEY_UTF8_MAX); if (x >= 0 && x < (int)COLS && y >= 0 && y < (int)LINES) { CHR(x, y) = c; ATTR(x, y) = a; @@ -617,7 +618,9 @@ void 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; - utf8_decode(&CHR(x, y), s, n); + 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); for (uint16_t *pa = &ATTR(x, y); n--;) *pa++ = a; } |
