summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWormHeamer2025-12-31 03:20:25 -0500
committerWormHeamer2025-12-31 03:20:25 -0500
commit79f562d94a908d3ebfc9ac68a577dbc70f12c450 (patch)
treefb37f0650dbb0041f273fe7427c6e7181f40d45d
parenta06101376e750a7ba57857a6ed6917e9e5503d71 (diff)
allow opening multiple files at once + replace modes
-rw-r--r--arena.h5
-rw-r--r--freelist.h31
-rw-r--r--main.c456
-rw-r--r--txt.c9
-rw-r--r--txt.h2
-rw-r--r--vui.c18
-rw-r--r--vui.h8
7 files changed, 343 insertions, 186 deletions
diff --git a/arena.h b/arena.h
index 3887c6c..a5cc3dd 100644
--- a/arena.h
+++ b/arena.h
@@ -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
diff --git a/main.c b/main.c
index b66c9c1..892dea6 100644
--- a/main.c
+++ b/main.c
@@ -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;
}
diff --git a/txt.c b/txt.c
index 818e4c2..fd51bc4 100644
--- a/txt.c
+++ b/txt.c
@@ -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;
}
diff --git a/txt.h b/txt.h
index 6bc5dd1..e61c881 100644
--- a/txt.h
+++ b/txt.h
@@ -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);
diff --git a/vui.c b/vui.c
index 299b394..f5d6fdd 100644
--- a/vui.c
+++ b/vui.c
@@ -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) {
diff --git a/vui.h b/vui.h
index ef484d0..d0dd366 100644
--- a/vui.h
+++ b/vui.h
@@ -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, ...);