summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-12-21 14:11:28 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2020-12-21 14:11:28 -0500
commit12cce30fad0a685d333cc23c7c71fded4b6cf329 (patch)
tree79ca41ac4e53596079db5e1637730da405b8942c
parent41c33e59e85909989009e96370403abf319008ad (diff)
undo working, just need to chain events (rather than undoing one character insertion/deletion at a time)
-rw-r--r--arr.c18
-rw-r--r--buffer.c78
-rw-r--r--main.c4
3 files changed, 78 insertions, 22 deletions
diff --git a/arr.c b/arr.c
index b9b3387..0fc856f 100644
--- a/arr.c
+++ b/arr.c
@@ -166,8 +166,13 @@ static inline void arr_set_len_(void **arr, size_t member_size, size_t n) {
#define arr_cast_typeof(a)
#endif
+#define arr__join2(a,b) a##b
+#define arr__join(a,b) arr__join2(a,b) // macro used internally
+
// if the array is not NULL, free it and set it to NULL
#define arr_free(a) do { if (a) { free(arr_hdr_(a)); (a) = NULL; } } while (0)
+// a nice alias
+#define arr_clear(a) arr_free(a)
// add an item to the array - if allocation fails, the array will be freed and set to NULL.
// (how this works: if we can successfully grow the array, increase the length and add the item.)
#define arr_add(a, x) do { if (((a) = arr_cast_typeof(a) arr_grow1_((a), sizeof *(a)))) ((a)[arr_hdr_(a)->len++] = (x)); } while (0)
@@ -181,8 +186,17 @@ static inline void arr_set_len_(void **arr, size_t member_size, size_t n) {
#define arr_pop_last(a) ((a)[--arr_hdr_(a)->len])
#define arr_size_in_bytes(a) (arr_len(a) * sizeof *(a))
#define arr_lastp(a) ((a) ? &(a)[arr_len(a)-1] : NULL)
-#define arr_shrink(a, n) ((void)((a) && (arr_hdr_(a)->len -= (n))))
-#define arr_foreach_backwards(a, var) for (var = arr_lastp(a); var; var = var == (a) ? NULL : var-1)
+#define arr_foreach_ptr_end(a, type, var, end) type *end = (a) + arr_len(a); \
+ for (type *var = (a); var != end; ++var)
+// Iterate through each element of the array, setting var to a pointer to the element.
+// You can't use this like, e.g.:
+// if (something)
+// arr_foreach_ptr(a, int, i);
+// You'll get an error. You will need to use braces because it expands to multiple statements.
+// (we need to name the end pointer something unique, which is why there's that arr__join thing
+// we can't just declare it inside the for loop, because type could be something like char *.)
+#define arr_foreach_ptr(a, type, var) arr_foreach_ptr_end(a, type, var, arr__join(_foreach_end,__LINE__))
+
#define arr_reverse(a, type) do { \
u64 _i, _len = arr_len(a); \
for (_i = 0; 2*_i < _len; ++_i) { \
diff --git a/buffer.c b/buffer.c
index 7ede69d..5acc536 100644
--- a/buffer.c
+++ b/buffer.c
@@ -37,6 +37,7 @@ typedef struct {
Font *font;
BufferPos cursor_pos;
u8 tab_width;
+ bool store_undo_events; // set to false to disable undo events
float x1, y1, x2, y2;
u32 nlines;
u32 lines_capacity;
@@ -70,6 +71,7 @@ void buffer_create(TextBuffer *buffer, Font *font) {
util_zero_memory(buffer, sizeof *buffer);
buffer->font = font;
buffer->tab_width = 4;
+ buffer->store_undo_events = true;
}
// this is a macro so we get -Wformat warnings
@@ -90,8 +92,23 @@ static void buffer_out_of_mem(TextBuffer *buffer) {
buffer_seterr(buffer, "Out of memory.");
}
+
+static void buffer_edit_free(BufferEdit *edit) {
+ free(edit->text);
+}
+
+static void buffer_clear_redo_history(TextBuffer *buffer) {
+ arr_foreach_ptr(buffer->redo_history, BufferEdit, edit) {
+ buffer_edit_free(edit);
+ }
+ arr_clear(buffer->redo_history);
+}
+
// add this edit to the undo history
static void buffer_append_edit(TextBuffer *buffer, BufferEdit const *edit) {
+ // whenever an edit is made, clear the redo history
+ buffer_clear_redo_history(buffer);
+
arr_add(buffer->undo_history, *edit);
if (!buffer->undo_history) buffer_out_of_mem(buffer);
}
@@ -189,16 +206,20 @@ static Status buffer_create_edit_insert_text(TextBuffer *buffer, BufferEdit *edi
}
static void buffer_edit_delete_text(TextBuffer *buffer, BufferPos start, size_t len) {
- BufferEdit edit = {0};
- if (buffer_create_edit_delete_text(buffer, &edit, start, len)) {
- buffer_append_edit(buffer, &edit);
+ if (buffer->store_undo_events) {
+ BufferEdit edit = {0};
+ if (buffer_create_edit_delete_text(buffer, &edit, start, len)) {
+ buffer_append_edit(buffer, &edit);
+ }
}
}
static void buffer_edit_insert_text(TextBuffer *buffer, BufferPos start, size_t len) {
- BufferEdit edit = {0};
- if (buffer_create_edit_insert_text(buffer, &edit, start, len)) {
- buffer_append_edit(buffer, &edit);
+ if (buffer->store_undo_events) {
+ BufferEdit edit = {0};
+ if (buffer_create_edit_insert_text(buffer, &edit, start, len)) {
+ buffer_append_edit(buffer, &edit);
+ }
}
}
@@ -1123,25 +1144,35 @@ void buffer_backspace_words_at_cursor(TextBuffer *buffer, i64 nwords) {
// returns the inverse edit
static Status buffer_undo_edit(TextBuffer *buffer, BufferEdit const *edit, BufferEdit *inverse) {
+ bool success = false;
+ bool prev_store_undo_events = buffer->store_undo_events;
+ // temporarily disable saving of undo events so we don't add the inverse edit
+ // to the undo history
+ buffer->store_undo_events = false;
+
switch (edit->type) {
case BUFFER_EDIT_INSERT_TEXT:
// the inverse edit is the deletion of this text
- if (!buffer_create_edit_delete_text(buffer, inverse, edit->pos, edit->text_len))
- return false;
- // delete the text
- buffer_delete_chars_at_pos(buffer, edit->pos, (i64)edit->text_len);
+ if (buffer_create_edit_delete_text(buffer, inverse, edit->pos, edit->text_len)) {
+ // delete the text
+ buffer_delete_chars_at_pos(buffer, edit->pos, (i64)edit->text_len);
+
+ success = true;
+ }
break;
case BUFFER_EDIT_DELETE_TEXT: {
// the inverse edit is the insertion of this text
- if (!buffer_create_edit_insert_text(buffer, inverse, edit->pos, edit->text_len))
- return false;
- // insert the text
- String32 str = {edit->text_len, edit->text};
- buffer_insert_text_at_pos(buffer, edit->pos, str);
+ if (buffer_create_edit_insert_text(buffer, inverse, edit->pos, edit->text_len)) {
+ // insert the text
+ String32 str = {edit->text_len, edit->text};
+ buffer_insert_text_at_pos(buffer, edit->pos, str);
+
+ success = true;
+ }
} break;
}
-
- return true;
+ buffer->store_undo_events = prev_store_undo_events;
+ return success;
}
void buffer_undo(TextBuffer *buffer, i64 ntimes) {
@@ -1150,7 +1181,11 @@ void buffer_undo(TextBuffer *buffer, i64 ntimes) {
if (edit) {
BufferEdit inverse = {0};
if (buffer_undo_edit(buffer, edit, &inverse)) {
+ buffer->cursor_pos = edit->pos; // put cursor where edit happened
+ buffer_scroll_to_cursor(buffer);
+
buffer_append_redo(buffer, &inverse);
+ buffer_edit_free(edit);
arr_remove_last(buffer->undo_history);
}
}
@@ -1163,7 +1198,14 @@ void buffer_redo(TextBuffer *buffer, i64 ntimes) {
if (edit) {
BufferEdit inverse = {0};
if (buffer_undo_edit(buffer, edit, &inverse)) {
- buffer_append_edit(buffer, &inverse);
+ buffer->cursor_pos = edit->pos; // put cursor where edit happened
+ buffer_scroll_to_cursor(buffer);
+
+ // NOTE: we can't just use buffer_append_edit, because that clears the redo history
+ arr_add(buffer->undo_history, inverse);
+ if (!buffer->undo_history) buffer_out_of_mem(buffer);
+
+ buffer_edit_free(edit);
arr_remove_last(buffer->redo_history);
}
}
diff --git a/main.c b/main.c
index 31dd849..0a7b420 100644
--- a/main.c
+++ b/main.c
@@ -146,9 +146,9 @@ int main(void) {
case SDLK_z:
if (ctrl) {
if (shift) {
- buffer_undo(&text_buffer, 1);
- } else {
buffer_redo(&text_buffer, 1);
+ } else {
+ buffer_undo(&text_buffer, 1);
}
}
break;