Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe4f8df0ab |
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.15)
|
||||
project(ke C) # Specify C language explicitly
|
||||
|
||||
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 "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_XOPEN_SOURCE")
|
||||
|
||||
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
|
||||
|
||||
|
||||
10
default.nix
10
default.nix
@@ -5,17 +5,9 @@
|
||||
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 {
|
||||
pname = "ke";
|
||||
inherit version;
|
||||
version = "1.5.0";
|
||||
|
||||
src = lib.cleanSource ./.;
|
||||
|
||||
|
||||
517
main.c
517
main.c
@@ -95,10 +95,38 @@ struct erow {
|
||||
int rsize;
|
||||
|
||||
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
|
||||
* to buffers and screen state, probably.
|
||||
@@ -123,6 +151,7 @@ struct editor_t {
|
||||
int mark_curx, mark_cury;
|
||||
int uarg, ucount; /* C-u support */
|
||||
time_t msgtm;
|
||||
undo_tree_t *undo;
|
||||
} editor = {
|
||||
.cols = 0,
|
||||
.rows = 0,
|
||||
@@ -144,6 +173,7 @@ struct editor_t {
|
||||
.mark_cury = 0,
|
||||
.uarg = 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_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... */
|
||||
void killring_flush(void);
|
||||
@@ -192,14 +276,13 @@ int get_winsz(int *rows, int *cols);
|
||||
void jump_to_position(int col, int row);
|
||||
void goto_line(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 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);
|
||||
@@ -212,7 +295,6 @@ void editor_find_callback(char *query, int16_t c);
|
||||
void editor_find(void);
|
||||
char *editor_prompt(char*, void (*cb)(char*, int16_t));
|
||||
void editor_openfile(void);
|
||||
int first_nonwhitespace(struct erow *row);
|
||||
void move_cursor_once(int16_t c, int interactive);
|
||||
void move_cursor(int16_t c, int interactive);
|
||||
void uarg_start(void);
|
||||
@@ -368,6 +450,13 @@ init_editor(void)
|
||||
editor.dirty = 0;
|
||||
editor.mark_set = 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]);
|
||||
}
|
||||
free(editor.row);
|
||||
editor.row = NULL;
|
||||
|
||||
if (editor.filename != NULL) {
|
||||
free(editor.filename);
|
||||
editor.filename = NULL;
|
||||
}
|
||||
|
||||
if (editor.undo != NULL) {
|
||||
undo_tree_free(editor.undo);
|
||||
editor.undo = NULL;
|
||||
}
|
||||
|
||||
init_editor();
|
||||
}
|
||||
@@ -508,12 +602,6 @@ erow_render_to_cursor(struct erow *row, int cx)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (b < 0x80) {
|
||||
rx++;
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t rem = (size_t)row->size - j;
|
||||
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) {
|
||||
w = 3; /* "\\xx" */
|
||||
adv = 1;
|
||||
} else if (b < 0x80) {
|
||||
w = 1;
|
||||
adv = 1;
|
||||
} else {
|
||||
size_t rem = (size_t)row->size - j;
|
||||
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->line = NULL;
|
||||
row->cap = cap_growth(0, len) + 1; /* extra byte for NUL end */
|
||||
row->dirty = 1;
|
||||
|
||||
row->line = malloc(row->cap);
|
||||
assert(row->line != NULL);
|
||||
@@ -708,6 +792,241 @@ 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(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);
|
||||
}
|
||||
}
|
||||
|
||||
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->size++;
|
||||
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->size++;
|
||||
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);
|
||||
row->line[0] = ch;
|
||||
row->size++;
|
||||
row->dirty = 1;
|
||||
erow_update(row);
|
||||
}
|
||||
|
||||
|
||||
@@ -1013,7 +1336,7 @@ unindent_region(void)
|
||||
if (del > 0) {
|
||||
memmove(row->line, row->line + del, row->size - del + 1); /* +1 for NUL */
|
||||
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
|
||||
find_next_word(void)
|
||||
{
|
||||
@@ -1186,7 +1502,7 @@ find_next_word(void)
|
||||
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]) && !
|
||||
cursor_at_eol()) {
|
||||
move_cursor(ARROW_RIGHT, 1);
|
||||
@@ -1213,7 +1529,7 @@ delete_next_word(void)
|
||||
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]) && !
|
||||
cursor_at_eol()) {
|
||||
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);
|
||||
row->size += len;
|
||||
row->line[row->size] = '\0';
|
||||
row->dirty = 1;
|
||||
erow_update(row);
|
||||
editor.dirty++;
|
||||
}
|
||||
|
||||
@@ -1372,7 +1688,39 @@ row_insert_ch(struct erow *row, int at, int16_t c)
|
||||
row->size++;
|
||||
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);
|
||||
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++;
|
||||
}
|
||||
|
||||
@@ -1393,6 +1762,8 @@ row_delete_ch(struct erow *row, int at)
|
||||
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
|
||||
@@ -1409,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++;
|
||||
}
|
||||
|
||||
@@ -1435,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],
|
||||
@@ -1449,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) {
|
||||
@@ -1844,7 +2228,7 @@ editor_find_callback(char* query, int16_t c)
|
||||
row = &editor.row[current];
|
||||
|
||||
/* Skip rendering search on raw bytes — use line[] but respect render offsets */
|
||||
row->dirty = 1;
|
||||
erow_update(row);
|
||||
|
||||
char* search_start = row->render;
|
||||
if (current == start_row && direction == 1 && last_match == -1) {
|
||||
@@ -1938,14 +2322,6 @@ first_nonwhitespace(struct erow *row)
|
||||
}
|
||||
|
||||
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);
|
||||
if (len == (size_t)-1 || len == (size_t)-2) {
|
||||
break;
|
||||
@@ -1972,6 +2348,8 @@ move_cursor_once(int16_t c, int interactive)
|
||||
struct erow *row;
|
||||
int reps = 0;
|
||||
|
||||
undo_commit();
|
||||
|
||||
row = (editor.cury >= editor.nrows) ? NULL : &editor.row[editor.cury];
|
||||
|
||||
switch (c) {
|
||||
@@ -2091,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++;
|
||||
@@ -2107,13 +2487,16 @@ newline(void)
|
||||
row = &editor.row[editor.cury];
|
||||
row->size = editor.curx;
|
||||
row->line[row->size] = '\0';
|
||||
row->dirty = 1;
|
||||
erow_update(row);
|
||||
editor.cury++;
|
||||
editor.curx = 0;
|
||||
}
|
||||
|
||||
/* BREAK THE KILL CHAIN \m/ */
|
||||
editor.kill = 0;
|
||||
|
||||
/* newlines aren't batched */
|
||||
undo_commit();
|
||||
}
|
||||
|
||||
|
||||
@@ -2175,6 +2558,8 @@ process_kcommand(int16_t c)
|
||||
int jumpy = 0;
|
||||
int reps = 0;
|
||||
|
||||
undo_commit();
|
||||
|
||||
switch (c) {
|
||||
case BACKSPACE:
|
||||
while (editor.curx > 0) {
|
||||
@@ -2334,10 +2719,12 @@ process_kcommand(int16_t c)
|
||||
case 'u':
|
||||
reps = uarg_get();
|
||||
|
||||
while (reps--);
|
||||
editor_set_status("Undo not implemented.");
|
||||
while (reps--) {
|
||||
editor_undo();
|
||||
}
|
||||
break;
|
||||
case 'U':
|
||||
editor_redo();
|
||||
break;
|
||||
case 'y':
|
||||
reps = uarg_get();
|
||||
@@ -2368,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();
|
||||
@@ -2469,6 +2858,8 @@ process_escape(int16_t c)
|
||||
{
|
||||
editor_set_status("hi");
|
||||
|
||||
undo_commit();
|
||||
|
||||
switch (c) {
|
||||
case '>':
|
||||
editor.cury = editor.nrows;
|
||||
@@ -2733,11 +3124,7 @@ draw_rows(struct abuf *ab)
|
||||
}
|
||||
} else {
|
||||
row = &editor.row[filerow];
|
||||
if (row->dirty) {
|
||||
erow_update(row);
|
||||
row->dirty = 0;
|
||||
}
|
||||
|
||||
erow_update(row);
|
||||
len = row->rsize - editor.coloffs;
|
||||
if (len < 0) {
|
||||
len = 0;
|
||||
@@ -2841,15 +3228,11 @@ draw_message_line(struct abuf *ab)
|
||||
void
|
||||
scroll(void)
|
||||
{
|
||||
struct erow *row = NULL;
|
||||
editor.rx = 0;
|
||||
if (editor.cury < editor.nrows) {
|
||||
row = &editor.row[editor.cury];
|
||||
if (row->dirty == 1) {
|
||||
erow_update(row);
|
||||
}
|
||||
|
||||
editor.rx = erow_render_to_cursor(row, editor.curx);
|
||||
editor.rx = erow_render_to_cursor(
|
||||
&editor.row[editor.cury],
|
||||
editor.curx);
|
||||
}
|
||||
|
||||
if (editor.cury < editor.rowoffs) {
|
||||
@@ -2879,7 +3262,6 @@ display_refresh(void)
|
||||
scroll();
|
||||
|
||||
ab_append(&ab, ESCSEQ "?25l", 6);
|
||||
ab_append(&ab, ESCSEQ "H", 3);
|
||||
display_clear(&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
|
||||
loop(void)
|
||||
{
|
||||
@@ -2938,9 +3311,7 @@ loop(void)
|
||||
*
|
||||
*/
|
||||
if ((up = process_keypress()) != 0) {
|
||||
while (kbhit()) {
|
||||
process_keypress();
|
||||
}
|
||||
while (process_keypress());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2980,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