1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
#ifndef TXT_H
#define TXT_H
#include "dynarr.h"
#include "arena.h"
#include "str.h"
#define TXT_HIST_MAX 32
typedef enum : u8 {
TXT_SRC,
TXT_ADD
} TxtBufIdx;
typedef struct TxtPiece {
TxtBufIdx buf;
u32 ofs, n;
} TxtPiece;
typedef struct {
char *s;
u32 n, c;
} TxtBuf;
typedef struct {
DYNARR(TxtPiece);
int dirty;
} TxtPieceTbl;
typedef struct {
struct Txt *t;
u32 p, i;
} TxtLoc;
typedef struct {
TxtPieceTbl v[TXT_HIST_MAX];
TxtLoc cur[TXT_HIST_MAX];
u32 i, n;
} TxtHist;
typedef struct Txt {
TxtPieceTbl ptbl;
TxtHist hist;
TxtBuf buf[2];
u32 len;
int readonly;
} Txt;
/* text buffer manipulation */
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);
void txt_hist_push(Txt *t, TxtLoc cur);
int txt_hist_fwd(Txt *t, TxtLoc *cur);
int txt_hist_back(Txt *t, TxtLoc *cur);
Str txt_collect_range(TxtLoc lo, TxtLoc hi, Arena *a);
u32 txt_read_chunk(TxtLoc *lo, TxtLoc hi, char *buf, u32 sz);
Str txt_next_chunk(TxtLoc *l);
int txt_range_equal(TxtLoc lo, TxtLoc hi, Str s);
/* insertion & deletion */
TxtLoc txt_insert(TxtLoc l, const char *s, u32 n);
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 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 prev_line_start(TxtLoc l);
TxtLoc next_line_start(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);
/* inline funcs */
static inline int at_start(TxtLoc l) {
return l.p == 0 && l.i == 0;
}
static inline int at_end(TxtLoc l) {
return l.p + 1 == l.t->ptbl.n && l.i == l.t->ptbl.v[l.p].n;
}
static inline 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 {
l.i++;
if (l.i > p->n) l.i = p->n;
}
return l;
}
static inline TxtLoc bprev(TxtLoc l) {
if (l.i > 0) {
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) { l.t, 0, 0 };
}
}
static inline TxtLoc cnext(TxtLoc l) {
l = bnext(l);
while ((txt_byte(l) & 0xc0) == 0x80) l = bnext(l);
return l;
}
static inline TxtLoc cprev(TxtLoc l) {
l = bprev(l);
while ((txt_byte(l) & 0xc0) == 0x80) l = bprev(l);
return l;
}
#endif
|