sim

the sim text editor
git clone git://ssnf.xyz/sim
Log | Files | Refs | README

commit 9d053b9c82e28b2da4ad8a778ff1eb590cf5120d
parent ab1f1760a39941a79bfc650f0141409272036c46
Author: ssnf <ssnf@ssnf.xyz>
Date:   Fri, 15 Oct 2021 11:41:07 +0000

improved do/undo reliability

Diffstat:
MMakefile | 8++++----
Mconfig.def.h | 51++++++++++++++++++++++++++++++++-------------------
Msim.c | 427++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
3 files changed, 294 insertions(+), 192 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ -CC = c89 -CFLAGS = -Os -g -Wall -Wpedantic -Wno-deprecated-declarations -Wno-return-type -SRC = ${wildcard *.c} -OBJ = ${SRC:.c=.o} +CC = c89 +CFLAGS = -Os -Wall -Wpedantic -Wno-deprecated-declarations -Wno-return-type +SRC = ${wildcard *.c} +OBJ = ${SRC:.c=.o} all: sim diff --git a/config.def.h b/config.def.h @@ -1,6 +1,32 @@ -#define STATUS "0x%02x -%lu- %s%c", f->s->s[f->dot.p1], f - file, f->name->n ? f->name->s : "-unnamed-", f->dirty +#define STATUS "%lu 0x%02x %s%c -%lu- %u", f->dot.p1, f->s->s[f->dot.p1], f->name->n ? f->name->s : "-unnamed-", f->dirty, f - file, counter Key keys[] = { + { 'c', change }, + { 'C', change , 'c' }, + { 'r', change, 'x' }, + { '0', count, 0 }, + { '1', count, 1 }, + { '2', count, 2 }, + { '3', count, 3 }, + { '4', count, 4 }, + { '5', count, 5 }, + { '6', count, 6 }, + { '7', count, 7 }, + { '8', count, 8 }, + { '9', count, 9 }, + { 'd', delete }, + { 'x', delete , Letter }, + { '.', dot }, + { Esc, escape }, + { 'E', file_open }, + { 'q', file_close, -1 }, + { 'S', file_save, -1 }, + { 'i', insert }, + { 'I', insert , StartLine}, + { 'A', insert , EndLine }, + { 'a', insert , Right }, + { 'o', insert , Down }, + { 'O', insert , Up }, { 'h', move, Left }, { 'j', move, Down }, { 'k', move, Up }, @@ -8,33 +34,20 @@ Key keys[] = { { Ctrl + 'd', move, HalfDown }, { Ctrl + 'u', move, HalfUp }, { '$', move, EndLine }, - { '0', move, StartLine }, { 'G', move, Bottom }, { 'g', move, Top }, { 'w', move, Word }, { 'e', move, EndWord }, { 'b', move, PrevWord }, - { 'i', insert }, - { 'I', insert , StartLine}, - { 'A', insert , EndLine }, - { 'a', insert , Right }, - { 'o', insert , Down }, - { 'O', insert , Up }, - { 'd', delete }, - { 'x', delete , 'x' }, - { 'c', change }, - { 'C', change , 'c' }, - { 'r', change, 'x' }, - { 'E', file_open }, - { 'q', file_close, -1 }, - { 'S', file_save, -1 }, { 'Q', quit }, { '/', search, '/' }, { '?', search, '?' }, { 'n', search, 'n' }, { 'N', search, 'N' }, { 'u', undo }, - { Ctrl + 'r', redo }, - { Esc, escape } + { Ctrl + 'r', redo } + /*TODO: yank command */ + /*TODO: replace command */ + /*TODO: dot command */ + /*TODO: visual selection(?) */ }; - diff --git a/sim.c b/sim.c @@ -21,11 +21,14 @@ enum { HalfDown, Top, Bottom, - StartLine, - EndLine, + Letter, Word, EndWord, PrevWord, + Till, + Line, + StartLine, + EndLine, Insert, Delete, Change, @@ -45,17 +48,20 @@ typedef struct { } String; typedef struct { - String s[2048]; - int c[2048]; + String is[2048]; + String ds[2048]; + uint c[2048]; + uint arg[2048]; + uint count[2048]; Posn p0[2048]; - ushort n; -} Buffer; /*yes, it is very big. absolutely no compromises*/ + short n; +} Buffer; typedef struct { String* s; String* name; Address dot; - ushort i; /*buffer index*/ + short i; /*buffer index*/ uchar dirty; } File; @@ -77,9 +83,11 @@ void* emalloc(ulong); void* erealloc(void*, ulong); static void blind_reader(Frame* fr, String* s, Posn p0); static void blind_writer(String* s, Frame* fr, ushort line, ushort offset, ushort top, ushort bot); -static void buf_add(Buffer* b, String* s, int c, Posn p0); +static void buf_add(Buffer* b, String* is, String* ds, uint c, uint arg, uint count, Posn p0); static void change(int arg); +static void count(int arg); static void delete(int arg); +static void dot(int arg); static void redo(int arg); static void escape(int c); static void init(); @@ -96,9 +104,10 @@ static void fr_close(Frame* fr); static void fr_init(Frame* fr); static void fr_insert(Frame* p, Frame* q, ushort n); static void fr_insure(Frame* fr, ushort n); -static void fr_update(int arg); +static void fr_update(); static void fr_zero(Frame*); static void search(int arg); +static int select(int arg); static void str_init(String* p); static void str_close(String* p); static void str_zero(String* p); @@ -115,8 +124,9 @@ static void quit(int); static Frame* fr, frame[FILECOUNT]; static File* f, file[FILECOUNT]; static Buffer* buf, buffer[FILECOUNT]; -static String srch; +static String istr, srch; static Window w; +static uint counter; #include "config.h" @@ -141,7 +151,7 @@ die(char* fmt, ...) file_save(i); fprintf(stderr, "file saved to %s\n", file[i].name->s); } - *fmt = *(char*)0; + *fmt = *(char*)NULL; } void* @@ -215,136 +225,161 @@ blind_writer(String* s, Frame* fr, ushort line, ushort offset, ushort top, ushor } static void -buf_add(Buffer* b, String* s, int c, Posn p0) +buf_add(Buffer* b, String* is, String* ds, uint c, uint arg, uint count, Posn p0) { - b->n = ++f->i; - if (s != NULL) { - str_zero(&b->s[b->n]); - str_insert(&b->s[b->n], s, 0); - } - b->c[b->n] = c; - b->p0[b->n] = p0; + b->n = ++f->i + 1; + str_zero(&b->is[f->i]); + str_zero(&b->ds[f->i]); + if (is != NULL) + str_insert(&b->is[f->i], is, 0); + if (ds != NULL) + str_insert(&b->ds[f->i], ds, 0); + b->c[f->i] = c; + b->arg[f->i] = arg; + b->count[f->i] = count; + b->p0[f->i] = p0; + f->dirty = '*'; } static void change(int arg) { + String s; + uint count; + if (!arg) arg = fgetc(stdin); switch (arg) { - case 'x': - delete('x'); - break; - case 'c': - delete('d'); - insert(Up); - buf_add(buf, NULL, Change, 0); - return; - case 'G': - delete('G'); - insert(Down); - buf_add(buf, NULL, Change, 0); - return; - case 'g': - delete('g'); - break; - case 'w': - delete('w'); - break; - case 't': - delete('t'); - break; - default: - return; + case 'x': arg = Letter; break; + case 'c': arg = Line; break; + case 'G': arg = Bottom; break; + case 'g': arg = Top; break; + case 'w': arg = Word; break; + case 't': arg = Till; break; + } + count = counter; + arg = select(arg); + if (arg == Word || arg == Letter || arg > 0x7f) + ++f->dot.p1; + str_init(&s); + if (f->dot.p0 != f->dot.p1) { + str_adds(&s, f->s->s + f->dot.p0, f->dot.p1 - f->dot.p0); + str_delete(f->s, f->dot.p0, f->dot.p1); } + f->dot.p0 = f->dot.p1 = f->dot.p0; insert(0); - if (buf->c[f->i] == Insert) - arg = -1; - buf_add(buf, NULL, Change, arg); + if (s.n) + str_insert(&buf->ds[f->i], &s, 0); + str_close(&s); + buf->c[f->i] = Change; + buf->arg[f->i] = arg; + buf->count[f->i] = count; + fr_update(); +} + +static void +count(int arg) +{ + if (!counter && !arg) { + move(StartLine); + return; + } + counter *= 10; + counter += arg; } static void delete(int arg) { String s; - Posn m, n; + uint count; if (!f->s->n) return; - if (!arg) - arg = fgetc(stdin); - switch (arg) { - case 'x': - m = f->dot.p0; - n = f->dot.p1 + 1; - if (f->dot.p1 == f->s->n) - move(Left); - break; - case 'd': - m = fr->a[fr->dot].p0; - n = fr->a[fr->dot].p1 + 1; - move(StartLine); - if (fr->dot == fr->n - 1) - move(Up); - break; - case 'G': - m = fr->a[fr->dot].p0; - n = f->s->n; - move(Up); - break; - case 'g': - m = 0; - n = fr->a[fr->dot].p1 + 1; - move(Top); - break; - case 'w': - m = f->dot.p0; - move(EndWord); - move(Right); - n = f->dot.p1; - f->dot.p0 = f->dot.p1 = m; - break; - case 't': - arg = fgetc(stdin); - if (arg == Esc) + if (!arg) { + switch ((arg = fgetc(stdin))) { + case 'x': arg = Letter; break; + case 'd': arg = Line; break; + case 'G': arg = Bottom; break; + case 'g': arg = Top; break; + case 'w': arg = Word; break; + case 't': arg = Till; break; + default: return; - for (;++f->dot.p1, f->dot.p1 < f->s->n && f->s->s[f->dot.p1] != arg;); - m = f->dot.p0; - n = f->dot.p1; - f->dot.p0 = f->dot.p1 = m; - break; - default: - return; + } } + count = counter; + if ((arg = select(arg)) < 0) + return; str_init(&s); - str_adds(&s, f->s->s + m, n - m); - buf_add(buf, &s, Delete, m); + str_adds(&s, f->s->s + f->dot.p0, f->dot.p1 + 1 - f->dot.p0); + buf_add(buf, NULL, &s, Delete, arg, count, f->dot.p0); + str_delete(f->s, f->dot.p0, f->dot.p1 + 1); str_close(&s); - str_delete(f->s, m, n); - f->dirty = '*'; - fr_update(1); + if (f->dot.p0 == f->s->n && f->dot.p0) + --f->dot.p0; + f->dot.p1 = f->dot.p0; + fr_update(); +} + +static void +dot(int arg) +{ + String ds; + + if (f->i < 0) + f->i = 0; + arg = buf->arg[f->i]; + counter = buf->count[f->i]; + switch (buf->c[f->i]) { + case Insert: + if (arg == Down) + move(EndLine); + else + move(arg); + str_insert(f->s, &istr, f->dot.p0); + buf_add(buf, &istr, NULL, Insert, arg, counter, f->dot.p0); + break; + case Delete: + delete(arg); + break; + case Change: + str_init(&ds); + if (buf->ds[f->i].n) { + select(arg); + if (arg == Word || arg == Letter || arg > 0x7f) + ++f->dot.p1; + str_adds(&ds, f->s->s + f->dot.p0, f->dot.p1 - f->dot.p0); + str_delete(f->s, f->dot.p0, f->dot.p1); + } + if (buf->is[f->i].n) + str_insert(f->s, &buf->is[f->i], f->dot.p0); + buf_add(buf, buf->is, &ds, Change, arg, buf->count[f->i], f->dot.p0); + str_close(&ds); + f->dot.p1 = f->dot.p0; + break; + } + fr_update(); } static void redo(int arg) { - if (f->i == buf->n) + if (f->i == buf->n - 1) return; - ++f->i; - if (buf->c[f->i] == Insert) - str_insert(f->s, &buf->s[f->i], buf->p0[f->i]); - else if (buf->c[f->i] == Delete) - str_delete(f->s, buf->p0[f->i], buf->s[f->i].n); - else if (buf->c[f->i] == Change) { - str_delete(f->s, buf->p0[f->i], buf->s[f->i].n); - str_insert(f->s, &buf->s[f->i], buf->p0[f->i]); - } - fr_update(1); + if (buf->ds[++f->i].n) + str_delete(f->s, buf->p0[f->i], buf->p0[f->i] + buf->ds[f->i].n); + if (buf->is[f->i].n) + str_insert(f->s, &buf->is[f->i], buf->p0[f->i]); + f->dot.p0 = f->dot.p1 = buf->p0[f->i]; + f->dirty = '*'; + fr_update(); } static void escape(int c) { + counter = 0; c = fgetc(stdin) - 0x30; if (c > 0 && c <= 8) { --c; @@ -383,13 +418,13 @@ static int isword(uchar c) { switch (c) { - case ' ': - case '\t': - case '\n': - case '.': - case '(': - case ')': - return 0; + case ' ': case '\t': case '\n': case '.': + case '(': case ')': case '{': case '}': + case '[': case ']': case ':': case ';': + case ',': case '<': case '>': case '#': + case '*': case '+': case '-': case '!': + case '%': case '\\': case '/': case '"': + return 0; } return 1; } @@ -397,16 +432,14 @@ isword(uchar c) static void insert(int arg) { - String s; - uchar c; + String s, c; - str_init(&s); + str_init(&s), str_init(&c); + str_addc(&c, '\0'); switch (arg) { case StartLine: - move(StartLine); - break; case EndLine: - move(EndLine); + move(arg); break; case Right: move(Right); @@ -419,44 +452,38 @@ insert(int arg) str_insert(f->s, &s, f->dot.p0); ++f->dot.p1; break; - case Up: - move(StartLine); - str_addc(&s, '\n'); - str_insert(f->s, &s, f->dot.p0); - ++f->dot.p1; - break; } for (;;) { win_query(&w); - fr_update(0); - c = fgetc(stdin); - switch (c) { + fr_update(); + switch ((c.s[0] = fgetc(stdin))) { case Esc: - if (f->dot.p1 != f->dot.p0) - str_delete(f->s, f->dot.p0, f->dot.p1); - goto endloop; + goto endmode; case Del: - str_delc(&s); + if (f->dot.p1 != f->dot.p0) { + str_delc(&s); + str_delete(f->s, f->dot.p1 - 1, f->dot.p1); + --f->dot.p1; + } break; default: - str_addc(&s, c); + str_addc(&s, c.s[0]); + str_insert(f->s, &c, f->dot.p1); } - if (f->dot.p1 != f->dot.p0) - str_delete(f->s, f->dot.p0, f->dot.p1); - str_insert(f->s, &s, f->dot.p0); f->dot.p1 = f->dot.p0 + s.n; } - endloop: - if (s.n) { - if (arg == Up) - str_addc(&s, '\n'); - buf_add(buf, &s, Insert, f->dot.p0); + endmode: + str_zero(&istr); + str_insert(&istr, &s, 0); + for (counter ? --counter : 0; counter; --counter) { + str_insert(&istr, &s, istr.n); str_insert(f->s, &s, f->dot.p0); - fr_update(0); - f->dirty = '*'; } - str_close(&s); + buf_add(buf, &istr, NULL, Insert, arg, counter, f->dot.p0); + str_close(&s), str_close(&c); f->dot.p0 = f->dot.p1; + if (f->dot.p1 >= f->s->n) + move(Left); } static void @@ -473,6 +500,7 @@ init() fr = &frame[0]; buf = &buffer[0]; str_init(&srch); + str_init(&istr); } static void @@ -498,7 +526,8 @@ file_init(File* f) f->name = emalloc(sizeof(String)); str_init(f->s); str_init(f->name); - f->i = f->dirty = f->dot.p0 = f->dot.p1 = 0; + f->dirty = f->dot.p0 = f->dot.p1 = 0; + f->i = -1; } static void @@ -514,7 +543,8 @@ file_load(File* f) rewind(disk); fread(f->s->s, f->s->n, 1, disk); fclose(disk); - f->i = f->dot.p0 = f->dot.p1 = 0; + f->dot.p0 = f->dot.p1 = 0; + f->i = -1; } static void @@ -587,7 +617,7 @@ fr_insure(Frame* fr, ushort n) } static void -fr_update(int arg) +fr_update() { Frame fr0; Posn p0, p1; @@ -599,7 +629,7 @@ fr_update(int arg) printf(ED); goto status; } - if (arg || !fr->n || f->dot.p0 != f->dot.p1 || f->dot.p1 > fr->a[fr->n - 1].p1 || f->dot.p1 < fr->a[0].p0 || fr->n - fr->dot <= w.wy || fr->dot <= half) { + if (1 || !fr->n || f->dot.p0 != f->dot.p1 || f->dot.p1 > fr->a[fr->n - 1].p1 || f->dot.p1 < fr->a[0].p0 || fr->n - fr->dot <= w.wy || fr->dot <= half) { fr_zero(fr); for (p0 = f->dot.p1; p0; --p0) if (f->s->s[p0 - 1] == '\n') @@ -670,10 +700,12 @@ move(int arg) f->dot.p1 = 0; break; case Down: - if (fr->dot < fr->n - 1) + if (!f->s->n) + return; + else if (fr->dot < fr->n - 1) f->dot.p1 = fr->a[fr->dot + 1].p0; else - f->dot.p1 = fr->a[fr->dot].p1; + f->dot.p1 = fr->a[fr->dot].p1 - 1; break; case HalfUp: if (fr->dot < w.wy/2) @@ -700,10 +732,12 @@ move(int arg) f->dot.p1 = f->s->n - 1; break; case StartLine: + if (fr->dot) + for (;fr->dot > 1 && f->s->s[fr->a[fr->dot].p0 - 1] != '\n'; --fr->dot); f->dot.p1 = fr->a[fr->dot].p0; break; case EndLine: - f->dot.p1 = fr->a[fr->dot].p1; + for (;f->dot.p1 + 1 < f->s->n && f->s->s[f->dot.p1] != '\n'; ++f->dot.p1); break; case Word: for (;f->dot.p1 < f->s->n && isword(f->s->s[f->dot.p1]); ++f->dot.p1); @@ -777,6 +811,70 @@ search(int arg) f->dot.p0 = f->dot.p1 = p - (uchar*)f->s->s; } +static int +select(int arg) +{ + Posn p0; + + if (arg > 0x7f) { + arg -= 0x7f; + goto till; + } + f->dot.p1 = f->dot.p0; + if (!counter) + ++counter; + switch (arg) { + case Letter: + p0 = f->dot.p0; + for (;counter > 1; --counter) + move(Right); + break; + case Line: + move(StartLine); + p0 = f->dot.p0; + for (;counter; --counter) { + move(EndLine); + move(Right); + } + if (f->dot.p1 + 1 < f->s->n) + move(Left); + break; + case Bottom: + move(StartLine); + move(Left); + p0 = f->dot.p0; + move(Bottom); + break; + case Top: + p0 = 0; + move(EndLine); + break; + case Word: + p0 = f->dot.p0; + for (;counter; --counter) + move(EndWord); + break; + case Till: + arg = fgetc(stdin); + if (arg == Esc) + return 0; + till: + p0 = f->dot.p0; + for (;counter; --counter) + if (f->dot.p1 + 1 < f->s->n) + for (++f->dot.p1; f->dot.p1 + 1 < f->s->n && f->s->s[f->dot.p1 + 1] != arg; ++f->dot.p1); + if (f->s->s[f->dot.p1 + 1] != arg) { + f->dot.p1 = f->dot.p0; + return -1; + } + arg += 0x7f; + break; + } + f->dot.p0 = p0; + counter = 0; + return arg; +} + static void str_init(String* p) { @@ -836,8 +934,6 @@ str_delc(String* p) static void str_insert(String* p, String* q, Posn p0) { - if (p0 > p->n) - p0 = p->n; str_insure(p, p->n + q->n); memmove(p->s + p0 + q->n, p->s + p0, p->n - p0); memmove(p->s + p0, q->s, q->n); @@ -847,8 +943,6 @@ str_insert(String* p, String* q, Posn p0) static void str_delete(String* p, Posn p0, Posn p1) { - if (p1 > p->n) - p1 = p->n; memmove(p->s + p0, p->s + p1, p->n - p1); p->n -= p1 - p0; } @@ -856,20 +950,15 @@ str_delete(String* p, Posn p0, Posn p1) static void undo(int arg) { - if (!f->i) + if (f->i < 0) return; - if (buf->c[f->i] == Insert) - str_delete(f->s, buf->p0[f->i], buf->p0[f->i] + buf->s[f->i].n); - else if (buf->c[f->i] == Delete) - str_insert(f->s, &buf->s[f->i], buf->p0[f->i]); - else if (buf->c[f->i] == Change) { - if (buf->p0[f->i] == -1) - --f->i, str_delete(f->s, buf->p0[f->i], buf->p0[f->i] + buf->s[f->i].n); - --f->i, str_insert(f->s, &buf->s[f->i], buf->p0[f->i]); - } - f->dot.p0 = f->dot.p1 = buf->p0[f->i]; - --f->i; - fr_update(1); + if (buf->is[f->i].n) + str_delete(f->s, buf->p0[f->i], buf->p0[f->i] + buf->is[f->i].n); + if (buf->ds[f->i].n) + str_insert(f->s, &buf->ds[f->i], buf->p0[f->i]); + f->dot.p0 = f->dot.p1 = buf->p0[f->i--]; + f->dirty = '*'; + fr_update(); } int @@ -885,7 +974,7 @@ main(int argc, char* argv[]) } for (;;) { win_query(&w); - fr_update(0); + fr_update(); c = fgetc(stdin); for (i = 0; i < LENGTH(keys); ++i) { if (keys[i].key == c) {