diff options
Diffstat (limited to 'src/undo.c')
-rw-r--r-- | src/undo.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/src/undo.c b/src/undo.c new file mode 100644 index 0000000..10e438f --- /dev/null +++ b/src/undo.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2012-2021 Robin Haberkorn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "sciteco.h" +#include "cmdline.h" +#include "undo.h" + +//#define DEBUG + +TECO_DEFINE_UNDO_SCALAR(gchar); +TECO_DEFINE_UNDO_SCALAR(gint); +TECO_DEFINE_UNDO_SCALAR(guint); +TECO_DEFINE_UNDO_SCALAR(gsize); +TECO_DEFINE_UNDO_SCALAR(teco_int_t); +TECO_DEFINE_UNDO_SCALAR(gboolean); +TECO_DEFINE_UNDO_SCALAR(gconstpointer); + +/** + * An undo token. + * + * Undo tokens are generated to revert any + * changes to the editor state, ie. they + * define an action to take upon rubout. + * + * Undo tokens are organized into an undo stack. + */ +typedef struct teco_undo_token_t { + struct teco_undo_token_t *next; + teco_undo_action_t action_cb; + guint8 user_data[]; +} teco_undo_token_t; + +/** + * Stack of teco_undo_token_t lists. + * + * Each stack element represents + * a command line character (the undo tokens + * generated by that character), so it's OK + * to use a data structure that may need + * reallocation but is space efficient. + * This data structure allows us to omit the + * command line program counter from teco_undo_token_t + * but wastes a few bytes for input characters + * that produce no undo tokens (e.g. NOPs like space). + */ +static GPtrArray *teco_undo_heads; + +gboolean teco_undo_enabled = FALSE; + +static void __attribute__((constructor)) +teco_undo_init(void) +{ + teco_undo_heads = g_ptr_array_new(); +} + +/** + * Allocate and push undo token. + * + * This does nothing if undo is disabled and should + * not be used when ownership of some data is to be + * passed to the undo token. + */ +gpointer +teco_undo_push_size(teco_undo_action_t action_cb, gsize size) +{ + if (!teco_undo_enabled) + return NULL; + + teco_undo_token_t *token = g_malloc(sizeof(teco_undo_token_t) + size); + token->action_cb = action_cb; + +#ifdef DEBUG + g_printf("UNDO PUSH %p\n", token); +#endif + + /* + * There can very well be 0 undo tokens + * per input character (e.g. NOPs like space). + */ + while (teco_undo_heads->len <= teco_cmdline.pc) + g_ptr_array_add(teco_undo_heads, NULL); + g_assert(teco_undo_heads->len == teco_cmdline.pc+1); + + token->next = g_ptr_array_index(teco_undo_heads, + teco_undo_heads->len-1); + g_ptr_array_index(teco_undo_heads, teco_undo_heads->len-1) = token; + + return token->user_data; +} + +void +teco_undo_pop(gint pc) +{ + while ((gint)teco_undo_heads->len > pc) { + teco_undo_token_t *top = + g_ptr_array_remove_index(teco_undo_heads, + teco_undo_heads->len-1); + + while (top) { + teco_undo_token_t *next = top->next; + +#ifdef DEBUG + g_printf("UNDO POP %p\n", top); + fflush(stdout); +#endif + top->action_cb(top->user_data, TRUE); + + g_free(top); + top = next; + } + } +} + +void +teco_undo_clear(void) +{ + while (teco_undo_heads->len) { + teco_undo_token_t *top = + g_ptr_array_remove_index(teco_undo_heads, + teco_undo_heads->len-1); + + while (top) { + teco_undo_token_t *next = top->next; + top->action_cb(top->user_data, FALSE); + g_free(top); + top = next; + } + } +} + +/* + * NOTE: This destructor should always be run, even with NDEBUG, + * as there are undo tokens that release more than memory (e.g. files). + */ +static void __attribute__((destructor)) +teco_undo_cleanup(void) +{ + teco_undo_clear(); + g_ptr_array_free(teco_undo_heads, TRUE); +} |