Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db38266849 | |||
| 6d1b7f8e56 | |||
| 64647f77b0 | |||
| 2c3b2ae0f0 | |||
| a605e47458 | |||
| 40ee1e8d7b | |||
| 4464159301 | |||
| dc9fb58a41 | |||
| bbd682cec7 | |||
| ace25c5c65 |
@@ -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.0")
|
||||
set(KE_VERSION "1.5.2")
|
||||
|
||||
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")
|
||||
|
||||
10
default.nix
10
default.nix
@@ -5,9 +5,17 @@
|
||||
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";
|
||||
version = "1.5.0";
|
||||
inherit version;
|
||||
|
||||
src = lib.cleanSource ./.;
|
||||
|
||||
|
||||
595
main.c
595
main.c
@@ -88,45 +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;
|
||||
int dirty;
|
||||
};
|
||||
|
||||
|
||||
typedef enum undo_flag {
|
||||
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 *child;
|
||||
} 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.
|
||||
@@ -151,7 +123,6 @@ 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,
|
||||
@@ -173,7 +144,6 @@ struct editor_t {
|
||||
.mark_cury = 0,
|
||||
.uarg = 0,
|
||||
.ucount = 0,
|
||||
.undo = NULL,
|
||||
};
|
||||
|
||||
|
||||
@@ -187,6 +157,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);
|
||||
@@ -199,67 +171,6 @@ 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);
|
||||
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_begin(undo_flag_t type);
|
||||
void undo_append_char(char c);
|
||||
void undo_append(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);
|
||||
|
||||
/* kill ring, marking, etc... */
|
||||
void killring_flush(void);
|
||||
@@ -281,8 +192,12 @@ 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_delete_ch(struct erow *row, int at);
|
||||
void insertch(int16_t c);
|
||||
@@ -297,6 +212,7 @@ 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);
|
||||
@@ -452,13 +368,6 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -478,10 +387,6 @@ reset_editor(void)
|
||||
editor.filename = NULL;
|
||||
}
|
||||
|
||||
if (editor.undo != NULL) {
|
||||
undo_tree_free(editor.undo);
|
||||
editor.undo = NULL;
|
||||
}
|
||||
|
||||
init_editor();
|
||||
}
|
||||
@@ -527,6 +432,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)
|
||||
{
|
||||
@@ -582,6 +508,12 @@ 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);
|
||||
|
||||
@@ -635,6 +567,9 @@ 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);
|
||||
@@ -676,6 +611,7 @@ 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);
|
||||
@@ -775,366 +711,6 @@ erow_free(struct erow *row)
|
||||
}
|
||||
|
||||
|
||||
undo_node_t *
|
||||
undo_node_new(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;
|
||||
|
||||
ab_init(&node->text);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
undo_node_free(undo_node_t *node)
|
||||
{
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ab_free(&node->text);
|
||||
undo_node_free(node->child);
|
||||
undo_node_free(node->next);
|
||||
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)
|
||||
{
|
||||
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 *ut)
|
||||
{
|
||||
if (ut == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
undo_node_free(ut->root);
|
||||
undo_node_free(ut->pending);
|
||||
|
||||
if (debug_log == NULL) {
|
||||
ut->root = NULL;
|
||||
ut->current = NULL;
|
||||
ut->saved = NULL;
|
||||
ut->pending = NULL;
|
||||
} else {
|
||||
ut->root =
|
||||
ut->current =
|
||||
ut->saved =
|
||||
ut->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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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(const undo_flag_t type)
|
||||
{
|
||||
undo_tree_t *tree = editor.undo;
|
||||
undo_node_t *node = tree->pending;
|
||||
|
||||
if (undo_continue_pending(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tree->pending != NULL) {
|
||||
undo_commit();
|
||||
}
|
||||
|
||||
node = undo_node_new(type);
|
||||
assert(node != NULL);
|
||||
|
||||
node->type = type;
|
||||
node->row = editor.cury;
|
||||
node->col = editor.curx;
|
||||
tree->pending = node;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
|
||||
if (tree->pending == NULL) {
|
||||
return; /* nothing to commit */
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
jump_to_position(col, row);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
editor.dirty = 1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
killring_flush(void)
|
||||
{
|
||||
@@ -1191,7 +767,7 @@ killring_start_with_char(unsigned char ch)
|
||||
row->line[row->size] = ch;
|
||||
row->size++;
|
||||
row->line[row->size] = '\0';
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1211,7 +787,7 @@ killring_append_char(unsigned char ch)
|
||||
row->line[row->size] = ch;
|
||||
row->size++;
|
||||
row->line[row->size] = '\0';
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1229,7 +805,7 @@ killring_prepend_char(unsigned char ch)
|
||||
memmove(&row->line[1], &row->line[0], row->size + 1);
|
||||
row->line[0] = ch;
|
||||
row->size++;
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1437,7 +1013,7 @@ unindent_region(void)
|
||||
if (del > 0) {
|
||||
memmove(row->line, row->line + del, row->size - del + 1); /* +1 for NUL */
|
||||
row->size -= del;
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1596,6 +1172,13 @@ cursor_at_eol(void)
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iswordchar(unsigned char c)
|
||||
{
|
||||
return isalnum(c) || c == '_' || strchr("/!@#$%^&*+-=~", c) != NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
find_next_word(void)
|
||||
{
|
||||
@@ -1603,7 +1186,7 @@ find_next_word(void)
|
||||
move_cursor(ARROW_RIGHT, 1);
|
||||
}
|
||||
|
||||
if (isalnum(editor.row[editor.cury].line[editor.curx])) {
|
||||
if (iswordchar(editor.row[editor.cury].line[editor.curx])) {
|
||||
while (!isspace(editor.row[editor.cury].line[editor.curx]) && !
|
||||
cursor_at_eol()) {
|
||||
move_cursor(ARROW_RIGHT, 1);
|
||||
@@ -1630,7 +1213,7 @@ delete_next_word(void)
|
||||
deletech(KILLRING_APPEND);
|
||||
}
|
||||
|
||||
if (isalnum(editor.row[editor.cury].line[editor.curx])) {
|
||||
if (iswordchar(editor.row[editor.cury].line[editor.curx])) {
|
||||
while (!isspace(editor.row[editor.cury].line[editor.curx]) && !
|
||||
cursor_at_eol()) {
|
||||
move_cursor(ARROW_RIGHT, 1);
|
||||
@@ -1767,7 +1350,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';
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
editor.dirty++;
|
||||
}
|
||||
|
||||
@@ -1789,7 +1372,7 @@ row_insert_ch(struct erow *row, int at, int16_t c)
|
||||
row->size++;
|
||||
row->line[at] = c & 0xff;
|
||||
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1802,7 +1385,7 @@ row_delete_ch(struct erow *row, int at)
|
||||
|
||||
memmove(&row->line[at], &row->line[at + 1], row->size - at);
|
||||
row->size--;
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
editor.dirty++;
|
||||
}
|
||||
|
||||
@@ -1834,8 +1417,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;
|
||||
@@ -2261,7 +1844,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 */
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
|
||||
char* search_start = row->render;
|
||||
if (current == start_row && direction == 1 && last_match == -1) {
|
||||
@@ -2355,6 +1938,14 @@ 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;
|
||||
@@ -2378,8 +1969,8 @@ 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;
|
||||
|
||||
row = (editor.cury >= editor.nrows) ? NULL : &editor.row[editor.cury];
|
||||
|
||||
@@ -2516,7 +2107,7 @@ newline(void)
|
||||
row = &editor.row[editor.cury];
|
||||
row->size = editor.curx;
|
||||
row->line[row->size] = '\0';
|
||||
erow_update(row);
|
||||
row->dirty = 1;
|
||||
editor.cury++;
|
||||
editor.curx = 0;
|
||||
}
|
||||
@@ -2741,10 +2332,12 @@ process_kcommand(int16_t c)
|
||||
case 'x':
|
||||
exit(save_file());
|
||||
case 'u':
|
||||
editor_undo();
|
||||
reps = uarg_get();
|
||||
|
||||
while (reps--);
|
||||
editor_set_status("Undo not implemented.");
|
||||
break;
|
||||
case 'U':
|
||||
editor_redo();
|
||||
break;
|
||||
case 'y':
|
||||
reps = uarg_get();
|
||||
@@ -3140,7 +2733,11 @@ draw_rows(struct abuf *ab)
|
||||
}
|
||||
} else {
|
||||
row = &editor.row[filerow];
|
||||
erow_update(row);
|
||||
if (row->dirty) {
|
||||
erow_update(row);
|
||||
row->dirty = 0;
|
||||
}
|
||||
|
||||
len = row->rsize - editor.coloffs;
|
||||
if (len < 0) {
|
||||
len = 0;
|
||||
@@ -3244,11 +2841,15 @@ draw_message_line(struct abuf *ab)
|
||||
void
|
||||
scroll(void)
|
||||
{
|
||||
struct erow *row = NULL;
|
||||
editor.rx = 0;
|
||||
if (editor.cury < editor.nrows) {
|
||||
editor.rx = erow_render_to_cursor(
|
||||
&editor.row[editor.cury],
|
||||
editor.curx);
|
||||
row = &editor.row[editor.cury];
|
||||
if (row->dirty == 1) {
|
||||
erow_update(row);
|
||||
}
|
||||
|
||||
editor.rx = erow_render_to_cursor(row, editor.curx);
|
||||
}
|
||||
|
||||
if (editor.cury < editor.rowoffs) {
|
||||
@@ -3278,6 +2879,7 @@ display_refresh(void)
|
||||
scroll();
|
||||
|
||||
ab_append(&ab, ESCSEQ "?25l", 6);
|
||||
ab_append(&ab, ESCSEQ "H", 3);
|
||||
display_clear(&ab);
|
||||
|
||||
draw_rows(&ab);
|
||||
@@ -3311,6 +2913,15 @@ 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)
|
||||
{
|
||||
@@ -3327,7 +2938,9 @@ loop(void)
|
||||
*
|
||||
*/
|
||||
if ((up = process_keypress()) != 0) {
|
||||
while (process_keypress());
|
||||
while (kbhit()) {
|
||||
process_keypress();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user