Files
ke/killring.cc
2025-11-25 09:17:50 -08:00

263 lines
6.2 KiB
C++

#include "killring.h"
#include <termios.h>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <ctime>
// Need access to main.cc structures and functions
extern int erow_init(struct erow *row, int len);
extern void erow_update(struct erow *row);
extern void erow_free(struct erow *row);
extern void editor_set_status(const char *fmt, ...);
extern void insertch(int16_t c);
extern void newline();
extern void move_cursor(int16_t c);
extern int cursor_at_eol();
extern void swap_int(int *a, int *b);
extern void deletech(uint8_t op);
// erow definition
struct erow {
char *line;
char *render;
int size;
int rsize;
int cap;
};
// editor_t definition
struct editor_t {
struct termios entry_term;
int rows, cols;
int curx, cury;
int rx;
int mode;
int nrows;
int rowoffs, coloffs;
struct erow *row;
struct erow *killring;
int kill;
int no_kill;
char *filename;
int dirty;
int dirtyex;
char msg[80];
int mark_set;
int mark_curx, mark_cury;
time_t msgtm;
};
namespace ke {
void Killring::flush(editor_t* editor) {
if (editor->killring != nullptr) {
erow_free(editor->killring);
free(editor->killring);
editor->killring = nullptr;
}
}
void Killring::yank(editor_t* editor) {
if (editor->killring == nullptr) {
return;
}
/*
* Insert killring contents at the cursor without clearing the ring.
* 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(editor_t* editor, unsigned char ch) {
erow* row = nullptr;
if (editor->killring != nullptr) {
erow_free(editor->killring);
free(editor->killring);
editor->killring = nullptr;
}
editor->killring = static_cast<erow*>(malloc(sizeof(erow)));
assert(editor->killring != nullptr);
assert(erow_init(editor->killring, 0) == 0);
/* append one char to empty killring without affecting editor.dirty */
row = editor->killring;
row->line = static_cast<char*>(realloc(row->line, row->size + 2));
assert(row->line != nullptr);
row->line[row->size] = ch;
row->size++;
row->line[row->size] = '\0';
erow_update(row);
}
void Killring::append_char(editor_t* editor, unsigned char ch) {
erow* row = nullptr;
if (editor->killring == nullptr) {
start_with_char(editor, ch);
return;
}
row = editor->killring;
row->line = static_cast<char*>(realloc(row->line, row->size + 2));
assert(row->line != nullptr);
row->line[row->size] = ch;
row->size++;
row->line[row->size] = '\0';
erow_update(row);
}
void Killring::prepend_char(editor_t* editor, unsigned char ch) {
if (editor->killring == nullptr) {
start_with_char(editor, ch);
return;
}
erow* row = editor->killring;
row->line = static_cast<char*>(realloc(row->line, row->size + 2));
assert(row->line != nullptr);
memmove(&row->line[1], &row->line[0], row->size + 1);
row->line[0] = ch;
row->size++;
erow_update(row);
}
void Killring::toggle_markset(editor_t* editor) {
if (editor->mark_set) {
editor->mark_set = 0;
editor_set_status("Mark cleared.");
return;
}
editor->mark_set = 1;
editor->mark_curx = editor->curx;
editor->mark_cury = editor->cury;
editor_set_status("Mark set.");
}
int Killring::cursor_after_mark(editor_t* editor) {
if (editor->mark_cury < editor->cury) {
return 1;
}
if (editor->mark_cury > editor->cury) {
return 0;
}
return editor->curx >= editor->mark_curx;
}
int Killring::count_chars_from_cursor_to_mark(editor_t* editor) {
int count = 0;
int curx = editor->curx;
int cury = editor->cury;
int markx = editor->mark_curx;
int marky = editor->mark_cury;
if (!cursor_after_mark(editor)) {
swap_int(&curx, &markx);
swap_int(&cury, &marky);
}
editor->curx = markx;
editor->cury = marky;
while (editor->cury != cury) {
while (!cursor_at_eol()) {
move_cursor(1000 + 3); // ARROW_RIGHT
count++;
}
move_cursor(1000 + 3); // ARROW_RIGHT
count++;
}
while (editor->curx != curx) {
count++;
move_cursor(1000 + 3); // ARROW_RIGHT
}
return count;
}
void Killring::kill_region(editor_t* editor) {
int curx = editor->curx;
int cury = editor->cury;
int markx = editor->mark_curx;
int marky = editor->mark_cury;
if (!editor->mark_set) {
return;
}
/* kill the current killring */
flush(editor);
if (!cursor_after_mark(editor)) {
swap_int(&curx, &markx);
swap_int(&cury, &marky);
}
editor->curx = markx;
editor->cury = marky;
while (editor->cury != cury) {
while (!cursor_at_eol()) {
append_char(editor, editor->row[editor->cury].line[editor->curx]);
move_cursor(1000 + 3); // ARROW_RIGHT
}
append_char(editor, '\n');
move_cursor(1000 + 3); // ARROW_RIGHT
}
while (editor->curx != curx) {
append_char(editor, editor->row[editor->cury].line[editor->curx]);
move_cursor(1000 + 3); // ARROW_RIGHT
}
editor_set_status("Region killed.");
}
void Killring::delete_region(editor_t* editor) {
int count = count_chars_from_cursor_to_mark(editor);
int killed = 0;
int curx = editor->curx;
int cury = editor->cury;
int markx = editor->mark_curx;
int marky = editor->mark_cury;
if (!editor->mark_set) {
return;
}
if (!cursor_after_mark(editor)) {
swap_int(&curx, &markx);
swap_int(&cury, &marky);
}
editor->curx = markx;
editor->cury = marky;
while (killed < count) {
deletech(0); // KILLRING_NO_OP
killed++;
}
editor->kill = 1;
editor_set_status("Region killed.");
}
} // namespace ke