more undo work

This commit is contained in:
2025-11-26 03:00:49 -08:00
parent 09881706f2
commit ace25c5c65

140
main.c
View File

@@ -88,13 +88,13 @@ struct abuf {
/* editor row */ /* editor row */
struct erow { struct erow {
char *line; char *line;
char *render; char *render;
int size; int size;
int rsize; int rsize;
int cap; int cap;
}; };
@@ -284,7 +284,10 @@ int cursor_at_eol(void);
void delete_row(int at); void delete_row(int at);
void row_append_row(struct erow *row, char *s, int len); 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_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_ch(struct erow *row, int at);
void row_delete_block(struct erow *row, int at, size_t len);
void insertch(int16_t c); void insertch(int16_t c);
void deletech(uint8_t op); void deletech(uint8_t op);
void open_file(const char *filename); void open_file(const char *filename);
@@ -1059,66 +1062,62 @@ editor_redo(void)
void void
undo_apply(const struct undo_node *node, const int direction) undo_apply(const struct undo_node *node, const int direction)
{ {
struct erow *erow = NULL;
int row = node->row; int row = node->row;
int col = node->col; int col = node->col;
const char *data = node->text.b; const char *data = node->text.b;
size_t len = node->text.len; size_t len = node->text.len;
if (row >= editor.nrows) {
return;
}
erow = &editor.row[row];
jump_to_position(col, row); jump_to_position(col, row);
switch (node->type) { switch (node->type) {
case UNDO_PASTE: case UNDO_PASTE:
case UNDO_INSERT: case UNDO_INSERT:
if (direction > 0) { if (direction > 0) {
for (size_t i = 0; i < len; i++) { row_insert_block(erow, col, data, len);
insertch((unsigned char)data[i]); editor.curx = col + len;
} } else { /* UNDO = delete the whole block */
} else { row_delete_block(erow, col, len);
for (size_t i = 0; i < len; i++) { editor.curx = col;
deletech(KILLRING_NO_OP);
}
} }
break; break;
case UNDO_DELETE: case UNDO_DELETE:
if (direction > 0) { if (direction > 0) {
for (size_t i = 0; i < len; i++) { row_delete_block(erow, col, len);
deletech(KILLRING_NO_OP); editor.curx = col;
}
} else { } else {
for (size_t i = 0; i < len; i++) { row_insert_block(erow, col, data, len);
insertch((unsigned char)data[i]); editor.curx = col + len;
}
} }
break; break;
case UNDO_NEWLINE: case UNDO_NEWLINE:
if (direction > 0) { if (direction > 0) {
newline(); newline();
} else { } else {
if (editor.cury > 0) { if (editor.cury > 0) {
editor.curx = editor.row[editor.cury - 1].size; int prev = editor.cury - 1;
row_append_row(&editor.row[editor.cury - 1], editor.curx = editor.row[prev].size;
editor.row[editor.cury].line, row_append_row(&editor.row[prev],
editor.row[editor.cury].size); editor.row[editor.cury].line,
editor.row[editor.cury].size);
delete_row(editor.cury); delete_row(editor.cury);
} }
} }
break; break;
case UNDO_DELETE_ROW: case UNDO_DELETE_ROW:
if (direction > 0) { if (direction > 0) {
/* redo = delete the whole row again */
delete_row(editor.cury); delete_row(editor.cury);
} else { } else {
/* undo = re-insert the saved row (including final '\n') */ if (len > 0 && data[len-1] == '\n') len--;
if (len > 0 && data[len - 1] == '\n') len--;
erow_insert(editor.cury, (char*)data, len); erow_insert(editor.cury, (char*)data, len);
/* cursor goes to start of re-inserted row */
editor.curx = 0; editor.curx = 0;
} }
break; break;
case UNDO_INDENT: case UNDO_INDENT:
case UNDO_KILL_REGION: case UNDO_KILL_REGION:
/* These are more complex you can implement later */ /* These are more complex you can implement later */
@@ -1165,6 +1164,10 @@ killring_yank(void)
insertch(ch); insertch(ch);
} }
} }
undo_begin(UNDO_PASTE);
undo_append(editor.killring->line, editor.killring->size);
undo_commit(); /* atomic */
} }
@@ -1793,6 +1796,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 void
row_delete_ch(struct erow *row, int at) row_delete_ch(struct erow *row, int at)
{ {
@@ -1807,9 +1842,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 void
insertch(int16_t c) insertch(int16_t c)
{ {
undo_begin(UNDO_INSERT);
/* /*
* insert_ch doesn't need to worry about how to update a * insert_ch doesn't need to worry about how to update a
* a row; it can just figure out where the cursor is * a row; it can just figure out where the cursor is
@@ -1826,6 +1884,7 @@ insertch(int16_t c)
row_insert_ch(&editor.row[editor.cury], row_insert_ch(&editor.row[editor.cury],
editor.curx, editor.curx,
(int16_t)(c & 0xff)); (int16_t)(c & 0xff));
undo_append_char((char)c);
editor.curx++; editor.curx++;
editor.dirty++; editor.dirty++;
} }
@@ -1852,9 +1911,12 @@ deletech(uint8_t op)
dch = '\n'; dch = '\n';
} }
undo_begin(UNDO_DELETE);
if (editor.curx > 0) { if (editor.curx > 0) {
row_delete_ch(row, editor.curx - 1); row_delete_ch(row, editor.curx - 1);
editor.curx--; editor.curx--;
undo_append_char(dch);
} else { } else {
editor.curx = editor.row[editor.cury - 1].size; editor.curx = editor.row[editor.cury - 1].size;
row_append_row(&editor.row[editor.cury - 1], row_append_row(&editor.row[editor.cury - 1],
@@ -1866,6 +1928,7 @@ deletech(uint8_t op)
delete_row(editor.cury); delete_row(editor.cury);
editor.no_kill = prev; editor.no_kill = prev;
editor.cury--; editor.cury--;
undo_append_char('\n');
} }
if (op == KILLRING_FLUSH) { if (op == KILLRING_FLUSH) {
@@ -2378,8 +2441,10 @@ first_nonwhitespace(struct erow *row)
void void
move_cursor_once(int16_t c, int interactive) move_cursor_once(int16_t c, int interactive)
{ {
struct erow *row; struct erow *row;
int reps; int reps = 0;
undo_commit();
row = (editor.cury >= editor.nrows) ? NULL : &editor.row[editor.cury]; row = (editor.cury >= editor.nrows) ? NULL : &editor.row[editor.cury];
@@ -2500,6 +2565,8 @@ newline(void)
{ {
struct erow *row = NULL; struct erow *row = NULL;
undo_begin(UNDO_NEWLINE);
if (editor.cury >= editor.nrows) { if (editor.cury >= editor.nrows) {
erow_insert(editor.cury, "", 0); erow_insert(editor.cury, "", 0);
editor.cury++; editor.cury++;
@@ -2523,6 +2590,9 @@ newline(void)
/* BREAK THE KILL CHAIN \m/ */ /* BREAK THE KILL CHAIN \m/ */
editor.kill = 0; editor.kill = 0;
/* newlines aren't batched */
undo_commit();
} }
@@ -2584,6 +2654,8 @@ process_kcommand(int16_t c)
int jumpy = 0; int jumpy = 0;
int reps = 0; int reps = 0;
undo_commit();
switch (c) { switch (c) {
case BACKSPACE: case BACKSPACE:
while (editor.curx > 0) { while (editor.curx > 0) {
@@ -2775,6 +2847,8 @@ process_normal(int16_t c)
{ {
int reps = 0; int reps = 0;
undo_commit();
/* C-u handling must be the very first thing */ /* C-u handling must be the very first thing */
if (c == CTRL_KEY('u')) { if (c == CTRL_KEY('u')) {
uarg_start(); uarg_start();
@@ -2876,6 +2950,8 @@ process_escape(int16_t c)
{ {
editor_set_status("hi"); editor_set_status("hi");
undo_commit();
switch (c) { switch (c) {
case '>': case '>':
editor.cury = editor.nrows; editor.cury = editor.nrows;