aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/undo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/undo.c')
-rw-r--r--src/undo.c163
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);
+}