Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe4f8df0ab | |||
| bbd682cec7 | |||
| ace25c5c65 |
1
Makefile
1
Makefile
@@ -6,6 +6,7 @@ CFLAGS := -Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g
|
||||
CFLAGS += -Wno-unused-result
|
||||
CFLAGS += -D_DEFAULT_SOURCE -D_XOPEN_SOURCE
|
||||
CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||
CFLAGS += -fprofile-instr-generate -fcoverage-mapping
|
||||
|
||||
LDFLAGS := -fsanitize=address
|
||||
|
||||
|
||||
526
main.c
526
main.c
@@ -88,17 +88,17 @@ struct abuf {
|
||||
|
||||
/* editor row */
|
||||
struct erow {
|
||||
char *line;
|
||||
char *render;
|
||||
char *line;
|
||||
char *render;
|
||||
|
||||
int size;
|
||||
int rsize;
|
||||
int size;
|
||||
int rsize;
|
||||
|
||||
int cap;
|
||||
int cap;
|
||||
};
|
||||
|
||||
|
||||
typedef enum undo_flag {
|
||||
typedef enum undo_type {
|
||||
UNDO_INSERT = 1 << 0, /* insertch */
|
||||
UNDO_DELETE = 1 << 1, /* deletech */
|
||||
UNDO_PASTE = 1 << 2, /* yank */
|
||||
@@ -115,7 +115,7 @@ typedef struct undo_node {
|
||||
int row, col;
|
||||
struct abuf text;
|
||||
struct undo_node *next;
|
||||
struct undo_node *child;
|
||||
struct undo_node *parent;
|
||||
} undo_node_t;
|
||||
|
||||
|
||||
@@ -187,6 +187,8 @@ size_t kstrnlen(const char *buf, const size_t max);
|
||||
void ab_init(struct abuf *buf);
|
||||
void ab_appendch(struct abuf *buf, char c);
|
||||
void ab_append(struct abuf *buf, const char *s, size_t len);
|
||||
void ab_prependch(struct abuf *buf, char c);
|
||||
void ab_prepend(struct abuf *buf, const char *s, size_t len);
|
||||
void ab_free(struct abuf *buf);
|
||||
char nibble_to_hex(char c);
|
||||
void swap_int(int *a, int *b);
|
||||
@@ -242,24 +244,17 @@ void erow_free(struct erow *row);
|
||||
*/
|
||||
undo_node_t *undo_node_new(undo_flag_t type);
|
||||
void undo_node_free(undo_node_t *node);
|
||||
void undo_node_free_branch(undo_node_t *node);
|
||||
undo_tree_t *undo_tree_new(void);
|
||||
void undo_tree_free(undo_tree_t *ut);
|
||||
int undo_continue_pending(undo_flag_t type);
|
||||
void undo_tree_free(undo_tree_t *tree);
|
||||
void undo_begin(undo_flag_t type);
|
||||
void undo_append_char(char c);
|
||||
void undo_append(const char *data, size_t len);
|
||||
void undo_prepend_char(char c);
|
||||
void undo_prepend(const char *data, size_t len);
|
||||
void undo_commit(void);
|
||||
void undo_discard_redo_branches(struct undo_node *from);
|
||||
undo_node_t *undo_parent_of(undo_node_t *node);
|
||||
void undo_apply(const undo_node_t *node, int direction);
|
||||
// void editor_undo(void);
|
||||
// void editor_redo(void);
|
||||
// void undo_mark_saved(void);
|
||||
// int undo_depth(const undo_tree_t *t);
|
||||
// int undo_can_undo(const undo_tree_t *t);
|
||||
// int undo_can_redo(const undo_tree_t *t);
|
||||
// void undo_tree_debug_dump(const undo_tree_t *t);
|
||||
void editor_undo(void);
|
||||
void editor_redo(void);
|
||||
|
||||
/* kill ring, marking, etc... */
|
||||
void killring_flush(void);
|
||||
@@ -284,7 +279,10 @@ int cursor_at_eol(void);
|
||||
void delete_row(int at);
|
||||
void row_append_row(struct erow *row, char *s, int len);
|
||||
void row_insert_ch(struct erow *row, int at, int16_t c);
|
||||
void row_insert_block(struct erow *row, int at,
|
||||
const char *data, size_t len);
|
||||
void row_delete_ch(struct erow *row, int at);
|
||||
void row_delete_block(struct erow *row, int at, size_t len);
|
||||
void insertch(int16_t c);
|
||||
void deletech(uint8_t op);
|
||||
void open_file(const char *filename);
|
||||
@@ -472,6 +470,7 @@ reset_editor(void)
|
||||
erow_free(&editor.row[i]);
|
||||
}
|
||||
free(editor.row);
|
||||
editor.row = NULL;
|
||||
|
||||
if (editor.filename != NULL) {
|
||||
free(editor.filename);
|
||||
@@ -527,6 +526,27 @@ ab_append(struct abuf *buf, const char *s, size_t len)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ab_prependch(struct abuf *buf, const char c)
|
||||
{
|
||||
ab_prepend(buf, &c, 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ab_prepend(struct abuf *buf, const char *s, const size_t len)
|
||||
{
|
||||
char *nc = realloc(buf->b, buf->len + len);
|
||||
assert(nc != NULL);
|
||||
|
||||
memmove(nc + len, nc, buf->len);
|
||||
memcpy(nc, s, len);
|
||||
|
||||
buf->b = nc;
|
||||
buf->len += len;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ab_free(struct abuf *buf)
|
||||
{
|
||||
@@ -772,18 +792,20 @@ erow_free(struct erow *row)
|
||||
free(row->line);
|
||||
row->render = NULL;
|
||||
row->line = NULL;
|
||||
row->cap = 0;
|
||||
row->size = 0;
|
||||
}
|
||||
|
||||
|
||||
undo_node_t *
|
||||
undo_node_new(undo_flag_t type)
|
||||
undo_node_new(const undo_flag_t type)
|
||||
{
|
||||
undo_node_t *node = calloc1(sizeof(undo_node_t));
|
||||
|
||||
node->type = type;
|
||||
node->row = node->col = 0;
|
||||
node->next = NULL;
|
||||
node->child = NULL;
|
||||
node->parent = NULL;
|
||||
|
||||
ab_init(&node->text);
|
||||
|
||||
@@ -798,32 +820,16 @@ undo_node_free(undo_node_t *node)
|
||||
return;
|
||||
}
|
||||
|
||||
assert(node->next->parent == node);
|
||||
|
||||
ab_free(&node->text);
|
||||
undo_node_free(node->child);
|
||||
undo_node_free(node->next);
|
||||
node = node->next;
|
||||
free(node->parent);
|
||||
node->parent = NULL;
|
||||
free(node);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
undo_node_free_branch(undo_node_t *node)
|
||||
{
|
||||
undo_node_t *next = NULL;
|
||||
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (node != NULL) {
|
||||
next = node->next;
|
||||
undo_node_free(node->child);
|
||||
ab_free(&node->text);
|
||||
free(node);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
undo_tree_t *
|
||||
undo_tree_new(void)
|
||||
{
|
||||
@@ -840,52 +846,32 @@ undo_tree_new(void)
|
||||
|
||||
|
||||
void
|
||||
undo_tree_free(undo_tree_t *ut)
|
||||
undo_tree_free(undo_tree_t *tree)
|
||||
{
|
||||
if (ut == NULL) {
|
||||
if (tree == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
undo_node_free(ut->root);
|
||||
undo_node_free(ut->pending);
|
||||
undo_node_free(tree->root);
|
||||
|
||||
if (tree->pending != NULL) {
|
||||
undo_node_free(tree->pending);
|
||||
}
|
||||
|
||||
if (debug_log == NULL) {
|
||||
ut->root = NULL;
|
||||
ut->current = NULL;
|
||||
ut->saved = NULL;
|
||||
ut->pending = NULL;
|
||||
tree->root = NULL;
|
||||
tree->current = NULL;
|
||||
tree->saved = NULL;
|
||||
tree->pending = NULL;
|
||||
} else {
|
||||
ut->root =
|
||||
ut->current =
|
||||
ut->saved =
|
||||
ut->pending =
|
||||
tree->root =
|
||||
tree->current =
|
||||
tree->saved =
|
||||
tree->pending =
|
||||
(void *)0xDEADBEEF;
|
||||
}
|
||||
|
||||
free(ut);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
undo_continue_pending(undo_flag_t type)
|
||||
{
|
||||
undo_tree_t *tree = editor.undo;
|
||||
undo_node_t *node = tree->pending;
|
||||
|
||||
/* no pending node, so we need to start anew. */
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->type != type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->row != editor.cury || node->col != editor.curx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
free(tree);
|
||||
}
|
||||
|
||||
|
||||
@@ -894,26 +880,22 @@ undo_continue_pending(undo_flag_t type)
|
||||
* if a new pending sequence doesn't need to be created.
|
||||
*/
|
||||
void
|
||||
undo_begin(const undo_flag_t type)
|
||||
undo_begin(undo_flag_t type)
|
||||
{
|
||||
undo_tree_t *tree = editor.undo;
|
||||
undo_node_t *node = tree->pending;
|
||||
struct undo_tree *tree = editor.undo;
|
||||
|
||||
if (undo_continue_pending(type)) {
|
||||
return;
|
||||
}
|
||||
assert(tree != NULL);
|
||||
|
||||
if (tree->pending != NULL) {
|
||||
if (tree->pending->type == type) {
|
||||
return;
|
||||
}
|
||||
undo_commit();
|
||||
}
|
||||
|
||||
node = undo_node_new(type);
|
||||
assert(node != NULL);
|
||||
|
||||
node->type = type;
|
||||
node->row = editor.cury;
|
||||
node->col = editor.curx;
|
||||
tree->pending = node;
|
||||
tree->pending = undo_node_new(type);
|
||||
tree->pending->row = editor.cury;
|
||||
tree->pending->col = editor.curx;
|
||||
}
|
||||
|
||||
|
||||
@@ -939,199 +921,112 @@ undo_append(const char *data, const size_t len)
|
||||
}
|
||||
|
||||
|
||||
/* Finish the current batch and link it into the tree */
|
||||
void
|
||||
undo_prepend_char(const char c)
|
||||
{
|
||||
undo_node_t *node = editor.undo->pending;
|
||||
|
||||
assert(node != NULL);
|
||||
|
||||
ab_prependch(&node->text, c);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
undo_prepend(const char *data, const size_t len)
|
||||
{
|
||||
undo_node_t *node = editor.undo->pending;
|
||||
|
||||
assert(node != NULL);
|
||||
|
||||
ab_prepend(&node->text, data, len);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* undo_commit commits the current pending tree into the root.
|
||||
*
|
||||
* Finish the current batch and link it into the tree.
|
||||
* */
|
||||
void
|
||||
undo_commit(void)
|
||||
{
|
||||
undo_tree_t *tree = editor.undo;
|
||||
undo_node_t *node = NULL;
|
||||
|
||||
assert(tree != NULL);
|
||||
|
||||
if (tree->pending == NULL) {
|
||||
return; /* nothing to commit */
|
||||
return; /* no pending operation */
|
||||
}
|
||||
|
||||
node = tree->pending;
|
||||
tree->pending = NULL;
|
||||
|
||||
if (tree->current && tree->current->child) {
|
||||
undo_node_free_branch(tree->current->child);
|
||||
tree->current->child = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (tree->root == NULL) {
|
||||
/* First edit ever */
|
||||
tree->root = node;
|
||||
tree->current = node;
|
||||
} else if (tree->current == NULL) {
|
||||
/* this shouldn't happen, so throw an
|
||||
* assert to catch it.
|
||||
*/
|
||||
assert(tree->current != NULL);
|
||||
tree->root = tree->current = node;
|
||||
} else {
|
||||
tree->current->child = node;
|
||||
tree->current = node;
|
||||
|
||||
}
|
||||
|
||||
if (tree->saved && tree->current != tree->saved) {
|
||||
tree->saved = NULL;
|
||||
}
|
||||
|
||||
editor.dirty = 1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
undo_discard_redo_branches(struct undo_node *from)
|
||||
{
|
||||
undo_node_free(from->child);
|
||||
from->child = NULL;
|
||||
|
||||
undo_node_free(from->next);
|
||||
from->next = NULL;
|
||||
}
|
||||
|
||||
|
||||
undo_node_t *
|
||||
undo_parent_of(undo_node_t *node)
|
||||
{
|
||||
undo_tree_t *tree = editor.undo;
|
||||
undo_node_t *parent = tree->root;
|
||||
|
||||
if (tree->root == node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parent = tree->root;
|
||||
while (parent != NULL && parent->child != node) {
|
||||
parent = parent->child;
|
||||
}
|
||||
|
||||
if (parent == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
editor_undo(void)
|
||||
{
|
||||
undo_tree_t *tree = editor.undo;
|
||||
undo_node_t *node = tree->current;
|
||||
undo_node_t *parent = NULL;
|
||||
|
||||
if (node == NULL || node == tree->root) {
|
||||
editor_set_status("Nothing to undo.");
|
||||
return;
|
||||
}
|
||||
|
||||
parent = undo_parent_of(node);
|
||||
assert(parent != NULL);
|
||||
|
||||
undo_apply(node, -1);
|
||||
tree->current = parent;
|
||||
|
||||
display_refresh();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
editor_redo(void)
|
||||
{
|
||||
undo_tree_t *tree = editor.undo;
|
||||
undo_node_t *node = tree->current;
|
||||
|
||||
if (node == NULL || node->child == NULL) {
|
||||
editor_set_status("Nothing to redo.");
|
||||
return;
|
||||
}
|
||||
|
||||
tree->current = node->child;
|
||||
undo_apply(node->child, 1);
|
||||
|
||||
display_refresh();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
undo_apply(const struct undo_node *node, const int direction)
|
||||
{
|
||||
int row = node->row;
|
||||
int col = node->col;
|
||||
const char *data = node->text.b;
|
||||
size_t len = node->text.len;
|
||||
int redo;
|
||||
|
||||
jump_to_position(col, row);
|
||||
assert(node != NULL);
|
||||
redo = direction < 0 ? 1 : 0;
|
||||
|
||||
switch (node->type) {
|
||||
case UNDO_PASTE:
|
||||
case UNDO_INSERT:
|
||||
if (direction > 0) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
insertch((unsigned char)data[i]);
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
deletech(KILLRING_NO_OP);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UNDO_DELETE:
|
||||
if (direction > 0) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
deletech(KILLRING_NO_OP);
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
insertch((unsigned char)data[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UNDO_NEWLINE:
|
||||
if (direction > 0) {
|
||||
newline();
|
||||
} else {
|
||||
if (editor.cury > 0) {
|
||||
editor.curx = editor.row[editor.cury - 1].size;
|
||||
row_append_row(&editor.row[editor.cury - 1],
|
||||
editor.row[editor.cury].line,
|
||||
editor.row[editor.cury].size);
|
||||
delete_row(editor.cury);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UNDO_DELETE_ROW:
|
||||
if (direction > 0) {
|
||||
/* redo = delete the whole row again */
|
||||
delete_row(editor.cury);
|
||||
} else {
|
||||
/* undo = re-insert the saved row (including final '\n') */
|
||||
if (len > 0 && data[len - 1] == '\n') len--;
|
||||
erow_insert(editor.cury, (char*)data, len);
|
||||
/* cursor goes to start of re-inserted row */
|
||||
editor.curx = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case UNDO_INDENT:
|
||||
case UNDO_KILL_REGION:
|
||||
/* These are more complex – you can implement later */
|
||||
/* For now just move cursor and say "not implemented yet" */
|
||||
editor_set_status("Undo of %s not implemented yet",
|
||||
direction > 0 ? "redo" : "complex op");
|
||||
break;
|
||||
default:
|
||||
editor_set_status("Unknown undo type: %d", node->type);
|
||||
break;
|
||||
if (redo) {
|
||||
editor_set_status("Redo isn't implemented.");
|
||||
} else {
|
||||
editor_set_status("Undo isn't implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
undo_apply_current(int direction)
|
||||
{
|
||||
undo_tree_t *tree = editor.undo;
|
||||
|
||||
assert(tree != NULL);
|
||||
undo_commit();
|
||||
|
||||
if (tree->current == NULL) {
|
||||
editor_set_status("No further undo.");
|
||||
return;
|
||||
}
|
||||
|
||||
editor.dirty = 1;
|
||||
assert(tree->current->next == NULL);
|
||||
undo_apply(tree->current, direction);
|
||||
|
||||
tree->current = tree->current->parent;
|
||||
undo_node_free(tree->current->next);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
editor_undo(void)
|
||||
{
|
||||
/*
|
||||
* pending should already have been committed, but it doesn't
|
||||
* hurt to be a little paranoid.
|
||||
*/
|
||||
undo_commit();
|
||||
|
||||
undo_apply_current(1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
editor_redo(void)
|
||||
{
|
||||
/*
|
||||
* pending should already have been committed, but it doesn't
|
||||
* hurt to be a little paranoid.
|
||||
*/
|
||||
undo_commit();
|
||||
|
||||
undo_apply_current(-1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1165,6 +1060,10 @@ killring_yank(void)
|
||||
insertch(ch);
|
||||
}
|
||||
}
|
||||
|
||||
undo_begin(UNDO_PASTE);
|
||||
undo_append(editor.killring->line, editor.killring->size);
|
||||
undo_commit(); /* atomic */
|
||||
}
|
||||
|
||||
|
||||
@@ -1793,6 +1692,38 @@ row_insert_ch(struct erow *row, int at, int16_t c)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
row_insert_block(struct erow *row, int at, const char *data, size_t len)
|
||||
{
|
||||
if (at < 0) {
|
||||
at = 0;
|
||||
}
|
||||
|
||||
if (at > row->size) {
|
||||
at = row->size;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (row->size + (int)len + 1 > row->cap) {
|
||||
row->cap = cap_growth(row->cap, row->size + len + 1);
|
||||
row->line = realloc(row->line, row->cap);
|
||||
assert(row->line != NULL);
|
||||
}
|
||||
|
||||
memmove(row->line + at + len, row->line + at,
|
||||
row->size - at + 1); /* +1 for NUL */
|
||||
memcpy(row->line + at, data, len);
|
||||
row->size += len;
|
||||
row->line[row->size] = '\0';
|
||||
|
||||
erow_update(row);
|
||||
editor.dirty++;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
row_delete_ch(struct erow *row, int at)
|
||||
{
|
||||
@@ -1807,9 +1738,32 @@ row_delete_ch(struct erow *row, int at)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
row_delete_block(struct erow *row, int at, size_t len)
|
||||
{
|
||||
if (at < 0 || at >= row->size || len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (at + (int)len > row->size) {
|
||||
len = row->size - at;
|
||||
}
|
||||
|
||||
memmove(row->line + at, row->line + at + len,
|
||||
row->size - (at + len) + 1);
|
||||
row->size -= len;
|
||||
row->line[row->size] = '\0';
|
||||
|
||||
erow_update(row);
|
||||
editor.dirty++;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
insertch(int16_t c)
|
||||
{
|
||||
undo_begin(UNDO_INSERT);
|
||||
|
||||
/*
|
||||
* insert_ch doesn't need to worry about how to update a
|
||||
* a row; it can just figure out where the cursor is
|
||||
@@ -1826,7 +1780,12 @@ insertch(int16_t c)
|
||||
row_insert_ch(&editor.row[editor.cury],
|
||||
editor.curx,
|
||||
(int16_t)(c & 0xff));
|
||||
undo_append_char((char)c);
|
||||
editor.curx++;
|
||||
|
||||
if (editor.undo->pending != NULL) {
|
||||
editor.undo->pending->col = editor.curx;
|
||||
}
|
||||
editor.dirty++;
|
||||
}
|
||||
|
||||
@@ -1834,8 +1793,8 @@ insertch(int16_t c)
|
||||
void
|
||||
deletech(uint8_t op)
|
||||
{
|
||||
struct erow *row = NULL;
|
||||
unsigned char dch = 0;
|
||||
struct erow *row = NULL;
|
||||
unsigned char dch = 0;
|
||||
|
||||
if (editor.cury >= editor.nrows) {
|
||||
return;
|
||||
@@ -1852,9 +1811,12 @@ deletech(uint8_t op)
|
||||
dch = '\n';
|
||||
}
|
||||
|
||||
undo_begin(UNDO_DELETE);
|
||||
|
||||
if (editor.curx > 0) {
|
||||
row_delete_ch(row, editor.curx - 1);
|
||||
editor.curx--;
|
||||
undo_append_char(dch);
|
||||
} else {
|
||||
editor.curx = editor.row[editor.cury - 1].size;
|
||||
row_append_row(&editor.row[editor.cury - 1],
|
||||
@@ -1866,6 +1828,11 @@ deletech(uint8_t op)
|
||||
delete_row(editor.cury);
|
||||
editor.no_kill = prev;
|
||||
editor.cury--;
|
||||
undo_append_char('\n');
|
||||
}
|
||||
|
||||
if (editor.undo->pending != NULL) {
|
||||
editor.undo->pending->col = editor.curx;
|
||||
}
|
||||
|
||||
if (op == KILLRING_FLUSH) {
|
||||
@@ -2378,8 +2345,10 @@ first_nonwhitespace(struct erow *row)
|
||||
void
|
||||
move_cursor_once(int16_t c, int interactive)
|
||||
{
|
||||
struct erow *row;
|
||||
int reps;
|
||||
struct erow *row;
|
||||
int reps = 0;
|
||||
|
||||
undo_commit();
|
||||
|
||||
row = (editor.cury >= editor.nrows) ? NULL : &editor.row[editor.cury];
|
||||
|
||||
@@ -2500,6 +2469,8 @@ newline(void)
|
||||
{
|
||||
struct erow *row = NULL;
|
||||
|
||||
undo_begin(UNDO_NEWLINE);
|
||||
|
||||
if (editor.cury >= editor.nrows) {
|
||||
erow_insert(editor.cury, "", 0);
|
||||
editor.cury++;
|
||||
@@ -2523,6 +2494,9 @@ newline(void)
|
||||
|
||||
/* BREAK THE KILL CHAIN \m/ */
|
||||
editor.kill = 0;
|
||||
|
||||
/* newlines aren't batched */
|
||||
undo_commit();
|
||||
}
|
||||
|
||||
|
||||
@@ -2584,6 +2558,8 @@ process_kcommand(int16_t c)
|
||||
int jumpy = 0;
|
||||
int reps = 0;
|
||||
|
||||
undo_commit();
|
||||
|
||||
switch (c) {
|
||||
case BACKSPACE:
|
||||
while (editor.curx > 0) {
|
||||
@@ -2741,7 +2717,11 @@ process_kcommand(int16_t c)
|
||||
case 'x':
|
||||
exit(save_file());
|
||||
case 'u':
|
||||
editor_undo();
|
||||
reps = uarg_get();
|
||||
|
||||
while (reps--) {
|
||||
editor_undo();
|
||||
}
|
||||
break;
|
||||
case 'U':
|
||||
editor_redo();
|
||||
@@ -2775,6 +2755,8 @@ process_normal(int16_t c)
|
||||
{
|
||||
int reps = 0;
|
||||
|
||||
undo_commit();
|
||||
|
||||
/* C-u handling – must be the very first thing */
|
||||
if (c == CTRL_KEY('u')) {
|
||||
uarg_start();
|
||||
@@ -2846,7 +2828,7 @@ process_normal(int16_t c)
|
||||
killring_yank();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case ESC_KEY:
|
||||
editor.mode = MODE_ESCAPE;
|
||||
break;
|
||||
@@ -2876,6 +2858,8 @@ process_escape(int16_t c)
|
||||
{
|
||||
editor_set_status("hi");
|
||||
|
||||
undo_commit();
|
||||
|
||||
switch (c) {
|
||||
case '>':
|
||||
editor.cury = editor.nrows;
|
||||
@@ -3367,11 +3351,7 @@ deathknell(void)
|
||||
debug_log = NULL;
|
||||
}
|
||||
|
||||
if (editor.killring != NULL) {
|
||||
erow_free(editor.killring);
|
||||
free(editor.killring);
|
||||
editor.killring = NULL;
|
||||
}
|
||||
killring_flush();
|
||||
|
||||
reset_editor();
|
||||
disable_termraw();
|
||||
|
||||
Reference in New Issue
Block a user