Support basic killing/yanking.
This commit is contained in:
302
main.c
302
main.c
@@ -137,6 +137,9 @@ void delete_next_word(void);
|
|||||||
void find_prev_word(void);
|
void find_prev_word(void);
|
||||||
void delete_prev_word(void);
|
void delete_prev_word(void);
|
||||||
void delete_row(int at);
|
void delete_row(int at);
|
||||||
|
void killring_start_with_char(unsigned char ch);
|
||||||
|
void killring_append_char(unsigned char ch);
|
||||||
|
void killring_prepend_char(unsigned char ch);
|
||||||
void killring_flush(void);
|
void killring_flush(void);
|
||||||
void killring_yank(void);
|
void killring_yank(void);
|
||||||
void row_append_row(struct erow *row, char *s, int len);
|
void row_append_row(struct erow *row, char *s, int len);
|
||||||
@@ -207,6 +210,7 @@ struct editor_t {
|
|||||||
struct erow *row;
|
struct erow *row;
|
||||||
struct erow *killring;
|
struct erow *killring;
|
||||||
int killing; /* are we in a contiguous delete sequence? */
|
int killing; /* are we in a contiguous delete sequence? */
|
||||||
|
int suppress_killrow; /* internal flag: don't add to killring inside delete_row */
|
||||||
char *filename;
|
char *filename;
|
||||||
int dirty;
|
int dirty;
|
||||||
int dirtyex;
|
int dirtyex;
|
||||||
@@ -224,6 +228,7 @@ struct editor_t {
|
|||||||
.row = NULL,
|
.row = NULL,
|
||||||
.killring = NULL,
|
.killring = NULL,
|
||||||
.killing = 0,
|
.killing = 0,
|
||||||
|
.suppress_killrow = 0,
|
||||||
.filename = NULL,
|
.filename = NULL,
|
||||||
.dirty = 0,
|
.dirty = 0,
|
||||||
.dirtyex = 0,
|
.dirtyex = 0,
|
||||||
@@ -252,6 +257,8 @@ init_editor(void)
|
|||||||
editor.rowoffs = editor.coloffs = 0;
|
editor.rowoffs = editor.coloffs = 0;
|
||||||
editor.row = NULL;
|
editor.row = NULL;
|
||||||
editor.killring = NULL;
|
editor.killring = NULL;
|
||||||
|
editor.killing = 0;
|
||||||
|
editor.suppress_killrow = 0;
|
||||||
|
|
||||||
editor.msg[0] = '\0';
|
editor.msg[0] = '\0';
|
||||||
editor.msgtm = 0;
|
editor.msgtm = 0;
|
||||||
@@ -277,8 +284,11 @@ reset_editor(void)
|
|||||||
editor.filename = NULL;
|
editor.filename = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
erow_free(editor.killring);
|
if (editor.killring != NULL) {
|
||||||
editor.killring = NULL;
|
erow_free(editor.killring);
|
||||||
|
free(editor.killring);
|
||||||
|
editor.killring = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
init_editor();
|
init_editor();
|
||||||
}
|
}
|
||||||
@@ -322,9 +332,9 @@ nibble_to_hex(char c)
|
|||||||
{
|
{
|
||||||
c &= 0xf;
|
c &= 0xf;
|
||||||
if (c < 10) {
|
if (c < 10) {
|
||||||
return c + 0x30;
|
return (char)('0' + c);
|
||||||
}
|
}
|
||||||
return c + 0x41;
|
return (char)('A' + (c - 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -547,21 +557,99 @@ erow_insert(int at, char *s, int len)
|
|||||||
void
|
void
|
||||||
killring_flush(void)
|
killring_flush(void)
|
||||||
{
|
{
|
||||||
erow_free(editor.killring);
|
if (editor.killring != NULL) {
|
||||||
editor.killring = NULL;
|
erow_free(editor.killring);
|
||||||
|
free(editor.killring);
|
||||||
|
editor.killring = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
killring_yank()
|
killring_yank(void)
|
||||||
{
|
{
|
||||||
if (editor.killring == NULL) {
|
if (editor.killring == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Insert killring contents at the cursor without clearing the ring. */
|
/*
|
||||||
for (int i = 0; i < editor.killring->size; i++) {
|
* Insert killring contents at the cursor without clearing the ring.
|
||||||
insertch((unsigned char)editor.killring->line[i]);
|
* Interpret '\n' as an actual newline() rather than inserting a raw 0x0A
|
||||||
}
|
* byte, so yanked content preserves lines correctly.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < editor.killring->size; i++) {
|
||||||
|
unsigned char ch = (unsigned char)editor.killring->line[i];
|
||||||
|
if (ch == '\n') {
|
||||||
|
newline();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
insertch(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
killring_start_with_char(unsigned char ch)
|
||||||
|
{
|
||||||
|
struct erow *row = NULL;
|
||||||
|
|
||||||
|
if (editor.killring != NULL) {
|
||||||
|
erow_free(editor.killring);
|
||||||
|
free(editor.killring);
|
||||||
|
editor.killring = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.killring = malloc(sizeof(struct erow));
|
||||||
|
assert(editor.killring != NULL);
|
||||||
|
assert(erow_init(editor.killring, 0) == 0);
|
||||||
|
|
||||||
|
/* append one char to empty killring without affecting editor.dirty */
|
||||||
|
row = editor.killring;
|
||||||
|
|
||||||
|
row->line = realloc(row->line, row->size + 2);
|
||||||
|
assert(row->line != NULL);
|
||||||
|
row->line[row->size] = ch;
|
||||||
|
row->size++;
|
||||||
|
row->line[row->size] = '\0';
|
||||||
|
erow_update(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
killring_append_char(unsigned char ch)
|
||||||
|
{
|
||||||
|
struct erow *row = NULL;
|
||||||
|
|
||||||
|
if (editor.killring == NULL) {
|
||||||
|
killring_start_with_char(ch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
row = editor.killring;
|
||||||
|
row->line = realloc(row->line, row->size + 2);
|
||||||
|
assert(row->line != NULL);
|
||||||
|
row->line[row->size] = ch;
|
||||||
|
row->size++;
|
||||||
|
row->line[row->size] = '\0';
|
||||||
|
erow_update(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
killring_prepend_char(unsigned char ch)
|
||||||
|
{
|
||||||
|
if (editor.killring == NULL) {
|
||||||
|
killring_start_with_char(ch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct erow* row = editor.killring;
|
||||||
|
row->line = realloc(row->line, row->size + 2);
|
||||||
|
assert(row->line != NULL);
|
||||||
|
memmove(&row->line[1], &row->line[0], row->size + 1);
|
||||||
|
row->line[0] = ch;
|
||||||
|
row->size++;
|
||||||
|
erow_update(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -676,28 +764,28 @@ find_next_word(void)
|
|||||||
void
|
void
|
||||||
delete_next_word(void)
|
delete_next_word(void)
|
||||||
{
|
{
|
||||||
while (cursor_at_eol()) {
|
while (cursor_at_eol()) {
|
||||||
move_cursor(ARROW_RIGHT);
|
move_cursor(ARROW_RIGHT);
|
||||||
deletech(KILLRING_NO_OP);;
|
deletech(KILLRING_APPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isalnum(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()) {
|
while (!isspace(editor.row[editor.cury].line[editor.curx]) && !cursor_at_eol()) {
|
||||||
move_cursor(ARROW_RIGHT);
|
move_cursor(ARROW_RIGHT);
|
||||||
deletech(KILLRING_NO_OP);;
|
deletech(KILLRING_APPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isspace(editor.row[editor.cury].line[editor.curx])) {
|
if (isspace(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])) {
|
||||||
move_cursor(ARROW_RIGHT);
|
move_cursor(ARROW_RIGHT);
|
||||||
deletech(KILLRING_NO_OP);;
|
deletech(KILLRING_APPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_next_word();
|
delete_next_word();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -730,24 +818,26 @@ delete_prev_word(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
deletech(KILLRING_NO_OP);;
|
deletech(KILLRING_PREPEND);
|
||||||
|
|
||||||
while (editor.cury > 0 || editor.curx > 0) {
|
while (editor.cury > 0 || editor.curx > 0) {
|
||||||
if (editor.curx == 0) {
|
if (editor.curx == 0) {
|
||||||
deletech(KILLRING_NO_OP);;
|
deletech(KILLRING_PREPEND);
|
||||||
} else {
|
continue
|
||||||
if (!isspace(editor.row[editor.cury].line[editor.curx - 1])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
deletech(KILLRING_NO_OP);;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isspace(editor.row[editor.cury].line[editor.curx - 1])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
deletech(KILLRING_PREPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (editor.curx > 0) {
|
while (editor.curx > 0) {
|
||||||
if (isspace(editor.row[editor.cury].line[editor.curx - 1])) {
|
if (isspace(editor.row[editor.cury].line[editor.curx - 1])) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
deletech(KILLRING_NO_OP);;
|
deletech(KILLRING_PREPEND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -758,9 +848,43 @@ delete_row(int at)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update killring with the deleted row's contents followed by a newline
|
||||||
|
* unless this deletion is an internal merge triggered by deletech at
|
||||||
|
* start-of-line. In that case, deletech will account for the single
|
||||||
|
* newline itself and we must NOT also push the entire row here.
|
||||||
|
*/
|
||||||
|
if (!editor.suppress_killrow) {
|
||||||
|
struct erow* r = &editor.row[at];
|
||||||
|
/* Start or continue the kill sequence based on editor.killing */
|
||||||
|
if (r->size > 0) {
|
||||||
|
/* push raw bytes of the line */
|
||||||
|
if (!editor.killing) {
|
||||||
|
killring_start_with_char((unsigned char)r->line[0]);
|
||||||
|
for (int i = 1; i < r->size; i++) {
|
||||||
|
killring_append_char((unsigned char)r->line[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < r->size; i++) {
|
||||||
|
killring_append_char((unsigned char)r->line[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
killring_append_char('\n');
|
||||||
|
editor.killing = 1;
|
||||||
|
} else {
|
||||||
|
if (!editor.killing) {
|
||||||
|
killring_start_with_char('\n');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
killring_append_char('\n');
|
||||||
|
}
|
||||||
|
editor.killing = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
erow_free(&editor.row[at]);
|
erow_free(&editor.row[at]);
|
||||||
memmove(&editor.row[at], &editor.row[at+1],
|
memmove(&editor.row[at], &editor.row[at + 1],
|
||||||
sizeof(struct erow) * (editor.nrows - at - 1));
|
sizeof(struct erow) * (editor.nrows - at - 1));
|
||||||
editor.nrows--;
|
editor.nrows--;
|
||||||
editor.dirty++;
|
editor.dirty++;
|
||||||
}
|
}
|
||||||
@@ -806,6 +930,7 @@ row_delete_ch(struct erow *row, int at)
|
|||||||
if (at < 0 || at >= row->size) {
|
if (at < 0 || at >= row->size) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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--;
|
||||||
erow_update(row);
|
erow_update(row);
|
||||||
@@ -825,9 +950,11 @@ insertch(int16_t c)
|
|||||||
erow_insert(editor.nrows, "", 0);
|
erow_insert(editor.nrows, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Any insertion breaks a delete sequence for killring chaining. */
|
||||||
|
editor.killing = 0;
|
||||||
/* Ensure we pass a non-negative byte value to avoid assert(c > 0). */
|
/* Ensure we pass a non-negative byte value to avoid assert(c > 0). */
|
||||||
row_insert_ch(&editor.row[editor.cury], editor.curx,
|
row_insert_ch(&editor.row[editor.cury], editor.curx,
|
||||||
(int16_t)(c & 0xff));
|
(int16_t)(c & 0xff));
|
||||||
editor.curx++;
|
editor.curx++;
|
||||||
editor.dirty++;
|
editor.dirty++;
|
||||||
}
|
}
|
||||||
@@ -839,7 +966,8 @@ insertch(int16_t c)
|
|||||||
void
|
void
|
||||||
deletech(uint8_t op)
|
deletech(uint8_t op)
|
||||||
{
|
{
|
||||||
struct erow *row = NULL;
|
struct erow* row = NULL;
|
||||||
|
unsigned char dch = 0;
|
||||||
|
|
||||||
if (editor.cury >= editor.nrows) {
|
if (editor.cury >= editor.nrows) {
|
||||||
return;
|
return;
|
||||||
@@ -850,16 +978,60 @@ deletech(uint8_t op)
|
|||||||
}
|
}
|
||||||
|
|
||||||
row = &editor.row[editor.cury];
|
row = &editor.row[editor.cury];
|
||||||
|
|
||||||
|
/* determine which character is being deleted for killring purposes */
|
||||||
|
if (editor.curx > 0) {
|
||||||
|
dch = (unsigned char)row->line[editor.curx - 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* deleting at start of line merges with previous line: treat as newline */
|
||||||
|
dch = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update buffer */
|
||||||
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--;
|
||||||
} 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->line,
|
row_append_row(&editor.row[editor.cury - 1], row->line,
|
||||||
row->size);
|
row->size);
|
||||||
|
int prev = editor.suppress_killrow;
|
||||||
|
editor.suppress_killrow = 1; /* prevent killring update on internal row delete */
|
||||||
delete_row(editor.cury);
|
delete_row(editor.cury);
|
||||||
|
editor.suppress_killrow = prev;
|
||||||
editor.cury--;
|
editor.cury--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* update killring if requested */
|
||||||
|
if (op == KILLRING_FLUSH) {
|
||||||
|
killring_flush();
|
||||||
|
editor.killing = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == KILLRING_NO_OP) {
|
||||||
|
/* Do not modify killring or chaining state. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!editor.killing) {
|
||||||
|
killring_start_with_char(dch);
|
||||||
|
editor.killing = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == KILLING_SET) {
|
||||||
|
killring_start_with_char(dch);
|
||||||
|
editor.killing = 1;
|
||||||
|
}
|
||||||
|
else if (op == KILLRING_APPEND) {
|
||||||
|
killring_append_char(dch);
|
||||||
|
}
|
||||||
|
else if (op == KILLRING_PREPEND) {
|
||||||
|
killring_prepend_char(dch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1338,22 +1510,24 @@ move_cursor(int16_t c)
|
|||||||
void
|
void
|
||||||
newline(void)
|
newline(void)
|
||||||
{
|
{
|
||||||
struct erow *row = NULL;
|
struct erow *row = NULL;
|
||||||
|
|
||||||
if (editor.curx == 0) {
|
if (editor.curx == 0) {
|
||||||
erow_insert(editor.cury, "", 0);
|
erow_insert(editor.cury, "", 0);
|
||||||
} else {
|
} else {
|
||||||
row = &editor.row[editor.cury];
|
row = &editor.row[editor.cury];
|
||||||
erow_insert(editor.cury + 1, &row->line[editor.curx],
|
erow_insert(editor.cury + 1, &row->line[editor.curx],
|
||||||
row->size - editor.curx);
|
row->size - editor.curx);
|
||||||
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';
|
||||||
erow_update(row);
|
erow_update(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.cury++;
|
editor.cury++;
|
||||||
editor.curx = 0;
|
editor.curx = 0;
|
||||||
|
/* Any insertion breaks a delete sequence for killring chaining. */
|
||||||
|
editor.killing = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1439,6 +1613,8 @@ void
|
|||||||
process_normal(int16_t c)
|
process_normal(int16_t c)
|
||||||
{
|
{
|
||||||
if (is_arrow_key(c)) {
|
if (is_arrow_key(c)) {
|
||||||
|
/* moving the cursor breaks a delete sequence */
|
||||||
|
editor.killing = 0;
|
||||||
move_cursor(c);
|
move_cursor(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1456,9 +1632,11 @@ process_normal(int16_t c)
|
|||||||
case DEL_KEY:
|
case DEL_KEY:
|
||||||
if (c == DEL_KEY || c == CTRL_KEY('d')) {
|
if (c == DEL_KEY || c == CTRL_KEY('d')) {
|
||||||
move_cursor(ARROW_RIGHT);
|
move_cursor(ARROW_RIGHT);
|
||||||
|
deletech(KILLRING_APPEND);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deletech(KILLRING_PREPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
deletech(KILLRING_NO_OP);;
|
|
||||||
break;
|
break;
|
||||||
case CTRL_KEY('l'):
|
case CTRL_KEY('l'):
|
||||||
display_refresh();
|
display_refresh();
|
||||||
|
|||||||
Reference in New Issue
Block a user