#include #include #include #include #include #include #include #include #define ARENA_IMPL #define UTF8_IMPL #include "wrmr.h" #include "arena.h" #include "txt.h" #include "vui.h" #include "utf8.h" Arena scratch = { 0 }; Txt txt = { 0 }; u32 cur = 0; int is_space(u32 c) { return c <= 0x20 && c != 0; } u32 move_word_back(Txt *t, u32 cur) { TxtLoc l = txt_at(t, cur); while (!txt_at_start(t,l)) { TxtLoc n = txt_prev(t, l); if (!is_space(txt_chr(t, n))) break; l = n; } while (!txt_at_start(t,l)) { TxtLoc n = txt_prev(t, l); if (is_space(txt_chr(t, n))) break; l = n; } return txt_ofs(t, l); } u32 move_word_fwd(Txt *t, u32 cur) { TxtLoc l = txt_at(t, cur); while (!txt_at_end(t,l) && is_space(txt_chr(t, l))) l = txt_next(t, l); while (!txt_at_end(t,l) && !is_space(txt_chr(t, l))) l = txt_next(t, l); return txt_ofs(t, l); } u32 move_char_back(Txt *t, u32 cur) { while (cur > 0) { cur--; u8 c = txt_byte(t, txt_at(t, cur)); if ((c & 0xc0) != 0x80) break; } return cur; } u32 move_char_fwd(Txt *t, u32 cur) { u8 b = txt_byte(t, txt_at(t, cur)); u32 n = stdc_leading_ones(b); if (cur + n >= t->len) return t->len; return cur + n + !n; } u32 del_between(Txt *t, u32 a, u32 b) { if (b < a) { u32 t = a; a = b; b = t; } //if (b - a > 10) TRAP(); return txt_delete(t, b, b - a); } TxtLoc next_newline(Txt *t, TxtLoc l) { do l = txt_next(t, l); while (!txt_at_end(t, l) && txt_byte(t, l) != '\n'); return l; } TxtLoc prev_newline(Txt *t, TxtLoc l) { do l = txt_prev(t, l); while (!txt_at_start(t, l) && txt_byte(t, l) != '\n'); return l; } TxtLoc start_of_line(Txt *t, TxtLoc l) { l = prev_newline(t, l); if (!txt_at_start(t,l) && txt_byte(t, l) == '\n') l = txt_next(t, l); return l; } TxtLoc prev_line(Txt *t, TxtLoc l) { TxtLoc start = start_of_line(t, l); u32 n = 0; for (TxtLoc tmp = start; txt_before(tmp, l); txt_chr_next(t, &tmp)) n++; TxtLoc start_up = start_of_line(t, txt_prev(t, start)); while (n--) { if (txt_chr_next(t, &start_up) == '\n') { start_up = txt_prev(t, start_up); break; } } return start_up; } TxtLoc next_line(Txt *t, TxtLoc l) { TxtLoc start = start_of_line(t, l); u32 n = 0; for (TxtLoc tmp = start; txt_before(tmp, l); txt_chr_next(t, &tmp)) n++; TxtLoc start_dn = txt_byte(t, l) == '\n' ? txt_next(t, l) : txt_next(t, next_newline(t, l)); while (n--) { if (txt_chr_next(t, &start_dn) == '\n') { start_dn = txt_prev(t, start_dn); break; } } return start_dn; } u32 move_line_up(Txt *t, u32 cur) { return txt_ofs(t, prev_line(t, txt_at(t, cur))); } u32 move_line_down(Txt *t, u32 cur) { return txt_ofs(t, next_line(t, txt_at(t, cur))); } int empty_line(Txt *t, TxtLoc l) { return txt_byte(t, start_of_line(t, l)) == '\n'; } TxtLoc next_par(Txt *t, TxtLoc l) { while (!txt_at_end(t, l) && empty_line(t, l)) l = next_line(t, l); while (!txt_at_end(t, l) && !empty_line(t, l)) l = next_line(t, l); return l; } TxtLoc prev_par(Txt *t, TxtLoc l) { while (!txt_at_start(t, l) && empty_line(t, l)) l = prev_line(t, l); while (!txt_at_start(t, l) && !empty_line(t, l)) l = prev_line(t, l); return l; } u32 move_par_up(Txt *t, u32 cur) { return txt_ofs(t, prev_par(t, txt_at(t, cur))); } u32 move_par_down(Txt *t, u32 cur) { return txt_ofs(t, next_par(t, txt_at(t, cur))); } /* main */ #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); u32 n = LINES; u32 u = LINES / 2; TxtLoc a = l; for (u32 i = 0; i < u; i++) a = prev_newline(t, a); TxtLoc b = a; while (!txt_at_end(t, b) && n--) b = next_newline(t, b); if (!txt_at_start(t,a) && txt_byte(t,a) == '\n') a = txt_next(t, a); *start = a; *end = b; } void draw(void *ctx) { (void)ctx; vui_clear(); int x = 0, y = 0; TxtLoc start, end; find_view_window(&txt, cur, &start, &end); 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]; 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 = txt_at(&txt, cur); int cur_found = 0; vui_printf(-1, -3, "(%u.%u)-(%u.%u)", start.p, start.i, end.p, end.i); while (txt_before(start, end)) { if (l.p == start.p && l.i == start.i) { cur_found = 1; vui_curs_pos(x, y); } u32 c = txt_chr_next(&txt, &start); if (c == '\n') { x = 0; y++; } else if (c == '\t') { x++; x += (-x & 7); } else if (c) { vui_chr(x++, y, c); } } ASSERT(start.i <= txt.ptbl.v[start.p].n); vui_printf(-1, -4, "end(%u.%u)", start.p, start.i); if (!cur_found) vui_curs_pos(x, y); u32 c = txt_chr(&txt, l); vui_printf(-1, -1, "%u, %u - %02x (%c)", 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 main(int argc, const char **argv) { scratch = arena_init(1L << 20); vui_init(); vui_curs_vis(1); vui_redraw_fn(draw); 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; for (;;) { scratch.beg = scratch.start; draw(NULL); vui_blit(); u32 c = vui_key(); switch (c) { case KEY_ESC: goto brk; case KEY_BKSP: cur = txt_delete_c(&txt, cur); break; case KEY_LEFT: cur = move_char_back(&txt, cur); break; case KEY_LEFT | KEY_CTRL_BIT: cur = move_word_back(&txt, cur); break; case KEY_RIGHT: cur = move_char_fwd(&txt, cur); break; case KEY_RIGHT | KEY_CTRL_BIT: cur = move_word_fwd(&txt, cur); break; case KEY_UP: cur = move_line_up(&txt, cur); break; case KEY_DOWN: cur = move_line_down(&txt, cur); break; case KEY_UP | KEY_CTRL_BIT: cur = move_par_up(&txt, cur); break; case KEY_DOWN | KEY_CTRL_BIT: cur = move_par_down(&txt, cur); break; case KEY_PGUP: for (u32 i = 0; i < LINES; i += 3) cur = move_line_up(&txt, cur); break; case KEY_PGDN: for (u32 i = 0; i < LINES; i += 3) cur = move_line_down(&txt, cur); break; case KEY_HOME: cur = txt_ofs(&txt, start_of_line(&txt, txt_at(&txt, cur))); break; case KEY_END: cur = txt_ofs(&txt, next_newline(&txt, txt_at(&txt, cur))); break; case 0x17 /* ^W */: cur = del_between(&txt, move_word_back(&txt, cur), cur); break; case 0x05 /* ^E */: cur = del_between(&txt, cur, move_word_fwd(&txt, cur)); break; case 0x13 /* ^S */: txt_save(&txt, path); //txt_load(&txt, "test.txt"); break; case '\r': { u32 tabs = 0; TxtLoc start = start_of_line(&txt, txt_at(&txt, cur)); for (TxtLoc l = start; txt_byte(&txt, l) == '\t'; l = txt_next(&txt, l)) tabs++; while (txt_byte(&txt, txt_at(&txt, cur - 1)) == '\t') { cur = txt_delete(&txt, cur, 1); } cur = txt_insert_c(&txt, cur, '\n'); while (tabs--) cur = txt_insert_c(&txt, cur, '\t'); } break; default: if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) cur = txt_insert_c(&txt, cur, c); break; } } brk: vui_fini(); arena_free(&scratch); return 0; }