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
|
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")
|
||||||
|
|||||||
1
Makefile
1
Makefile
@@ -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
|
||||||
|
|
||||||
|
|||||||
10
default.nix
10
default.nix
@@ -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
519
main.c
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user