#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;
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) {
}
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;
*(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);
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);
}
return curr;
}
+
/* 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);
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) {
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) {
}
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);
/*****************************************************************************/
+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);
{ 'B', cursor_prevbigword },
/* undo/redo handling */
- //{ 'u', undo },
- //{ 'r', redo },
+ { 'u', undo },
+ { 'r', redo },
/* insert mode handling */
{ 'a', insert_after },
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);
}
} 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);
}
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++);
}
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);
}
}