1 Commits

Author SHA1 Message Date
fe4f8df0ab wip 2025-11-26 11:32:35 -08:00
4 changed files with 446 additions and 86 deletions

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.15)
project(ke C) # Specify C language explicitly project(ke C) # Specify C language explicitly
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(KE_VERSION "1.5.2") set(KE_VERSION "1.5.0")
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g") set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_XOPEN_SOURCE") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_XOPEN_SOURCE")

View File

@@ -6,6 +6,7 @@ CFLAGS := -Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g
CFLAGS += -Wno-unused-result CFLAGS += -Wno-unused-result
CFLAGS += -D_DEFAULT_SOURCE -D_XOPEN_SOURCE CFLAGS += -D_DEFAULT_SOURCE -D_XOPEN_SOURCE
CFLAGS += -fsanitize=address -fno-omit-frame-pointer CFLAGS += -fsanitize=address -fno-omit-frame-pointer
CFLAGS += -fprofile-instr-generate -fcoverage-mapping
LDFLAGS := -fsanitize=address LDFLAGS := -fsanitize=address

View File

@@ -5,17 +5,9 @@
installShellFiles, installShellFiles,
... ...
}: }:
let
cmakeContent = builtins.readFile ./CMakeLists.txt;
cmakeLines = lib.splitString "\n" cmakeContent;
# Find the line containing set(KE_VERSION "...")
versionLine = lib.findFirst (l: builtins.match ".*set\\(KE_VERSION \".+\"\\).*" l != null) (throw "KE_VERSION not found in CMakeLists.txt") cmakeLines;
# Extract the version number
version = builtins.head (builtins.match ".*set\\(KE_VERSION \"(.+)\"\\).*" versionLine);
in
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "ke"; pname = "ke";
inherit version; version = "1.5.0";
src = lib.cleanSource ./.; src = lib.cleanSource ./.;

519
main.c
View File

