summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWormHeamer2025-12-28 20:30:37 -0500
committerWormHeamer2025-12-28 20:30:37 -0500
commit2fe237e65a13545c053c31b7b413898d5b8734ac (patch)
tree66354165554feeb303010473c22e67b0b48cda87
parent3ed6bbd8eb7214268d6e042736dcd5285cb4f4d7 (diff)
remove most explicit Txt* params, fix some utf-8 problems
-rw-r--r--main.c183
-rw-r--r--txt.c226
-rw-r--r--txt.h47
3 files changed, 246 insertions, 210 deletions
diff --git a/main.c b/main.c
index 0fe9513..91ef1dc 100644
--- a/main.c
+++ b/main.c
@@ -27,37 +27,37 @@ int is_space(u32 c) {
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;
+ while (!at_start(l)) {
+ TxtLoc n = bprev(l);
+ if (!is_space(txt_chr(n))) break;
l = n;
}
- while (!txt_at_start(t,l)) {
- TxtLoc n = txt_prev(t, l);
- if (is_space(txt_chr(t, n))) break;
+ while (!at_start(l)) {
+ TxtLoc n = bprev(l);
+ if (is_space(txt_chr(n))) break;
l = n;
}
- return txt_ofs(t, l);
+ return txt_ofs(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);
+ while (!at_end(l) && is_space(txt_chr(l))) l = cnext(l);
+ while (!at_end(l) && !is_space(txt_chr(l))) l = cnext(l);
+ return txt_ofs(l);
}
u32 move_char_back(Txt *t, u32 cur) {
while (cur > 0) {
cur--;
- u8 c = txt_byte(t, txt_at(t, cur));
+ u8 c = txt_byte(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));
+ u8 b = txt_byte(txt_at(t, cur));
u32 n = stdc_leading_ones(b);
if (cur + n >= t->len) return t->len;
return cur + n + !n;
@@ -66,91 +66,40 @@ u32 move_char_fwd(Txt *t, u32 cur) {
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 end_of_line(Txt *t, TxtLoc l) {
- TxtLoc start = start_of_line(t, l);
- return txt_byte(t, start) == '\n' ? start : next_newline(t, start);
-}
-
-u32 get_col(Txt *t, TxtLoc l) {
- u32 n = 0;
- for (TxtLoc tmp = start_of_line(t, l); txt_before(tmp, l); txt_chr_next(t, &tmp)) n++;
- return n;
-}
-
-TxtLoc at_col(Txt *t, TxtLoc l, u32 col) {
- l = start_of_line(t, l);
- while (col--) {
- if (txt_chr_next(t, &l) == '\n') {
- l = txt_prev(t, l);
- break;
- }
- }
- return l;
-}
-
-TxtLoc prev_line(Txt *t, TxtLoc l) {
- TxtLoc start = start_of_line(t, l);
- return at_col(t, txt_prev(t, start), get_col(t, l));
-}
-
-TxtLoc next_line(Txt *t, TxtLoc l) {
- TxtLoc end = end_of_line(t, l);
- if (txt_at_end(t, end)) return end;
- return at_col(t, txt_next(t, end), get_col(t, l));
+ return txt_ofs(txt_delete(txt_at(t, b), b - a));
}
u32 move_line_up(Txt *t, u32 cur) {
- return txt_ofs(t, prev_line(t, txt_at(t, cur)));
+ return txt_ofs(prev_line(txt_at(t, cur)));
}
u32 move_line_down(Txt *t, u32 cur) {
- return txt_ofs(t, next_line(t, txt_at(t, cur)));
+ return txt_ofs(next_line(txt_at(t, cur)));
}
-int empty_line(Txt *t, TxtLoc l) {
- u8 b = txt_byte(t, start_of_line(t, l));
+int empty_line(TxtLoc l) {
+ u8 b = txt_byte(start_of_line(l));
return b == '\n' || b == 0 /* last line of buffer */;
}
-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);
+TxtLoc next_par(TxtLoc l) {
+ while (!at_end(l) && empty_line(l)) l = next_line(l);
+ while (!at_end(l) && !empty_line(l)) l = next_line(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);
+TxtLoc prev_par(TxtLoc l) {
+ while (!at_start(l) && empty_line(l)) l = prev_line(l);
+ while (!at_start(l) && !empty_line(l)) l = prev_line(l);
return l;
}
u32 move_par_up(Txt *t, u32 cur) {
- return txt_ofs(t, prev_par(t, txt_at(t, cur)));
+ return txt_ofs(prev_par(txt_at(t, cur)));
}
u32 move_par_down(Txt *t, u32 cur) {
- return txt_ofs(t, next_par(t, txt_at(t, cur)));
+ return txt_ofs(next_par(txt_at(t, cur)));
}
/* main */
@@ -162,12 +111,12 @@ void find_view_window(Txt *t, u32 curs, TxtLoc *start, TxtLoc *end) {
TxtLoc l = txt_at(t, curs);
u32 u = LINES / 2;
TxtLoc a = l;
- for (u32 i = 0; i < u; i++) a = prev_newline(t, a);
+ for (u32 i = 0; i < u; i++) a = prev_newline(a);
u32 n = 0;
TxtLoc b = a;
- while (!txt_at_end(t, b) && n++ < LINES) b = next_newline(t, b);
- while (!txt_at_start(t, a) && n++ < LINES) a = prev_newline(t, a);
- if (!txt_at_start(t,a) && txt_byte(t,a) == '\n') a = txt_next(t, 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;
}
@@ -193,7 +142,7 @@ void draw(void *ctx) {
cur_found = 1;
vui_curs_pos(x, y);
}
- u32 c = txt_chr_next(&txt, &start);
+ u32 c = txt_chr_next(&start);
if (c == '\n') {
x = 0;
y++;
@@ -206,7 +155,7 @@ void draw(void *ctx) {
}
ASSERT(start.i <= txt.ptbl.v[start.p].n);
if (!cur_found) vui_curs_pos(x, y);
- u32 c = txt_chr(&txt, l);
+ 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);
@@ -259,11 +208,11 @@ loop:
case KEY_HOME:
case '^':
case '0':
- cur = txt_ofs(&txt, start_of_line(&txt, txt_at(&txt, cur)));
+ cur = txt_ofs(start_of_line(txt_at(&txt, cur)));
break;
case KEY_END:
case '$':
- cur = txt_ofs(&txt, next_newline(&txt, txt_at(&txt, cur)));
+ cur = txt_ofs(next_newline(txt_at(&txt, cur)));
break;
case KEY_HOME | KEY_CTRL_BIT:
case 'g':
@@ -275,12 +224,12 @@ loop:
break;
case 'f': {
u32 n = vui_key();
- TxtLoc l = txt_next(&txt, txt_at(&txt, cur));
+ TxtLoc l = cnext(txt_at(&txt, cur));
for (TxtLoc t = l;;) {
- u32 c = txt_chr_next(&txt, &t);
+ u32 c = txt_chr_next(&t);
if (c == '\n') break;
if (c == n) {
- cur = txt_ofs(&txt, l);
+ cur = txt_ofs(l);
break;
}
l = t;
@@ -288,15 +237,15 @@ loop:
} break;
case 'F': {
u32 n = vui_key();
- TxtLoc l = txt_prev(&txt, txt_at(&txt, cur));
+ TxtLoc l = bprev(txt_at(&txt, cur));
for (;;) {
- u32 c = txt_chr(&txt, l);
+ u32 c = txt_chr(l);
if (c == '\n') break;
if (c == n) {
- cur = txt_ofs(&txt, l);
+ cur = txt_ofs(l);
break;
}
- l = txt_prev(&txt, l);
+ l = bprev(l);
}
} break;
default:
@@ -343,25 +292,25 @@ int main(int argc, const char **argv) {
case 'a': {
mode = 1;
TxtLoc l = txt_at(&txt, cur);
- TxtLoc e = end_of_line(&txt, l);
- l = txt_before(l, e) ? txt_next(&txt, l) : e;
- cur = txt_ofs(&txt, l);
+ TxtLoc e = end_of_line(l);
+ l = txt_before(l, e) ? cnext(l) : e;
+ cur = txt_ofs(l);
} break;
case 'I':
mode = 1;
- cur = txt_ofs(&txt, start_of_line(&txt, txt_at(&txt, cur)));
+ cur = txt_ofs(start_of_line(txt_at(&txt, cur)));
break;
case 'A':
mode = 1;
- cur = txt_ofs(&txt, end_of_line(&txt, txt_at(&txt, cur)));
+ cur = txt_ofs(end_of_line(txt_at(&txt, cur)));
break;
case 'o':
mode = 1;
- cur = txt_ofs(&txt, end_of_line(&txt, txt_at(&txt, cur)));
+ cur = txt_ofs(end_of_line(txt_at(&txt, cur)));
goto ins_newline;
case 'O':
mode = 1;
- cur = txt_ofs(&txt, txt_prev(&txt, start_of_line(&txt, txt_at(&txt, cur))));
+ cur = txt_ofs(bprev(start_of_line(txt_at(&txt, cur))));
goto ins_newline;
case 'd': {
u32 before = cur;
@@ -371,19 +320,19 @@ int main(int argc, const char **argv) {
}
} break;
case 'D': {
- u32 end = txt_ofs(&txt, end_of_line(&txt, txt_at(&txt, cur)));
+ u32 end = txt_ofs(end_of_line(txt_at(&txt, cur)));
del_between(&txt, cur, end);
} break;
case 'C': {
- u32 end = txt_ofs(&txt, end_of_line(&txt, txt_at(&txt, cur)));
+ u32 end = txt_ofs(end_of_line(txt_at(&txt, cur)));
del_between(&txt, cur, end);
mode = 1;
} break;
case 'S': {
- TxtLoc l = start_of_line(&txt, txt_at(&txt, cur));
- u32 end = txt_ofs(&txt, end_of_line(&txt, l));
- del_between(&txt, txt_ofs(&txt, l), end);
- cur = txt_ofs(&txt, l);
+ 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);
mode = 1;
} break;
case 'c': {
@@ -396,10 +345,10 @@ int main(int argc, const char **argv) {
break;
}
case 'x':
- txt_delete_c(&txt, txt_ofs(&txt, txt_next(&txt, txt_at(&txt, cur))));
+ cur = txt_ofs(txt_delete_c(cnext(txt_at(&txt, cur))));
break;
case 's':
- txt_delete_c(&txt, txt_ofs(&txt, txt_next(&txt, txt_at(&txt, cur))));
+ cur = txt_ofs(txt_delete_c(cnext(txt_at(&txt, cur))));
mode = 1;
break;
case '1': case '2': case '3': case '4':
@@ -420,11 +369,13 @@ add_to_count:
case 1:
switch (c) {
case KEY_ESC:
- if (txt_after(txt_at(&txt, cur), start_of_line(&txt, txt_at(&txt, cur)))) cur--;
+ if (txt_after(txt_at(&txt, cur), start_of_line(txt_at(&txt, cur)))) {
+ cur = txt_ofs(cprev(txt_at(&txt, cur)));
+ }
mode = 0;
break;
case KEY_BKSP:
- cur = txt_delete_c(&txt, cur);
+ cur = txt_ofs(txt_delete_c(txt_at(&txt, cur)));
break;
case 0x17 /* ^W */:
cur = del_between(&txt, move_word_back(&txt, cur), cur);
@@ -434,19 +385,21 @@ add_to_count:
break;
case '\r': {
ins_newline:;
+ /*
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))
+ TxtLoc start = start_of_line(txt_at(&txt, cur));
+ for (TxtLoc l = start; txt_byte(l) == '\t'; l = bnext(l))
tabs++;
- while (txt_byte(&txt, txt_at(&txt, cur - 1)) == '\t') {
- cur = txt_delete(&txt, cur, 1);
+ while (txt_byte(txt_at(&txt, cur - 1)) == '\t') {
+ cur = txt_ofs(txt_delete(txt_at(&txt, cur), 1));
}
- cur = txt_insert_c(&txt, cur, '\n');
- while (tabs--) cur = txt_insert_c(&txt, cur, '\t');
+ */
+ cur = txt_ofs(txt_insert_c(txt_at(&txt, cur), '\n'));
+ //while (tabs--) cur = txt_ofs(txt_insert_c(txt_at(&txt, cur), '\t'));
} break;
default:
- if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) cur = txt_insert_c(&txt, cur, c);
+ if ((c == '\t' || c >= ' ') && c <= KEY_UTF8_MAX) cur = txt_ofs(txt_insert_c(txt_at(&txt, cur), c));
break;
}
break;
diff --git a/txt.c b/txt.c
index 38f34c4..b2dc2d8 100644
--- a/txt.c
+++ b/txt.c
@@ -56,7 +56,7 @@ static void txt_buf_append(Txt *b, TxtBufIdx bi, const char *s, u32 n) {
buf->n += n;
}
-u32 txt_add_piece(Txt *b, TxtLoc l) {
+TxtLoc txt_add_piece(Txt *b, TxtLoc l) {
if (l.p > 0 && !l.i) {
l.p--;
l.i = b->ptbl.v[l.p].n;
@@ -76,24 +76,24 @@ insert:
txt_insert_piece(b, l.p, TXT_ADD, b->buf[TXT_ADD].n, 0);
}
}
- return l.p;
+ return l;
}
-u32 txt_insert(Txt *b, u32 cur, const char *s, u32 n) {
- TxtLoc l = txt_at(b, cur);
- l.p = txt_add_piece(b, l);
- TxtPiece *p = &b->ptbl.v[l.p];
- txt_buf_append(b, p->buf, s, n);
+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];
+ txt_buf_append(l.t, p->buf, s, n);
p->n += n;
- b->len += n;
- return cur + n;
+ l.t->len += n;
+ l.i = p->n;
+ return l;
}
-u32 txt_insert_c(Txt *b, u32 cur, u32 ch) {
+TxtLoc txt_insert_c(TxtLoc l, u32 ch) {
char buf[6];
u32 n = utf8_encode_len(&ch, 1);
utf8_encode(buf, &ch, 1);
- return txt_insert(b, cur, buf, n);
+ return txt_insert(l, buf, n);
}
static int txt_are_pieces_adjacent(Txt *t, u32 a, u32 b) {
@@ -102,46 +102,46 @@ static int txt_are_pieces_adjacent(Txt *t, u32 a, u32 b) {
return pa->buf == pb->buf && pa->ofs + pa->n == pb->ofs;
}
-static void txt_join_or_kill(Txt *b, u32 pi) {
- if (b->ptbl.v[pi].n == 0 && !(pi == 0 && b->ptbl.n == 1)) {
- txt_remove_piece(b, pi);
+static TxtLoc txt_join_or_kill(TxtLoc l) {
+ Txt *b = l.t;
+ if (l.t->ptbl.v[l.p].n == 0 && !(l.p == 0 && b->ptbl.n == 1)) {
+ txt_remove_piece(b, l.p);
}
- if (pi + 1 < b->ptbl.n && txt_are_pieces_adjacent(b, pi, pi + 1)) {
- b->ptbl.v[pi].n += b->ptbl.v[pi+1].n;
- txt_remove_piece(b, pi + 1);
+ if (l.p + 1 < b->ptbl.n && txt_are_pieces_adjacent(b, l.p, l.p + 1)) {
+ b->ptbl.v[l.p].n += b->ptbl.v[l.p+1].n;
+ txt_remove_piece(b, l.p + 1);
}
+ return l;
}
-u32 txt_delete(Txt *b, u32 cur, u32 n) {
- TxtLoc l = txt_at(b, cur);
- u32 pi = txt_split_piece(b, l.p, l.i);
+TxtLoc txt_delete(TxtLoc l, u32 n) {
+ u32 pi = txt_split_piece(l.t, l.p, l.i);
if (pi > 0) pi--;
- if (n > cur) n = cur;
- cur -= n;
- b->len -= n;
+ l.p = pi;
+ l.i = l.t->ptbl.v[l.p].n;
for (;;) {
- TxtPiece *p = &b->ptbl.v[pi];
- if (n >= p->n) {
- n -= p->n;
- p->n = 0;
- if (pi > 0 || b->ptbl.n > 1) txt_remove_piece(b, pi);
- } else {
- p->n -= n;
- n = 0;
+ ASSERT(l.i == l.t->ptbl.v[l.p].n);
+ if (l.i >= n) {
+ l.i -= n;
+ l.t->ptbl.v[l.p].n -= n;
break;
+ } else {
+ l.t->ptbl.v[l.p].n -= l.i;
+ n -= l.i;
+ l.i = 0;
+ if (l.p == 0) break;
+ l.p = l.p - 1;
+ l.i = l.t->ptbl.v[l.p].n;
}
- if (pi == 0) break;
- pi--;
}
- txt_join_or_kill(b, pi);
- return cur;
+ return txt_join_or_kill(l);
}
-u32 txt_delete_c(Txt *b, u32 cur) {
- while (cur > 0 && (txt_byte(b, txt_at(b, cur-1)) & 0xc0) == 0x80)
- cur = txt_delete(b, cur, 1);
- cur = txt_delete(b, cur, 1);
- return cur;
+TxtLoc txt_delete_c(TxtLoc l) {
+ while (!at_start(l) && (txt_byte(l) & 0xc0) == 0x80)
+ l = txt_delete(l, 1);
+ l = txt_delete(l, 1);
+ return l;
}
int txt_load(Txt *b, const char *path) {
@@ -195,44 +195,37 @@ void txt_free(Txt *t) {
free(t->ptbl.v);
}
-/* reading individual chars */
+/* navigation */
-u8 txt_byte(Txt *t, TxtLoc l) {
- TxtPiece *p = &t->ptbl.v[l.p];
- TxtBuf *b = &t->buf[p->buf];
- if (l.i >= p->n) return 0;
- return b->s[p->ofs + l.i];
-}
-
-u32 txt_chr(Txt *t, TxtLoc l) {
- TxtPiece *p = &t->ptbl.v[l.p];
- TxtBuf *b = &t->buf[p->buf];
- return utf8_decode_at(b->s, p->ofs + l.i, p->ofs + p->n);
-}
-
-/* cursor manipulation */
-
-TxtLoc txt_at(Txt *b, u32 cur) {
+TxtLoc txt_at(Txt *b, u32 ofs) {
for (u32 i = 0; i < b->ptbl.n; i++) {
- if (cur < b->ptbl.v[i].n) {
- return (TxtLoc) { b, i, cur };
+ if (ofs < b->ptbl.v[i].n) {
+ return (TxtLoc) { b, i, ofs };
}
- cur -= b->ptbl.v[i].n;
+ ofs -= b->ptbl.v[i].n;
}
return (TxtLoc) { b, b->ptbl.n - 1, b->ptbl.v[b->ptbl.n - 1].n };
}
-u32 txt_ofs(Txt *b, TxtLoc l) {
+u32 txt_ofs(TxtLoc l) {
u32 r = 0;
for (u32 i = 0; i < l.p; i++) {
- r += b->ptbl.v[i].n;
+ r += l.t->ptbl.v[i].n;
}
return r + l.i;
}
-TxtLoc txt_next(Txt *b, TxtLoc l) {
- TxtPiece *p = &b->ptbl.v[l.p];
- if (l.p + 1 < b->ptbl.n) {
+int txt_before(TxtLoc a, TxtLoc b) {
+ return a.p < b.p || (a.p == b.p && a.i < b.i);
+}
+
+int txt_after(TxtLoc a, TxtLoc b) {
+ return a.p > b.p || (a.p == b.p && a.i > b.i);
+}
+
+TxtLoc bnext(TxtLoc l) {
+ TxtPiece *p = &l.t->ptbl.v[l.p];
+ if (l.p + 1 < l.t->ptbl.n) {
if (l.i + 1 < p->n) l.i++;
else l.p++, l.i = 0;
} else {
@@ -242,37 +235,106 @@ TxtLoc txt_next(Txt *b, TxtLoc l) {
return l;
}
-TxtLoc txt_prev(Txt *b, TxtLoc l) {
+TxtLoc bprev(TxtLoc l) {
if (l.i > 0) {
- return (TxtLoc) { b, l.p, l.i - 1 };
- } else if (l.p > 0) {
- return (TxtLoc) { b, l.p - 1, b->ptbl.v[l.p - 1].n - 1 };
+ return (TxtLoc) { l.t, l.p, l.i - 1 };
+ } else if (l.p > 0 && l.t->ptbl.v[l.p - 1].n > 0) {
+ return (TxtLoc) { l.t, l.p - 1, l.t->ptbl.v[l.p - 1].n - 1 };
} else {
- return (TxtLoc) { b, 0, 0 };
+ return (TxtLoc) { l.t, 0, 0 };
}
}
-int txt_at_start(Txt *b, TxtLoc l) {
- (void)b;
+TxtLoc cnext(TxtLoc l) {
+ l = bnext(l);
+ while ((txt_byte(l) & 0xc0) == 0x80) l = bnext(l);
+ return l;
+}
+
+TxtLoc cprev(TxtLoc l) {
+ l = bprev(l);
+ while ((txt_byte(l) & 0xc0) == 0x80) l = bprev(l);
+ return l;
+}
+
+int at_start(TxtLoc l) {
return l.p == 0 && l.i == 0;
}
-int txt_at_end(Txt *b, TxtLoc l) {
- return l.p + 1 == b->ptbl.n && l.i == b->ptbl.v[l.p].n;
+int at_end(TxtLoc l) {
+ return l.p + 1 == l.t->ptbl.n && l.i == l.t->ptbl.v[l.p].n;
}
-int txt_before(TxtLoc a, TxtLoc b) {
- return a.p < b.p || (a.p == b.p && a.i < b.i);
+TxtLoc next_newline(TxtLoc l) {
+ do l = cnext(l);
+ while (!at_end(l) && txt_byte(l) != '\n');
+ return l;
}
-int txt_after(TxtLoc a, TxtLoc b) {
- return a.p > b.p || (a.p == b.p && a.i > b.i);
+TxtLoc prev_newline(TxtLoc l) {
+ do l = cprev(l);
+ while (!at_start(l) && txt_byte(l) != '\n');
+ return l;
+}
+
+TxtLoc start_of_line(TxtLoc l) {
+ l = prev_newline(l);
+ if (!at_start(l) && txt_byte(l) == '\n') l = cnext(l);
+ return l;
+}
+
+TxtLoc end_of_line(TxtLoc l) {
+ TxtLoc start = start_of_line(l);
+ return txt_byte(start) == '\n' ? start : next_newline(start);
+}
+
+u32 get_col(TxtLoc l) {
+ u32 n = 0;
+ for (TxtLoc tmp = start_of_line(l); txt_before(tmp, l); txt_chr_next(&tmp)) n++;
+ return n;
+}
+
+TxtLoc at_col(TxtLoc l, u32 col) {
+ l = start_of_line(l);
+ while (col--) {
+ if (txt_chr_next(&l) == '\n') {
+ l = bprev(l);
+ break;
+ }
+ }
+ return l;
+}
+
+TxtLoc prev_line(TxtLoc l) {
+ TxtLoc start = start_of_line(l);
+ return at_col(cprev(start), get_col(l));
+}
+
+TxtLoc next_line(TxtLoc l) {
+ TxtLoc end = end_of_line(l);
+ if (at_end(end)) return end;
+ return at_col(cnext(end), get_col(l));
+}
+
+/* reading chars */
+
+u32 txt_chr(TxtLoc l) {
+ TxtPiece *p = &l.t->ptbl.v[l.p];
+ TxtBuf *b = &l.t->buf[p->buf];
+ return utf8_decode_at(b->s, p->ofs + l.i, p->ofs + p->n);
+}
+
+u8 txt_byte(TxtLoc l) {
+ TxtPiece *p = &l.t->ptbl.v[l.p];
+ TxtBuf *b = &l.t->buf[p->buf];
+ if (l.i >= p->n) return 0;
+ return b->s[p->ofs + l.i];
}
-u32 txt_chr_next(Txt *t, TxtLoc *l) {
- if (txt_at_end(t, *l)) return 0;
- u32 c = txt_chr(t, *l);
+u32 txt_chr_next(TxtLoc *l) {
+ if (at_end(*l)) return 0;
+ u32 c = txt_chr(*l);
u32 n = UTF8_CP_LEN(c);
- for (u32 i = 0; i < n; i++) *l = txt_next(t, *l);
+ for (u32 i = 0; i < n; i++) *l = bnext(*l);
return c;
}
diff --git a/txt.h b/txt.h
index 3af9970..b33599b 100644
--- a/txt.h
+++ b/txt.h
@@ -29,29 +29,50 @@ typedef struct {
u32 p, i;
} TxtLoc;
+/* text buffer manipulation */
+
u32 txt_split_piece(Txt *b, u32 pi, u32 i);
void txt_remove_piece(Txt *b, u32 pi);
void txt_insert_piece(Txt *b, u32 pi, TxtBufIdx buf, u32 ofs, u32 n);
-u32 txt_insert(Txt *b, u32 cur, const char *s, u32 n);
-u32 txt_delete(Txt *b, u32 cur, u32 n);
-u32 txt_insert_c(Txt *b, u32 cur, u32 ch);
-u32 txt_delete_c(Txt *b, u32 cur);
int txt_load(Txt *b, const char *path);
int txt_save(Txt *b, const char *path);
void txt_free(Txt *b);
-TxtLoc txt_at(Txt *b, u32 cur);
-u32 txt_ofs(Txt *b, TxtLoc l);
-TxtLoc txt_next(Txt *b, TxtLoc l);
-TxtLoc txt_prev(Txt *b, TxtLoc l);
-int txt_at_start(Txt *b, TxtLoc l);
-int txt_at_end(Txt *b, TxtLoc l);
+/* insertion & deletion */
+
+TxtLoc txt_insert(TxtLoc l, const char *s, u32 n);
+TxtLoc txt_delete(TxtLoc l, u32 n);
+TxtLoc txt_insert_c(TxtLoc l, u32 ch);
+TxtLoc txt_delete_c(TxtLoc l);
+
+/* navigation */
+
+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);
-u32 txt_chr(Txt *b, TxtLoc l);
-u8 txt_byte(Txt *b, TxtLoc l);
-u32 txt_chr_next(Txt *b, TxtLoc *l);
+TxtLoc bnext(TxtLoc l);
+TxtLoc bprev(TxtLoc l);
+TxtLoc cnext(TxtLoc l);
+TxtLoc cprev(TxtLoc l);
+int at_start(TxtLoc l);
+int at_end(TxtLoc l);
+
+TxtLoc next_newline(TxtLoc l);
+TxtLoc prev_newline(TxtLoc l);
+TxtLoc start_of_line(TxtLoc l);
+TxtLoc end_of_line(TxtLoc l);
+TxtLoc prev_line(TxtLoc l);
+TxtLoc next_line(TxtLoc l);
+TxtLoc at_col(TxtLoc l, u32 col);
+u32 get_col(TxtLoc l);
+
+/* reading */
+
+u32 txt_chr(TxtLoc l);
+u8 txt_byte(TxtLoc l);
+u32 txt_chr_next(TxtLoc *l);
#endif