]> git.mdlowis.com Git - projs/tide.git/commitdiff
first crack at undo/redo. *horribly* inefficient
authorMichael D. Lowis <mike@mdlowis.com>
Thu, 27 Oct 2016 00:47:03 +0000 (20:47 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Thu, 27 Oct 2016 00:47:03 +0000 (20:47 -0400)
buf.c
edit.h
keyboard.c
mouse.c
tests/buf.c
xedit.c

diff --git a/buf.c b/buf.c
index 67b85d5e86cdc78a46c6873eccd025ce4aaad1bd..05ce9f70907756d666fdce287ff1922242e47cff 100644 (file)
--- a/buf.c
+++ b/buf.c
@@ -6,7 +6,7 @@
 #include "edit.h"
 
 void buf_load(Buf* buf, char* path) {
-    buf->insert_mode = true;
+    buf_setlocked(buf, false);
     if (!strcmp(path,"-")) {
         buf->charset = UTF_8;
         Rune r;
@@ -29,8 +29,8 @@ void buf_load(Buf* buf, char* path) {
             buf_ins(buf, 0, (Rune)'\n');
         funmap(file);
     }
-    buf->insert_mode = false;
-    buf->modified    = false;
+    buf_setlocked(buf, true);
+    buf->modified = false;
 }
 
 void buf_save(Buf* buf) {
@@ -80,32 +80,41 @@ static void syncgap(Buf* buf, unsigned off) {
 }
 
 void buf_init(Buf* buf) {
-    buf->insert_mode = false;
-    buf->modified    = false;
-    buf->charset     = DEFAULT_CHARSET;
-    buf->crlf        = DEFAULT_CRLF;
-    buf->bufsize     = BufSize;
-    buf->bufstart    = (Rune*)malloc(buf->bufsize * sizeof(Rune));
-    buf->bufend      = buf->bufstart + buf->bufsize;
-    buf->gapstart    = buf->bufstart;
-    buf->gapend      = buf->bufend;
+    buf->locked   = true;
+    buf->modified = false;
+    buf->charset  = DEFAULT_CHARSET;
+    buf->crlf     = DEFAULT_CRLF;
+    buf->bufsize  = BufSize;
+    buf->bufstart = (Rune*)malloc(buf->bufsize * sizeof(Rune));
+    buf->bufend   = buf->bufstart + buf->bufsize;
+    buf->gapstart = buf->bufstart;
+    buf->gapend   = buf->bufend;
+    buf->undo     = NULL;
+    buf->redo     = NULL;
 }
 
-void buf_clr(Buf* buf) {
-    free(buf->bufstart);
-    buf_init(buf);
+//static void log_insert(Log** list, unsigned beg, unsigned end) {
+static void log_insert(Log** list, Log* log) {
+    Log* newlog  = (Log*)calloc(sizeof(Log), 1);
+    *newlog = *log;
+    newlog->next = *list;
+    *list = newlog;
 }
 
-void buf_del(Buf* buf, unsigned off) {
-    if (!buf->insert_mode) { return; }
-    buf->modified = true;
-    syncgap(buf, off);
-    buf->gapend++;
+//static void log_delete(Log** list, unsigned off, Rune r) {
+static void log_delete(Log** list, Log* log) {
+    size_t len  = log->data.del.len;
+    Rune* runes = (Rune*)malloc(sizeof(Rune) * len);
+    memcpy(runes, log->data.del.runes, sizeof(Rune) * len);
+    Log* newlog  = (Log*)calloc(sizeof(Log), 1);
+    *newlog = *log;
+    newlog->next = *list;
+    newlog->data.del.runes = runes;
+    *list = newlog;
 }
 
-void buf_ins(Buf* buf, unsigned off, Rune rune) {
-    if (!buf->insert_mode) { return; }
-    buf->modified = true;
+static void insert(Buf* buf, unsigned off, Rune rune)
+{
     syncgap(buf, off);
     if (buf->crlf && rune == '\n' && buf_get(buf, off-1) == '\r')
         *(buf->gapstart-1) = RUNE_CRLF;
@@ -113,6 +122,132 @@ void buf_ins(Buf* buf, unsigned off, Rune rune) {
         *(buf->gapstart++) = rune;
 }
 
+void buf_ins(Buf* buf, unsigned off, Rune rune) {
+    if (buf->locked) { return; }
+    buf->modified = true;
+    //log_insert(&(buf->undo), off);
+    log_insert(&(buf->undo), &(Log){
+        .insert = true,
+        .data.ins = { .beg = off, .end = off+1 }
+    });
+    insert(buf, off, rune);
+
+    ///* add or update the insert log */
+    //if (NULL == buf->undo || !buf->undo->insert || buf->undo->locked ||
+    //    off < buf->undo->data.ins.beg || off > buf->undo->data.ins.end) {
+    //    puts("creating insert log");
+    //    Log* log = (Log*)calloc(sizeof(Log),1);
+    //    log->insert = true;
+    //    log->data.ins.beg = off;
+    //    log->data.ins.end = off+1;
+    //    log->next = buf->undo;
+    //    if (buf->undo)
+    //        buf->undo->locked = true;
+    //    buf->undo = log;
+    //} else {
+    //    puts("updating insert log");
+    //    buf->undo->data.ins.end++;
+    //}
+}
+
+static void delete(Buf* buf, unsigned off) {
+    syncgap(buf, off);
+    buf->gapend++;
+}
+
+void buf_del(Buf* buf, unsigned off) {
+    if (buf->locked) { return; }
+    buf->modified = true;
+
+    //log_delete(&(buf->undo), off, buf_get(buf, off));
+    log_delete(&(buf->undo), &(Log){
+        .insert = false,
+        .data.del = { .off = off, .len = 1, .runes = &(Rune){ buf_get(buf,off) } }
+    });
+    delete(buf, off);
+
+    /* add or update the insert log */
+    //if (NULL == buf->undo || buf->undo->insert || buf->undo->locked ||
+    //    off < buf->undo->data.ins.beg || off > buf->undo->data.ins.end) {
+    //    puts("creating insert log");
+    //    Log* log = (Log*)calloc(sizeof(Log),1);
+    //    log->insert = true;
+    //    log->data.ins.beg = off;
+    //    log->data.ins.end = off+1;
+    //    log->next = buf->undo;
+    //    if (buf->undo)
+    //        buf->undo->locked = true;
+    //    buf->undo = log;
+    //} else {
+    //    puts("updating insert log");
+    //    buf->undo->data.ins.end++;
+    //}
+}
+
+unsigned buf_undo(Buf* buf, unsigned pos) {
+    /* pop the last undo action */
+    Log* log = buf->undo;
+    if (!log) return pos;
+    buf->undo = log->next;
+    /* */
+    Log* redo = (Log*)calloc(sizeof(Log), 1);
+    if (log->insert) {
+        redo->insert = false;
+        size_t n = (log->data.ins.end - log->data.ins.beg);
+        redo->data.del.off   = log->data.ins.beg;
+        redo->data.del.len   = n;
+        redo->data.del.runes = (Rune*)malloc(n * sizeof(Rune));
+        for (size_t i = 0; i < n; i++) {
+            redo->data.del.runes[i] = buf_get(buf, log->data.ins.beg);
+            delete(buf, log->data.ins.beg);
+        }
+        pos = redo->data.del.off;
+    } else {
+        redo->insert = true;
+        redo->data.ins.beg = log->data.del.off;
+        redo->data.ins.end = log->data.del.off + log->data.del.len;
+        for (size_t i = log->data.del.len; i > 0; i--) {
+            insert(buf, redo->data.ins.beg, log->data.del.runes[i-1]);
+        }
+        pos = redo->data.ins.end;
+    }
+    redo->next = buf->redo;
+    buf->redo  = redo;
+    return pos;
+}
+
+unsigned buf_redo(Buf* buf, unsigned pos) {
+    /* pop the last redo action */
+    Log* log = buf->redo;
+    if (!log) return pos;
+    buf->redo = log->next;
+    /* */
+    Log* undo = (Log*)calloc(sizeof(Log), 1);
+    if (log->insert) {
+        undo->insert = false;
+        size_t n = (log->data.ins.end - log->data.ins.beg);
+        undo->data.del.off   = log->data.ins.beg;
+        undo->data.del.len   = n;
+        undo->data.del.runes = (Rune*)malloc(n * sizeof(Rune));
+        for (size_t i = 0; i < n; i++) {
+            undo->data.del.runes[i] = buf_get(buf, log->data.ins.beg);
+            delete(buf, log->data.ins.beg);
+        }
+        pos = undo->data.del.off;
+    } else {
+        undo->insert = true;
+        undo->data.ins.beg = log->data.del.off;
+        undo->data.ins.end = log->data.del.off + log->data.del.len;
+        for (size_t i = log->data.del.len; i > 0; i--) {
+            insert(buf, undo->data.ins.beg, log->data.del.runes[i-1]);
+        }
+        pos = undo->data.ins.end;
+    }
+    undo->next = buf->undo;
+    buf->undo  = undo;
+    return pos;
+}
+
 Rune buf_get(Buf* buf, unsigned off) {
     if (off >= buf_end(buf)) return (Rune)'\n';
     size_t bsz = (buf->gapstart - buf->bufstart);
@@ -122,6 +257,16 @@ Rune buf_get(Buf* buf, unsigned off) {
         return *(buf->gapend + (off - bsz));
 }
 
+void buf_setlocked(Buf* buf, bool locked) {
+    if (locked)
+        buf->undo->locked =true;
+    buf->locked = locked;
+}
+
+bool buf_locked(Buf* buf) {
+    return buf->locked;
+}
+
 bool buf_iseol(Buf* buf, unsigned off) {
     Rune r = buf_get(buf, off);
     return (r == '\n' || r == RUNE_CRLF);
@@ -254,3 +399,4 @@ unsigned buf_setcol(Buf* buf, unsigned pos, unsigned col) {
     }
     return curr;
 }
+
diff --git a/edit.h b/edit.h
index 0a4ddd05d75a85869d12568d34d062bfd470c3a3..576f8139b61fc7a22cb02996c2cf56d07704a0d0 100644 (file)
--- a/edit.h
+++ b/edit.h
@@ -41,26 +41,49 @@ bool risblank(Rune r);
 
 /* Buffer management functions
  *****************************************************************************/
+typedef struct Log {
+    struct Log* next;
+    bool locked;
+    bool insert;
+    union {
+        struct {
+            size_t beg;
+            size_t end;
+        } ins;
+        struct {
+            size_t off;
+            size_t len;
+            Rune* runes;
+        } del;
+    } data;
+} Log;
+
 typedef struct buf {
-    char* path;       /* the path to the open file */
-    int charset;      /* the character set of the buffer */
-    int crlf;         /* tracks whether the file uses dos style line endings */
-    bool insert_mode; /* tracks current mode */
-    bool modified;    /* tracks whether the buffer has been modified */
-    size_t bufsize;   /* size of the buffer in runes */
-    Rune* bufstart;   /* start of the data buffer */
-    Rune* bufend;     /* end of the data buffer */
-    Rune* gapstart;   /* start of the gap */
-    Rune* gapend;     /* end of the gap */
+    char* path;     /* the path to the open file */
+    int charset;    /* the character set of the buffer */
+    int crlf;       /* tracks whether the file uses dos style line endings */
+    bool locked;    /* tracks current mode */
+    bool modified;  /* tracks whether the buffer has been modified */
+    size_t bufsize; /* size of the buffer in runes */
+    Rune* bufstart; /* start of the data buffer */
+    Rune* bufend;   /* end of the data buffer */
+    Rune* gapstart; /* start of the gap */
+    Rune* gapend;   /* end of the gap */
+    Log* undo;      /* undo list */
+    Log* redo;      /* redo list */
 } Buf;
 
 void buf_load(Buf* buf, char* path);
 void buf_save(Buf* buf);
 void buf_init(Buf* buf);
-void buf_clr(Buf* buf);
-void buf_del(Buf* buf, unsigned pos);
 void buf_ins(Buf* buf, unsigned pos, Rune);
+void buf_del(Buf* buf, unsigned pos);
+unsigned buf_undo(Buf* buf, unsigned pos);
+unsigned buf_redo(Buf* buf, unsigned pos);
 Rune buf_get(Buf* buf, unsigned pos);
+void buf_setlocked(Buf* buf, bool locked);
+bool buf_locked(Buf* buf);
+
 bool buf_iseol(Buf* buf, unsigned pos);
 unsigned buf_bol(Buf* buf, unsigned pos);
 unsigned buf_eol(Buf* buf, unsigned pos);
index 3012a041acb4859013dda1cac007087fc890a169..59df3a2bf955e7e2a290f28311b7c93f62923533 100644 (file)
@@ -36,16 +36,16 @@ static void cursor_eol(void) {
 
 static void insert_before(void) {
     SelEnd = SelBeg;
-    Buffer.insert_mode = true;
+    buf_setlocked(&Buffer,false);
 }
 
 static void insert_after(void) {
     SelBeg = ++SelEnd;
-    Buffer.insert_mode = true;
+    buf_setlocked(&Buffer,false);
 }
 
 static void exit_insert(void) {
-    Buffer.insert_mode = false;
+    buf_setlocked(&Buffer,true);
 }
 
 static void write(void) {
@@ -65,19 +65,19 @@ static void quit(void) {
 static void dot_delete(void) {
     if (SelEnd == buf_end(&Buffer)) return;
     size_t n = SelEnd - SelBeg;
-    bool insert = Buffer.insert_mode;
-    if (!insert || !n) n++;
-    Buffer.insert_mode = true;
+    bool locked = buf_locked(&Buffer);
+    if (locked || !n) n++;
+    buf_setlocked(&Buffer,false);
     for (size_t i = 0; i < n; i++)
         buf_del(&Buffer, SelBeg);
     SelEnd = SelBeg;
     TargetCol = buf_getcol(&Buffer, SelEnd);
-    Buffer.insert_mode = insert;
+    buf_setlocked(&Buffer, locked);
 }
 
 static void dot_change(void) {
     dot_delete();
-    Buffer.insert_mode = true;
+    buf_setlocked(&Buffer,false);
 }
 
 static void dot_backspace(void) {
@@ -88,7 +88,7 @@ static void dot_backspace(void) {
 }
 
 static void insert(Rune r) {
-    if (!Buffer.insert_mode) return;
+    if (buf_locked(&Buffer)) return;
     buf_ins(&Buffer, SelEnd++, r);
     SelBeg = SelEnd;
     TargetCol = buf_getcol(&Buffer, SelEnd);
@@ -116,6 +116,18 @@ static void cursor_prevbigword(void) {
 
 /*****************************************************************************/
 
+static void undo(void) {
+    SelBeg = SelEnd = buf_undo(&Buffer, SelEnd);
+    TargetCol = buf_getcol(&Buffer, SelEnd);
+}
+
+static void redo(void) {
+    SelBeg = SelEnd = buf_redo(&Buffer, SelEnd);
+    TargetCol = buf_getcol(&Buffer, SelEnd);
+}
+
+/*****************************************************************************/
+
 typedef struct {
     Rune key;
     void (*action)(void);
@@ -156,8 +168,8 @@ static KeyBinding_T Normal[] = {
     { 'B',        cursor_prevbigword },
 
     /* undo/redo handling */
-    //{ 'u',        undo },
-    //{ 'r',        redo },
+    { 'u',        undo },
+    { 'r',        redo },
 
     /* insert mode handling */
     { 'a',        insert_after    },
@@ -216,8 +228,8 @@ void handle_key(Rune key) {
     if (key == '\r') key = '\n';
     if (key == '\n' && Buffer.crlf) key = RUNE_CRLF;
     /* handle the key */
-    if (Buffer.insert_mode)
-        process_table(Insert, key);
-    else
+    if (buf_locked(&Buffer))
         process_table(Normal, key);
+    else
+        process_table(Insert, key);
 }
diff --git a/mouse.c b/mouse.c
index d8ef254957a49097fc3939c1f546b2a398c5883c..f6239178a5031513f8e12d1066cfc61e9461ba55 100644 (file)
--- a/mouse.c
+++ b/mouse.c
@@ -29,19 +29,19 @@ void selection(MouseEvent* mevnt) {
     } else if (risword(r)) {
         SelBeg = buf_bow(&Buffer, SelEnd);
         SelEnd = buf_eow(&Buffer, SelEnd);
-        if (Buffer.insert_mode) SelEnd++;
+        if (!buf_locked(&Buffer)) SelEnd++;
     } else if (r == '(' || r == ')') {
         SelBeg = buf_lscan(&Buffer, SelEnd, '(');
         SelEnd = buf_rscan(&Buffer, SelEnd, ')');
-        if (Buffer.insert_mode) SelEnd++;
+        if (!buf_locked(&Buffer)) SelEnd++;
     } else if (r == '[' || r == ']') {
         SelBeg = buf_lscan(&Buffer, SelEnd, '[');
         SelEnd = buf_rscan(&Buffer, SelEnd, ']');
-        if (Buffer.insert_mode) SelEnd++;
+        if (!buf_locked(&Buffer)) SelEnd++;
     } else if (r == '{' || r == '}') {
         SelBeg = buf_lscan(&Buffer, SelEnd, '{');
         SelEnd = buf_rscan(&Buffer, SelEnd, '}');
-        if (Buffer.insert_mode) SelEnd++;
+        if (!buf_locked(&Buffer)) SelEnd++;
     } else {
         bigword(mevnt);
     }
index fac28c2ce933d14cab270afa58fbd6ca7cd60a31..ccd1f1c0b6e835245e9181ea5bd62d761d2cb847 100644 (file)
@@ -3,12 +3,17 @@
 
 static Buf TestBuf;
 
+static void buf_clr(Buf* buf) {
+    free(buf->bufstart);
+    buf_init(buf);
+}
+
 static void set_buffer_text(char* str) {
     int i = 0;
     buf_clr(&TestBuf);
     for (Rune* curr = TestBuf.bufstart; curr < TestBuf.bufend; curr++)
         *curr = '-';
-    TestBuf.insert_mode = true;
+    TestBuf.locked = false;
     while (*str)
         buf_ins(&TestBuf, i++, (Rune)*str++);
 }
diff --git a/xedit.c b/xedit.c
index 2d2f3df77f3ab870ebcba93e5cd1891e3faeaa3d..4a9755e7e040db99485186655a4d8729d4b9e901 100644 (file)
--- a/xedit.c
+++ b/xedit.c
@@ -377,11 +377,11 @@ static void draw_cursor(unsigned csrx, unsigned csry) {
     unsigned rwidth;
     UGlyph* csrrune = screen_getglyph(csry, csrx, &rwidth);
     csrrune->attr = (CLR_BASE3 << 8 | CLR_BASE03);
-    if (Buffer.insert_mode) {
-        XftDrawRect(X.xft, clr(CLR_BASE3), csrx * Fonts.base.width, (csry+1) * Fonts.base.height, 1, Fonts.base.height);
-    } else {
+    if (buf_locked(&Buffer)) {
         XftDrawRect(X.xft, clr(CLR_BASE3), csrx * Fonts.base.width, (csry+1) * Fonts.base.height, rwidth * Fonts.base.width, Fonts.base.height);
         draw_glyphs(csrx * Fonts.base.width, (csry+2) * Fonts.base.height, csrrune, 1, rwidth);
+    } else {
+        XftDrawRect(X.xft, clr(CLR_BASE3), csrx * Fonts.base.width, (csry+1) * Fonts.base.height, 1, Fonts.base.height);
     }
 }