summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-12-20 22:59:03 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2020-12-20 22:59:03 -0500
commitb50f5c763c575d7284b7a7e7507b2151d1e98fbf (patch)
treeca0e9ac875a5919e8143824c7df221d3095a02cc
parent4f5cee291bb3a5e06a63dc28bf3757c9af49c250 (diff)
more undo
-rw-r--r--base.h8
-rw-r--r--buffer.c123
2 files changed, 113 insertions, 18 deletions
diff --git a/base.h b/base.h
index 65c8f20..b17a998 100644
--- a/base.h
+++ b/base.h
@@ -10,7 +10,7 @@
#endif
#include <stdbool.h>
-#include <stdint.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
@@ -24,11 +24,17 @@ typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
+#define U32_FMT "%" PRIu32
+#define U64_FMT "%" PRIu64
+
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
+#define I32_FMT "%" PRId32
+#define I64_FMT "%" PRId64
+
typedef unsigned int uint;
typedef unsigned long ulong;
diff --git a/buffer.c b/buffer.c
index 54c6dbe..39a25a5 100644
--- a/buffer.c
+++ b/buffer.c
@@ -90,11 +90,19 @@ static void buffer_out_of_mem(TextBuffer *buffer) {
buffer_seterr(buffer, "Out of memory.");
}
+// add this edit to the undo history
static void buffer_append_edit(TextBuffer *buffer, BufferEdit *edit) {
edit->next = buffer->undo_history;
buffer->undo_history = edit;
}
+// add this edit to the redo history
+static void buffer_append_redo(TextBuffer *buffer, BufferEdit *edit) {
+ edit->next = buffer->redo_history;
+ buffer->redo_history = edit;
+}
+
+
static void *buffer_calloc(TextBuffer *buffer, size_t n, size_t size) {
void *ret = calloc(n, size);
if (!ret) buffer_out_of_mem(buffer);
@@ -107,10 +115,77 @@ static void *buffer_realloc(TextBuffer *buffer, void *p, size_t new_size) {
return ret;
}
+// ensures that `p` refers to a valid position.
+static void buffer_pos_validate(TextBuffer *buffer, BufferPos *p) {
+ if (p->line >= buffer->nlines)
+ p->line = buffer->nlines - 1;
+ u32 line_len = buffer->lines[p->line].len;
+ if (p->index > line_len)
+ p->index = line_len;
+}
+
+static bool buffer_pos_valid(TextBuffer *buffer, BufferPos p) {
+ return p.line < buffer->nlines && p.index <= buffer->lines[p.line].len;
+}
+
+
+// get some number of characters of text from the given position in the buffer.
+static Status buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t *text, u32 nchars) {
+ if (!buffer_pos_valid(buffer, pos)) {
+ return false;
+ }
+ char32_t *p = text;
+ u32 chars_left = nchars;
+ Line *line = &buffer->lines[pos.line], *end = buffer->lines + buffer->nlines;
+ u32 index = pos.index;
+ while (chars_left) {
+ u32 chars_from_this_line = line->len - index;
+ if (chars_left <= chars_from_this_line) {
+ memcpy(p, line->str, chars_left);
+ chars_left = 0;
+ } else {
+ memcpy(p, line->str, chars_from_this_line);
+ p += chars_from_this_line;
+ *p++ = U'\n';
+ chars_left -= chars_from_this_line+1;
+ }
+
+ index = 0;
+ ++line;
+ if (chars_left && line == end) {
+ // reached end of file before getting full text... this isn't good
+ buffer_seterr(buffer, "Failed to fetch " U32_FMT " characters at " U32_FMT ":" U32_FMT ".", nchars, pos.line+1, pos.index+1);
+ return false;
+ }
+ }
+ return true;
+}
+
// functions for creating buffer edits:
-// call these before you make an edit to keep an undo event
-static WarnUnusedResult BufferEdit *buffer_edit_delete_text(TextBuffer *buffer, BufferPos start, u32 len) {
- // @TODO
+// call these before you make an edit to create an undo event
+static WarnUnusedResult BufferEdit *buffer_create_edit_delete_text(TextBuffer *buffer, BufferPos start, u32 len) {
+ BufferEdit *e = buffer_calloc(buffer, 1, sizeof *e + len * sizeof *e->text);
+ if (e) {
+ e->type = BUFFER_EDIT_DELETE_TEXT;
+ e->pos = start;
+ e->text_len = len;
+ if (!buffer_get_text_at_pos(buffer, start, e->text, len)) {
+ free(e);
+ e = NULL;
+ }
+ }
+ return e;
+}
+
+static WarnUnusedResult BufferEdit *buffer_create_edit_insert_text(TextBuffer *buffer, BufferPos start, u32 len) {
+ if (!buffer_pos_valid(buffer, start)) return NULL;
+ BufferEdit *e = buffer_calloc(buffer, 1, sizeof *e);
+ if (e) {
+ e->type = BUFFER_EDIT_INSERT_TEXT;
+ e->pos = start;
+ e->text_len = len;
+ }
+ return e;
}
@@ -563,20 +638,6 @@ static void buffer_scroll_to_cursor(TextBuffer *buffer) {
buffer_correct_scroll(buffer); // it's possible that min/max_scroll_x/y go too far
}
-
-// ensures that `p` refers to a valid position.
-static void buffer_pos_validate(TextBuffer *buffer, BufferPos *p) {
- if (p->line >= buffer->nlines)
- p->line = buffer->nlines - 1;
- u32 line_len = buffer->lines[p->line].len;
- if (p->index > line_len)
- p->index = line_len;
-}
-
-static bool buffer_pos_valid(TextBuffer *buffer, BufferPos p) {
- return p.line < buffer->nlines && p.index <= buffer->lines[p.line].len;
-}
-
// returns "p2 - p1", that is, the number of characters between p1 and p2.
static i64 buffer_pos_diff(TextBuffer *buffer, BufferPos p1, BufferPos p2) {
assert(buffer_pos_valid(buffer, p1));
@@ -1038,3 +1099,31 @@ void buffer_backspace_words_at_cursor(TextBuffer *buffer, i64 nwords) {
buffer_backspace_words_at_pos(buffer, &buffer->cursor_pos, nwords);
}
+void buffer_undo(TextBuffer *buffer, i64 ntimes) {
+ for (i64 i = 0; i < ntimes; ++i) {
+ BufferEdit *edit = buffer->undo_history;
+ BufferEdit *inverse = NULL;
+
+ switch (edit->type) {
+ case BUFFER_EDIT_INSERT_TEXT:
+ // the inverse edit is the deletion of this text
+ inverse = buffer_create_edit_delete_text(buffer, edit->pos, edit->text_len);
+ // delete the text
+ buffer_delete_chars_at_pos(buffer, edit->pos, edit->text_len);
+ break;
+ case BUFFER_EDIT_DELETE_TEXT: {
+ // the inverse edit is the insertion of this text
+ inverse = buffer_create_edit_insert_text(buffer, edit->pos, edit->text_len);
+ // insert the text
+ String32 str = {edit->text_len, edit->text};
+ buffer_insert_text_at_pos(buffer, edit->pos, str);
+ } break;
+ }
+
+ if (inverse) {
+ buffer_append_redo(buffer, inverse);
+ }
+
+ free(edit);
+ }
+}