From 5bffb50207f8d8ba873f4c40dde4859179153001 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Wed, 10 Feb 2021 10:55:51 -0500 Subject: chain replace all into one undo event --- buffer.c | 40 ++++++++++++++++++++++++++++++++++++---- find.c | 2 ++ main.c | 3 +-- ted.h | 3 +++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/buffer.c b/buffer.c index 7cb1208..640e64c 100644 --- a/buffer.c +++ b/buffer.c @@ -446,6 +446,8 @@ static void buffer_print_undo_history(TextBuffer *buffer) { static void buffer_edit(TextBuffer *buffer, BufferPos start, u32 prev_len, u32 new_len) { BufferEdit edit = {0}; if (buffer_edit_create(buffer, &edit, start, prev_len, new_len)) { + edit.chain = buffer->chaining_edits; + if (buffer->will_chain_edits) buffer->chaining_edits = true; buffer_append_edit(buffer, &edit); } } @@ -489,10 +491,11 @@ static bool buffer_edit_does_anything(TextBuffer *buffer, BufferEdit *edit) { // has enough time passed since the last edit that we should create a new one? static bool buffer_edit_split(TextBuffer *buffer) { - double curr_time = time_get_seconds(); - double undo_time_cutoff = buffer_settings(buffer)->undo_save_time; // only keep around edits for this long (in seconds). BufferEdit *last_edit = arr_lastp(buffer->undo_history); if (!last_edit) return true; + if (buffer->chaining_edits) return false; + double curr_time = time_get_seconds(); + double undo_time_cutoff = buffer_settings(buffer)->undo_save_time; // only keep around edits for this long (in seconds). return curr_time - last_edit->time > undo_time_cutoff; } @@ -1632,12 +1635,18 @@ static void buffer_cursor_to_edit(TextBuffer *buffer, BufferEdit *edit) { buffer_center_cursor(buffer); // whenever we undo an edit, put the cursor in the center, to make it clear where the undo happened } +// a <-b <-c +// c <-b <-a + void buffer_undo(TextBuffer *buffer, i64 ntimes) { + bool chain_next = false; for (i64 i = 0; i < ntimes; ++i) { BufferEdit *edit = arr_lastp(buffer->undo_history); if (edit) { + bool chain = edit->chain; BufferEdit inverse = {0}; if (buffer_undo_edit(buffer, edit, &inverse)) { + inverse.chain = chain_next; if (i == ntimes - 1) { // if we're on the last undo, put cursor where edit is buffer_cursor_to_edit(buffer, edit); @@ -1647,16 +1656,21 @@ void buffer_undo(TextBuffer *buffer, i64 ntimes) { buffer_edit_free(edit); arr_remove_last(buffer->undo_history); } - } + if (chain) --i; + chain_next = chain; + } else break; } } void buffer_redo(TextBuffer *buffer, i64 ntimes) { + bool chain_next = false; for (i64 i = 0; i < ntimes; ++i) { BufferEdit *edit = arr_lastp(buffer->redo_history); if (edit) { + bool chain = edit->chain; BufferEdit inverse = {0}; if (buffer_undo_edit(buffer, edit, &inverse)) { + inverse.chain = chain_next; if (i == ntimes - 1) buffer_cursor_to_edit(buffer, edit); @@ -1667,7 +1681,9 @@ void buffer_redo(TextBuffer *buffer, i64 ntimes) { buffer_edit_free(edit); arr_remove_last(buffer->redo_history); } - } + if (chain) --i; + chain_next = chain; + } else break; } } @@ -2176,3 +2192,19 @@ void buffer_render(TextBuffer *buffer, Rect r) { } } } + +// if you do: +// buffer_start_edit_chain(buffer) +// buffer_insert_text_at_pos(buffer, some position, "text1") +// buffer_insert_text_at_pos(buffer, another position, "text2") +// buffer_end_edit_chain(buffer) +// pressing ctrl+z will undo both the insertion of text1 and text2. +static void buffer_start_edit_chain(TextBuffer *buffer) { + assert(!buffer->chaining_edits); + assert(!buffer->will_chain_edits); + buffer->will_chain_edits = true; +} + +static void buffer_end_edit_chain(TextBuffer *buffer) { + buffer->chaining_edits = buffer->will_chain_edits = false; +} diff --git a/find.c b/find.c index 48a9299..805210a 100644 --- a/find.c +++ b/find.c @@ -272,11 +272,13 @@ static void find_replace_all(Ted *ted) { FindResult *last_result = arr_lastp(ted->find_results); buffer_cursor_move_to_pos(buffer, last_result->start); } + buffer_start_edit_chain(buffer); // NOTE: we don't need to increment i because the matches will be removed from the find_results array. for (u32 i = match_idx; i < arr_len(ted->find_results); ) { if (!find_replace_match(ted, i)) break; } + buffer_end_edit_chain(buffer); find_update(ted, true); } } diff --git a/main.c b/main.c index 4036a1f..790c461 100644 --- a/main.c +++ b/main.c @@ -1,7 +1,6 @@ // @TODO: -// - chained undos (replace all = one undo action) -// - highlight matching parentheses // - indent/dedent region +// - highlight matching parentheses // - split // - completion // - view-only diff --git a/ted.h b/ted.h index b1d5554..60cb06d 100644 --- a/ted.h +++ b/ted.h @@ -114,6 +114,7 @@ typedef struct { // this refers to replacing prev_len characters (found in prev_text) at pos with new_len characters typedef struct BufferEdit { + bool chain; // should this + the next edit be treated as one? BufferPos pos; u32 new_len; u32 prev_len; @@ -134,6 +135,8 @@ typedef struct { // This is set to true whenever a change is made to the buffer, and never set to false by buffer_ functions. // (Distinct from buffer_unsaved_changes) bool modified; + bool will_chain_edits; + bool chaining_edits; // are we chaining undo events together? float x1, y1, x2, y2; u32 nlines; u32 lines_capacity; -- cgit v1.2.3