From 12cce30fad0a685d333cc23c7c71fded4b6cf329 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Mon, 21 Dec 2020 14:11:28 -0500 Subject: undo working, just need to chain events (rather than undoing one character insertion/deletion at a time) --- arr.c | 18 +++++++++++++-- buffer.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++--------------- main.c | 4 ++-- 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; -- cgit v1.2.3