241 lines
3.2 KiB
C
241 lines
3.2 KiB
C
/*
|
|
* undo.c: ke's undo system
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "abuf.h"
|
|
#include "buffer.h"
|
|
#include "undo.h"
|
|
|
|
|
|
undo_node *
|
|
undo_node_new(undo_kind kind)
|
|
{
|
|
undo_node *node = NULL;
|
|
|
|
node = (undo_node *)malloc(sizeof(undo_node));
|
|
assert(node != NULL);
|
|
|
|
node->kind = kind;
|
|
node->row = node->col = 0;
|
|
|
|
ab_init(&node->text);
|
|
|
|
node->next = NULL;
|
|
node->next = NULL;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
void
|
|
undo_node_free(undo_node *node)
|
|
{
|
|
if (node == NULL) {
|
|
return;
|
|
}
|
|
|
|
ab_free(&node->text);
|
|
}
|
|
|
|
|
|
void
|
|
undo_node_free_all(undo_node *node)
|
|
{
|
|
undo_node *next = NULL;
|
|
|
|
if (node == NULL) {
|
|
return;
|
|
}
|
|
|
|
while (node != NULL) {
|
|
next = node->next;
|
|
undo_node_free(node);
|
|
free(node);
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
undo_tree_init(undo_tree *tree)
|
|
{
|
|
assert(tree != NULL);
|
|
|
|
tree->root = NULL;
|
|
tree->current = NULL;
|
|
tree->pending = NULL;
|
|
}
|
|
|
|
|
|
void
|
|
undo_tree_free(undo_tree *tree)
|
|
{
|
|
assert(tree != NULL);
|
|
|
|
undo_node_free(tree->pending);
|
|
undo_node_free_all(tree->root);
|
|
undo_tree_init(tree);
|
|
}
|
|
|
|
|
|
void
|
|
undo_begin(undo_tree *tree, undo_kind kind)
|
|
{
|
|
undo_node *pending = NULL;
|
|
|
|
if (tree->pending != NULL) {
|
|
if (tree->pending->kind == kind) {
|
|
/* don't initiate a new undo sequence if it's the same kind */
|
|
return;
|
|
}
|
|
undo_commit(tree);
|
|
}
|
|
|
|
pending = undo_node_new(kind);
|
|
assert(pending != NULL);
|
|
|
|
tree->pending = pending;
|
|
}
|
|
|
|
|
|
void
|
|
undo_prepend(undo_tree *tree, abuf *buf)
|
|
{
|
|
assert(tree != NULL);
|
|
assert(tree->pending != NULL);
|
|
|
|
ab_prepend_ab(&tree->pending->text, buf);
|
|
}
|
|
|
|
|
|
void
|
|
undo_append(undo_tree *tree, abuf *buf)
|
|
{
|
|
assert(tree != NULL);
|
|
assert(tree->pending != NULL);
|
|
|
|
ab_append_ab(&tree->pending->text, buf);
|
|
}
|
|
|
|
|
|
void
|
|
undo_prependch(undo_tree *tree, char c)
|
|
{
|
|
assert(tree != NULL);
|
|
assert(tree->pending != NULL);
|
|
|
|
ab_prependch(&tree->pending->text, c);
|
|
}
|
|
|
|
|
|
void
|
|
undo_appendch(undo_tree *tree, char c)
|
|
{
|
|
assert(tree != NULL);
|
|
assert(tree->pending != NULL);
|
|
|
|
ab_appendch(&tree->pending->text, c);
|
|
}
|
|
|
|
|
|
void
|
|
undo_commit(undo_tree *tree)
|
|
{
|
|
assert(tree != NULL);
|
|
|
|
if (tree->pending == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (tree->root == NULL) {
|
|
assert(tree->current == NULL);
|
|
|
|
tree->root = tree->pending;
|
|
tree->current = tree->pending;
|
|
tree->pending = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
assert(tree->current != NULL);
|
|
if (tree->current->next != NULL) {
|
|
undo_node_free_all(tree->current->next);
|
|
}
|
|
|
|
tree->pending->prev = tree->current;
|
|
tree->current->next = tree->pending;
|
|
tree->current = tree->pending;
|
|
tree->pending = NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
undo_apply_undo(struct buffer *buf)
|
|
{
|
|
undo_node *node = buf->undo.current;
|
|
|
|
switch (node->kind) {
|
|
case UNDO_INSERT:
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
undo_apply(struct buffer *buf, int direction)
|
|
{
|
|
(void)direction;
|
|
|
|
if (buf == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
undo_commit(&buf->undo);
|
|
if (buf->undo.current == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (direction == UNDO_DIR_UNDO) {
|
|
return undo_apply_undo(buf);
|
|
} else if (direction == UNDO_DIR_UNDO) {
|
|
return 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
editor_undo(struct buffer *buf)
|
|
{
|
|
if (buf == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
undo_commit(&buf->undo);
|
|
return undo_apply(buf, UNDO_DIR_UNDO);
|
|
|
|
}
|
|
|
|
|
|
int
|
|
editor_redo(struct buffer *buf)
|
|
{
|
|
if (buf == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
undo_commit(&buf->undo);
|
|
return undo_apply(buf, UNDO_DIR_REDO);
|
|
}
|
|
|