@@ -95,10 +95,38 @@ struct erow {
int rsize; int rsize;
int cap; int cap;
int dirty;
}; };
typedef enum undo_type {
UNDO_INSERT = 1 << 0, /* insertch */
UNDO_DELETE = 1 << 1, /* deletech */
UNDO_PASTE = 1 << 2, /* yank */
UNDO_NEWLINE = 1 << 3, /* newline duh */
UNDO_DELETE_ROW = 1 << 4, /* delete_row duh */
UNDO_INDENT = 1 << 5,
UNDO_UNINDENT = 1 << 6,
UNDO_KILL_REGION = 1 << 7
} undo_flag_t;
typedef struct undo_node {
undo_flag_t type;
int row, col;
struct abuf text;
struct undo_node *next;
struct undo_node *parent;
} undo_node_t;
typedef struct undo_tree {
undo_node_t *root;
undo_node_t *current;
undo_node_t *saved;
undo_node_t *pending;
} undo_tree_t;
/* /*
* editor is the global editor state; it should be broken out * editor is the global editor state; it should be broken out
* to buffers and screen state, probably. * to buffers and screen state, probably.
@@ -123,6 +151,7 @@ struct editor_t {
int mark_curx, mark_cury; int mark_curx, mark_cury;
int uarg, ucount; /* C-u support */ int uarg, ucount; /* C-u support */
time_t msgtm; time_t msgtm;
undo_tree_t *undo;
} editor = { } editor = {
.cols = 0, .cols = 0,
.rows = 0, .rows = 0,
@@ -144,6 +173,7 @@ struct editor_t {
.mark_cury = 0, .mark_cury = 0,
.uarg = 0, .uarg = 0,
.ucount = 0, .ucount = 0,
.undo = NULL,
}; };
@@ -171,6 +201,60 @@ void erow_update(struct erow *row);
void erow_insert(int at, char *s, int len); void erow_insert(int at, char *s, int len);
void erow_free(struct erow *row); void erow_free(struct erow *row);
/*
* undo ops
*
* notes:
* + undo_node_free destroys an entire timeline, including children and next.
* + undo_node_free_branch only discards next.
* + undo_discard_redo_branches kills child and next.
*
* Basic invariants of the undo system:
* + root->parent == NULL
* + root->current is reachable from root via repeated child walk
* + saved is NULL or reachable the same way
* + pending is either NULL or a brand-new node not yet linked
* + when we commit, pending becomes current->child and current moves forward
* + when we undo, current = current->parent
* + when we type after undo, we free current->child (redo branch) first
*
* Or, visually,
*
* root ──> N1 ──> N2 ──> N3 ──> N4* ──> N5 ──> N6 (main timeline)
* ^ ^ ^
* | | |
* saved pending current
*
* + root : first edit ever, never has a parent
* + current : where we are right now in history
* + saved : points to the node that matches the on-disk file
* + pending : temporary node being built (committed → becomes current->child)
*
* If I do a double undo then type something:
*
* root ──> N1 ──> N2 ──> N3* ──> N4 ──> N5 (old N4→N5→N6 discarded)
* ^ ^
* | |
* current pending (new edit)
* |
* saved
*
* All four pointers point into the same tree → and should only be memory
* managed via the root node.
*/
undo_node_t *undo_node_new(undo_flag_t type);
void undo_node_free(undo_node_t *node);
undo_tree_t *undo_tree_new(void);
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_apply(const undo_node_t *node, int direction);
void editor_undo(void);
void editor_redo(void);
/* kill ring, marking, etc... */ /* kill ring, marking, etc... */
void killring_flush(void); void killring_flush(void);
@@ -192,14 +276,13 @@ int get_winsz(int *rows, int *cols);
void jump_to_position(int col, int row); void jump_to_position(int col, int row);
void goto_line(void); void goto_line(void);
int cursor_at_eol(void); int cursor_at_eol(void);
int iswordchar(unsigned char c);
void find_next_word(void);
void delete_next_word(void);
void find_prev_word(void);
void delete_prev_word(void);
void delete_row(int at); 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_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);
@@ -212,7 +295,6 @@ void editor_find_callback(char *query, int16_t c);
void editor_find(void); void editor_find(void);
char *editor_prompt(char*, void (*cb)(char*, int16_t)); char *editor_prompt(char*, void (*cb)(char*, int16_t));
void editor_openfile(void); void editor_openfile(void);
int first_nonwhitespace(struct erow *row);
void move_cursor_once(int16_t c, int interactive); void move_cursor_once(int16_t c, int interactive);
void move_cursor(int16_t c, int interactive); void move_cursor(int16_t c, int interactive);
void uarg_start(void); void uarg_start(void);
@@ -368,6 +450,13 @@ init_editor(void)
editor.dirty = 0; editor.dirty = 0;
editor.mark_set = 0; editor.mark_set = 0;
editor.mark_cury = editor.mark_curx = 0; editor.mark_cury = editor.mark_curx = 0;
if (editor.undo != NULL) {
undo_tree_free(editor.undo);
editor.undo = NULL;
}
editor.undo = undo_tree_new();
} }
@@ -381,12 +470,17 @@ reset_editor(void)
erow_free(&editor.row[i]); erow_free(&editor.row[i]);
} }
free(editor.row); free(editor.row);
editor.row = NULL;
if (editor.filename != NULL) { if (editor.filename != NULL) {
free(editor.filename); free(editor.filename);
editor.filename = NULL; editor.filename = NULL;
} }
if (editor.undo != NULL) {
undo_tree_free(editor.undo);
editor.undo = NULL;
}
init_editor(); init_editor();
} }
@@ -508,12 +602,6 @@ erow_render_to_cursor(struct erow *row, int cx)
continue; continue;
} }
if (b < 0x80) {
rx++;
j++;
continue;
}
size_t rem = (size_t)row->size - j; size_t rem = (size_t)row->size - j;
size_t n = mbrtowc(&wc, &row->line[j], rem, &st); size_t n = mbrtowc(&wc, &row->line[j], rem, &st);
@@ -567,9 +655,6 @@ erow_cursor_to_render(struct erow *row, int rx)
} else if (b < 0x20) { } else if (b < 0x20) {
w = 3; /* "\\xx" */ w = 3; /* "\\xx" */
adv = 1; adv = 1;
} else if (b < 0x80) {
w = 1;
adv = 1;
} else { } else {
size_t rem = (size_t)row->size - j; size_t rem = (size_t)row->size - j;
size_t n = mbrtowc(&wc, &row->line[j], rem, &st); size_t n = mbrtowc(&wc, &row->line[j], rem, &st);
@@ -611,7 +696,6 @@ erow_init(struct erow *row, int len)
row->render = NULL; row->render = NULL;
row->line = NULL; row->line = NULL;
row->cap = cap_growth(0, len) + 1; /* extra byte for NUL end */ row->cap = cap_growth(0, len) + 1; /* extra byte for NUL end */
row->dirty = 1;
row->line = malloc(row->cap); row->line = malloc(row->cap);
assert(row->line != NULL); assert(row->line != NULL);
@@ -708,6 +792,241 @@ erow_free(struct erow *row)
free(row->line); free(row->line);
row->render = NULL; row->render = NULL;
row->line = NULL; row->line = NULL;
row->cap = 0;
row->size = 0;
}
undo_node_t *
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->parent = NULL;
ab_init(&node->text);
return node;
}
void
undo_node_free(undo_node_t *node)
{
if (node == NULL) {
return;
}
assert(node->next->parent == node);
ab_free(&node->text);
node = node->next;
free(node->parent);
node->parent = NULL;
free(node);
}
undo_tree_t *
undo_tree_new(void)
{
undo_tree_t *tree = NULL;
tree = calloc1(sizeof(undo_tree_t));
tree->root = NULL;
tree->current = NULL;
tree->saved = NULL;
tree->pending = NULL;
return tree;
}
void
undo_tree_free(undo_tree_t *tree)
{
if (tree == NULL) {
return;
}
undo_node_free(tree->root);
if (tree->pending != NULL) {
undo_node_free(tree->pending);
}
if (debug_log == NULL) {
tree->root = NULL;
tree->current = NULL;
tree->saved = NULL;
tree->pending = NULL;
} else {
tree->root =
tree->current =
tree->saved =
tree->pending =
(void *)0xDEADBEEF;
}
free(tree);
}
/*
* undo_begin starts a new undo sequence. Note that it is a non-op
* if a new pending sequence doesn't need to be created.
*/
void
undo_begin(undo_flag_t type)
{
struct undo_tree *tree = editor.undo;
assert(tree != NULL);
if (tree->pending != NULL) {
if (tree->pending->type == type) {
return;
}
undo_commit();
}
tree->pending = undo_node_new(type);
tree->pending->row = editor.cury;
tree->pending->col = editor.curx;
}
void
undo_append_char(const char c)
{
undo_node_t *node = editor.undo->pending;
assert(node != NULL);
ab_appendch(&node->text, c);
}
void
undo_append(const char *data, const size_t len)
{
undo_node_t *node = editor.undo->pending;
assert(node != NULL);
ab_append(&node->text, data, len);
}
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;
assert(tree != NULL);
if (tree->pending == NULL) {
return; /* no pending operation */
}
if (tree->root == NULL) {
}
}
void
undo_apply(const struct undo_node *node, const int direction)
{
int redo;
assert(node != NULL);
redo = direction < 0 ? 1 : 0;
switch (node->type) {
default:
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;
}
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);
} }
@@ -741,6 +1060,10 @@ killring_yank(void)
insertch(ch); insertch(ch);
} }
} }
undo_begin(UNDO_PASTE);
undo_append(editor.killring->line, editor.killring->size);
undo_commit(); /* atomic */
} }
@@ -767,7 +1090,7 @@ killring_start_with_char(unsigned char ch)
row->line[row->size] = ch; row->line[row->size] = ch;
row->size++; row->size++;
row->line[row->size] = '\0'; row->line[row->size] = '\0';
row->dirty = 1; erow_update(row);
} }
@@ -787,7 +1110,7 @@ killring_append_char(unsigned char ch)
row->line[row->size] = ch; row->line[row->size] = ch;
row->size++; row->size++;
row->line[row->size] = '\0'; row->line[row->size] = '\0';
row->dirty = 1; erow_update(row);
} }
@@ -805,7 +1128,7 @@ killring_prepend_char(unsigned char ch)
memmove(&row->line[1], &row->line[0], row->size + 1); memmove(&row->line[1], &row->line[0], row->size + 1);
row->line[0] = ch; row->line[0] = ch;
row->size++; row->size++;
row->dirty = 1; erow_update(row);
} }
@@ -1013,7 +1336,7 @@ unindent_region(void)
if (del > 0) { if (del > 0) {
memmove(row->line, row->line + del, row->size - del + 1); /* +1 for NUL */ memmove(row->line, row->line + del, row->size - del + 1); /* +1 for NUL */
row->size -= del; row->size -= del;
row->dirty = 1; erow_update(row);
} }
} }
} }
@@ -1172,13 +1495,6 @@ cursor_at_eol(void)
} }
int
iswordchar(unsigned char c)
{
return isalnum(c) || c == '_' || strchr("/!@#$%^&*+-=~", c) != NULL;
}
void void
find_next_word(void) find_next_word(void)
{ {
@@ -1186,7 +1502,7 @@ find_next_word(void)
move_cursor(ARROW_RIGHT, 1); move_cursor(ARROW_RIGHT, 1);
} }
if (iswordchar(editor.row[editor.cury].line[editor.curx])) { if (isalnum(editor.row[editor.cury].line[editor.curx])) {
while (!isspace(editor.row[editor.cury].line[editor.curx]) && ! while (!isspace(editor.row[editor.cury].line[editor.curx]) && !
cursor_at_eol()) { cursor_at_eol()) {
move_cursor(ARROW_RIGHT, 1); move_cursor(ARROW_RIGHT, 1);
@@ -1213,7 +1529,7 @@ delete_next_word(void)
deletech(KILLRING_APPEND); deletech(KILLRING_APPEND);
} }
if (iswordchar(editor.row[editor.cury].line[editor.curx])) { if (isalnum(editor.row[editor.cury].line[editor.curx])) {
while (!isspace(editor.row[editor.cury].line[editor.curx]) && ! while (!isspace(editor.row[editor.cury].line[editor.curx]) && !
cursor_at_eol()) { cursor_at_eol()) {
move_cursor(ARROW_RIGHT, 1); move_cursor(ARROW_RIGHT, 1);
@@ -1350,7 +1666,7 @@ row_append_row(struct erow *row, char *s, int len)
memcpy(&row->line[row->size], s, len); memcpy(&row->line[row->size], s, len);
row->size += len; row->size += len;
row->line[row->size] = '\0'; row->line[row->size] = '\0';
row->dirty = 1; erow_update(row);
editor.dirty++; editor.dirty++;
} }
@@ -1372,7 +1688,39 @@ row_insert_ch(struct erow *row, int at, int16_t c)
row->size++; row->size++;
row->line[at] = c & 0xff; row->line[at] = c & 0xff;
row->dirty = 1; erow_update(row);
}
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++;
} }
@@ -1385,7 +1733,28 @@ row_delete_ch(struct erow *row, int at)
memmove(&row->line[at], &row->line[at + 1], row->size - at); memmove(&row->line[at], &row->line[at + 1], row->size - at);
row->size--; row->size--;
row->dirty = 1; erow_update(row);
editor.dirty++;
}
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++; editor.dirty++;
} }
@@ -1393,6 +1762,8 @@ row_delete_ch(struct erow *row, int at)
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
@@ -1409,7 +1780,12 @@ 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++;
if (editor.undo->pending != NULL) {
editor.undo->pending->col = editor.curx;
}
editor.dirty++; editor.dirty++;
} }
@@ -1435,9 +1811,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],
@@ -1449,6 +1828,11 @@ 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 (editor.undo->pending != NULL) {
editor.undo->pending->col = editor.curx;
} }
if (op == KILLRING_FLUSH) { if (op == KILLRING_FLUSH) {
@@ -1844,7 +2228,7 @@ editor_find_callback(char* query, int16_t c)
row = &editor.row[current]; row = &editor.row[current];
/* Skip rendering search on raw bytes — use line[] but respect render offsets */ /* Skip rendering search on raw bytes — use line[] but respect render offsets */
row->dirty = 1; erow_update(row);
char* search_start = row->render; char* search_start = row->render;
if (current == start_row && direction == 1 && last_match == -1) { if (current == start_row && direction == 1 && last_match == -1) {
@@ -1938,14 +2322,6 @@ first_nonwhitespace(struct erow *row)
} }
while (pos < row->size) { while (pos < row->size) {
if ((unsigned char)row->line[pos] < 0x80) {
if (!isspace((unsigned char)row->line[pos])) {
return pos;
}
pos++;
continue;
}
len = mbrtowc(&wc, &row->line[pos], row->size - pos, &state); len = mbrtowc(&wc, &row->line[pos], row->size - pos, &state);
if (len == (size_t)-1 || len == (size_t)-2) { if (len == (size_t)-1 || len == (size_t)-2) {
break; break;
@@ -1972,6 +2348,8 @@ move_cursor_once(int16_t c, int interactive)
struct erow *row; struct erow *row;
int reps = 0; 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];
switch (c) { switch (c) {
@@ -2091,6 +2469,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++;
@@ -2107,13 +2487,16 @@ newline(void)
row = &editor.row[editor.cury]; row = &editor.row[editor.cury];
row->size = editor.curx; row->size = editor.curx;
row->line[row->size] = '\0'; row->line[row->size] = '\0';
row->dirty = 1; erow_update(row);
editor.cury++; editor.cury++;
editor.curx = 0; editor.curx = 0;
} }
/* BREAK THE KILL CHAIN \m/ */ /* BREAK THE KILL CHAIN \m/ */
editor.kill = 0; editor.kill = 0;
/* newlines aren't batched */
undo_commit();
} }
@@ -2175,6 +2558,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) {
@@ -2334,10 +2719,12 @@ process_kcommand(int16_t c)
case 'u': case 'u':
reps = uarg_get(); reps = uarg_get();
while (reps--); while (reps--) {
editor_set_status("Undo not implemented."); editor_undo();
}
break; break;
case 'U': case 'U':
editor_redo();
break; break;
case 'y': case 'y':
reps = uarg_get(); reps = uarg_get();
@@ -2368,6 +2755,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();
@@ -2439,7 +2828,7 @@ process_normal(int16_t c)
killring_yank(); killring_yank();
} }
break; break;
case ESC_KEY: case ESC_KEY:
editor.mode = MODE_ESCAPE; editor.mode = MODE_ESCAPE;
break; break;
@@ -2469,6 +2858,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;
@@ -2733,11 +3124,7 @@ draw_rows(struct abuf *ab)
} }
} else { } else {
row = &editor.row[filerow]; row = &editor.row[filerow];
if (row->dirty) { erow_update(row);
erow_update(row);
row->dirty = 0;
}
len = row->rsize - editor.coloffs; len = row->rsize - editor.coloffs;
if (len < 0) { if (len < 0) {
len = 0; len = 0;
@@ -2841,15 +3228,11 @@ draw_message_line(struct abuf *ab)
void void
scroll(void) scroll(void)
{ {
struct erow *row = NULL;
editor.rx = 0; editor.rx = 0;
if (editor.cury < editor.nrows) { if (editor.cury < editor.nrows) {
row = &editor.row[editor.cury]; editor.rx = erow_render_to_cursor(
if (row->dirty == 1) { &editor.row[editor.cury],
erow_update(row); editor.curx);
}
editor.rx = erow_render_to_cursor(row, editor.curx);
} }
if (editor.cury < editor.rowoffs) { if (editor.cury < editor.rowoffs) {
@@ -2879,7 +3262,6 @@ display_refresh(void)
scroll(); scroll();
ab_append(&ab, ESCSEQ "?25l", 6); ab_append(&ab, ESCSEQ "?25l", 6);
ab_append(&ab, ESCSEQ "H", 3);
display_clear(&ab); display_clear(&ab);
draw_rows(&ab); draw_rows(&ab);
@@ -2913,15 +3295,6 @@ editor_set_status(const char *fmt, ...)
} }
int
kbhit(void)
{
int bytes_waiting;
ioctl(STDIN_FILENO, FIONREAD, &bytes_waiting);
return bytes_waiting > 0;
}
void void
loop(void) loop(void)
{ {
@@ -2938,9 +3311,7 @@ loop(void)
* *
*/ */
if ((up = process_keypress()) != 0) { if ((up = process_keypress()) != 0) {
while (kbhit()) { while (process_keypress());
process_keypress();
}
} }
} }
} }
@@ -2980,11 +3351,7 @@ deathknell(void)
debug_log = NULL; debug_log = NULL;
} }
if (editor.killring != NULL) { killring_flush();
erow_free(editor.killring);
free(editor.killring);
editor.killring = NULL;
}
reset_editor(); reset_editor();
disable_termraw(); disable_termraw();