editor: add shadow-lines and region-deletes
This implements the "copy" portion of copy-and-paste and the "delete" portion of cut-and-paste. 'backspace' was also partially refactored to share code with the new 'selection_delete'. I am excited in how expressiveness/readability improved in 'backspace' after selection_delete was implemented.
This commit is contained in:
parent
d313bdd855
commit
6a5ebab01f
2
Makefile
2
Makefile
@ -1,5 +1,5 @@
|
|||||||
CFLAGS = -Isaturn
|
CFLAGS = -Isaturn
|
||||||
OPT = -Og
|
OPT ?= -Og
|
||||||
LIBGCC = $(shell $(CC) -print-file-name=libgcc.a)
|
LIBGCC = $(shell $(CC) -print-file-name=libgcc.a)
|
||||||
LIB = ./saturn
|
LIB = ./saturn
|
||||||
|
|
||||||
|
@ -10,12 +10,15 @@ namespace editor {
|
|||||||
template <int C>
|
template <int C>
|
||||||
struct line {
|
struct line {
|
||||||
uint8_t buf[C];
|
uint8_t buf[C];
|
||||||
int32_t length;
|
int16_t length;
|
||||||
|
int16_t refcount;
|
||||||
|
|
||||||
|
static_assert(C < 32768);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cursor {
|
struct cursor {
|
||||||
int32_t row;
|
|
||||||
int32_t col;
|
int32_t col;
|
||||||
|
int32_t row;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum struct mode {
|
enum struct mode {
|
||||||
@ -23,11 +26,38 @@ enum struct mode {
|
|||||||
mark
|
mark
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct selection {
|
||||||
|
cursor * min;
|
||||||
|
cursor * max;
|
||||||
|
|
||||||
|
inline constexpr bool contains(int32_t col, int32_t row);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr bool selection::contains(int32_t col, int32_t row)
|
||||||
|
{
|
||||||
|
if (this->min != this->max) {
|
||||||
|
if (row == this->min->row && row == this->max->row) {
|
||||||
|
if (col >= this->min->col && col < this->max->col)
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if ( (row == this->min->row && col >= this->min->col)
|
||||||
|
|| (row == this->max->row && col < this->max->col)
|
||||||
|
|| (row > this->min->row && row < this->max->row))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
template <int C, int R>
|
template <int C, int R>
|
||||||
struct buffer {
|
struct buffer {
|
||||||
line<C> row[R];
|
line<C> row[R];
|
||||||
line<C> * lines[R];
|
line<C> * lines[R];
|
||||||
int32_t length;
|
int32_t length;
|
||||||
|
struct {
|
||||||
|
line<C> * lines[R];
|
||||||
|
int32_t length;
|
||||||
|
} shadow;
|
||||||
int32_t alloc_ix;
|
int32_t alloc_ix;
|
||||||
struct {
|
struct {
|
||||||
int32_t cell_width;
|
int32_t cell_width;
|
||||||
@ -46,7 +76,12 @@ struct buffer {
|
|||||||
|
|
||||||
inline constexpr buffer(int32_t width, int32_t height);
|
inline constexpr buffer(int32_t width, int32_t height);
|
||||||
inline constexpr line<C> * allocate();
|
inline constexpr line<C> * allocate();
|
||||||
inline constexpr void deallocate(line<C> *& l);
|
inline static constexpr void deallocate(line<C> *& l);
|
||||||
|
inline static constexpr void decref(line<C> *& l);
|
||||||
|
inline constexpr line<C> * dup(line<C> const * const l);
|
||||||
|
inline constexpr line<C> * mutref(line<C> *& l);
|
||||||
|
inline static constexpr line<C> * incref(line<C> * l);
|
||||||
|
inline static constexpr int32_t line_length(line<C> const * const l);
|
||||||
inline constexpr bool put(uint8_t c);
|
inline constexpr bool put(uint8_t c);
|
||||||
inline constexpr bool backspace();
|
inline constexpr bool backspace();
|
||||||
inline constexpr bool cursor_left();
|
inline constexpr bool cursor_left();
|
||||||
@ -56,9 +91,21 @@ struct buffer {
|
|||||||
inline constexpr bool cursor_home();
|
inline constexpr bool cursor_home();
|
||||||
inline constexpr bool cursor_end();
|
inline constexpr bool cursor_end();
|
||||||
inline constexpr bool enter();
|
inline constexpr bool enter();
|
||||||
inline constexpr void set_mark();
|
inline constexpr void mark_set();
|
||||||
|
inline constexpr selection mark_get();
|
||||||
|
inline constexpr void delete_from_line(line<C> *& l,
|
||||||
|
const int32_t col_start_ix,
|
||||||
|
const int32_t col_end_ix);
|
||||||
|
inline constexpr void selection_delete(const selection& sel);
|
||||||
|
inline constexpr bool mark_delete();
|
||||||
inline constexpr void quit();
|
inline constexpr void quit();
|
||||||
private:
|
inline constexpr void shadow_clear();
|
||||||
|
inline constexpr void _shadow_cow(line<C> * src,
|
||||||
|
const int32_t dst_row_ix,
|
||||||
|
const int32_t col_start_ix,
|
||||||
|
const int32_t col_end_ix);
|
||||||
|
inline constexpr bool shadow_copy();
|
||||||
|
inline constexpr bool shadow_cut();
|
||||||
inline constexpr void scroll_left();
|
inline constexpr void scroll_left();
|
||||||
inline constexpr void scroll_right();
|
inline constexpr void scroll_right();
|
||||||
inline constexpr void scroll_up();
|
inline constexpr void scroll_up();
|
||||||
@ -69,6 +116,7 @@ template <int C, int R>
|
|||||||
inline constexpr buffer<C, R>::buffer(int32_t width, int32_t height)
|
inline constexpr buffer<C, R>::buffer(int32_t width, int32_t height)
|
||||||
{
|
{
|
||||||
this->length = 1;
|
this->length = 1;
|
||||||
|
this->shadow.length = 1;
|
||||||
this->alloc_ix = 0;
|
this->alloc_ix = 0;
|
||||||
this->window.top = 0;
|
this->window.top = 0;
|
||||||
this->window.left = 0;
|
this->window.left = 0;
|
||||||
@ -80,6 +128,7 @@ inline constexpr buffer<C, R>::buffer(int32_t width, int32_t height)
|
|||||||
for (int32_t i = 0; i < R; i++) {
|
for (int32_t i = 0; i < R; i++) {
|
||||||
this->row[i].length = -1;
|
this->row[i].length = -1;
|
||||||
this->lines[i] = nullptr;
|
this->lines[i] = nullptr;
|
||||||
|
this->shadow.lines[i] = nullptr;
|
||||||
|
|
||||||
for (int32_t j = 0; j < C; j++) {
|
for (int32_t j = 0; j < C; j++) {
|
||||||
this->row[i].buf[j] = 0x7f;
|
this->row[i].buf[j] = 0x7f;
|
||||||
@ -97,6 +146,7 @@ inline constexpr line<C> * buffer<C, R>::allocate()
|
|||||||
this->alloc_ix = (this->alloc_ix + 1) & (R - 1);
|
this->alloc_ix = (this->alloc_ix + 1) & (R - 1);
|
||||||
}
|
}
|
||||||
l->length = 0;
|
l->length = 0;
|
||||||
|
l->refcount = 1;
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,17 +156,62 @@ inline constexpr void buffer<C, R>::deallocate(line<C> *& l)
|
|||||||
// does not touch alloc_ix
|
// does not touch alloc_ix
|
||||||
fill<uint8_t>(l->buf, 0x7f, l->length);
|
fill<uint8_t>(l->buf, 0x7f, l->length);
|
||||||
l->length = -1;
|
l->length = -1;
|
||||||
|
l->refcount = 0;
|
||||||
l = nullptr;
|
l = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::decref(line<C> *& l)
|
||||||
|
{
|
||||||
|
if (l->refcount == 1)
|
||||||
|
buffer<C, R>::deallocate(l);
|
||||||
|
else {
|
||||||
|
l->refcount--;
|
||||||
|
l = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr line<C> * buffer<C, R>::dup(line<C> const * const src)
|
||||||
|
{
|
||||||
|
line<C> * dst = this->allocate();
|
||||||
|
copy(&dst->buf[0], &src->buf[0], src->length);
|
||||||
|
dst->length = src->length;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr line<C> * buffer<C, R>::mutref(line<C> *& l)
|
||||||
|
{
|
||||||
|
if (l == nullptr) {
|
||||||
|
l = this->allocate();
|
||||||
|
} else if (l->refcount > 1) {
|
||||||
|
// copy-on-write
|
||||||
|
l->refcount--;
|
||||||
|
l = this->dup(l);
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr line<C> * buffer<C, R>::incref(line<C> * l)
|
||||||
|
{
|
||||||
|
if (l != nullptr) l->refcount++;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr int32_t buffer<C, R>::line_length(line<C> const * const l)
|
||||||
|
{
|
||||||
|
if (l == nullptr) return 0;
|
||||||
|
else return l->length;
|
||||||
|
}
|
||||||
|
|
||||||
template <int C, int R>
|
template <int C, int R>
|
||||||
inline constexpr bool buffer<C, R>::put(const uint8_t c)
|
inline constexpr bool buffer<C, R>::put(const uint8_t c)
|
||||||
{
|
{
|
||||||
struct cursor& cur = this->cursor;
|
struct cursor& cur = this->cursor;
|
||||||
line<C> *& l = this->lines[cur.row];
|
line<C> * l = mutref(this->lines[cur.row]);
|
||||||
if (l == nullptr) {
|
|
||||||
l = this->allocate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// v
|
// v
|
||||||
// 0123
|
// 0123
|
||||||
@ -141,47 +236,36 @@ inline constexpr bool buffer<C, R>::put(const uint8_t c)
|
|||||||
template <int C, int R>
|
template <int C, int R>
|
||||||
inline constexpr bool buffer<C, R>::backspace()
|
inline constexpr bool buffer<C, R>::backspace()
|
||||||
{
|
{
|
||||||
|
if (this->mode == mode::mark) {
|
||||||
|
return mark_delete();
|
||||||
|
}
|
||||||
|
|
||||||
struct cursor& cur = this->cursor;
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
if (cur.col < 0 || cur.col > C)
|
if (cur.col < 0 || cur.col > C)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
line<C> *& l = this->lines[cur.row];
|
|
||||||
if (l == nullptr) {
|
|
||||||
// is it possible to get in this state?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (l->length < 0) {
|
|
||||||
// is it possible to get in this state?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur.col == 0) {
|
if (cur.col == 0) {
|
||||||
if (cur.row == 0) return false;
|
if (cur.row == 0) return false;
|
||||||
// combine this line with the previous line
|
// make selection
|
||||||
line<C> *& lp = this->lines[cur.row-1];
|
struct cursor min { line_length(this->lines[cur.row-1]), cur.row-1 };
|
||||||
if (lp == nullptr) lp = allocate();
|
struct cursor max { cur.col, cur.row };
|
||||||
// blindly truncate overflowing lines
|
selection sel { &min, &max };
|
||||||
auto length = min(l->length - cur.col, C - lp->length);
|
selection_delete(sel);
|
||||||
if (length > 0) move(&lp->buf[lp->length], &l->buf[cur.col], length);
|
|
||||||
|
|
||||||
cur.col = lp->length;
|
|
||||||
scroll_right();
|
|
||||||
lp->length += length;
|
|
||||||
deallocate(l);
|
|
||||||
// 0 a
|
|
||||||
// 1
|
|
||||||
// 2 _ (cur.row, deleted)
|
|
||||||
// 3 b
|
|
||||||
int32_t n_lines = this->length - (cur.row + 1);
|
|
||||||
move(&this->lines[cur.row], &this->lines[cur.row+1], (sizeof (line<C>*)) * n_lines);
|
|
||||||
this->length--;
|
|
||||||
this->lines[this->length] = nullptr;
|
|
||||||
cur.row--;
|
cur.row--;
|
||||||
scroll_up();
|
scroll_up();
|
||||||
|
if (min.col < cur.col) {
|
||||||
|
cur.col = min.col;
|
||||||
|
scroll_right();
|
||||||
|
} else {
|
||||||
|
cur.col = min.col;
|
||||||
|
scroll_left();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// c
|
// c
|
||||||
// 01234
|
// 01234
|
||||||
|
line<C> * l = mutref(this->lines[cur.row]);
|
||||||
auto length = l->length - cur.col;
|
auto length = l->length - cur.col;
|
||||||
if (length > 0) move(&l->buf[cur.col-1], &l->buf[cur.col], length);
|
if (length > 0) move(&l->buf[cur.col-1], &l->buf[cur.col], length);
|
||||||
|
|
||||||
@ -207,8 +291,7 @@ inline constexpr bool buffer<C, R>::cursor_left()
|
|||||||
scroll_up();
|
scroll_up();
|
||||||
|
|
||||||
line<C> const * const l = this->lines[cur.row];
|
line<C> const * const l = this->lines[cur.row];
|
||||||
int32_t length = (l == nullptr) ? 0 : l->length;
|
cur.col = line_length(l);
|
||||||
cur.col = length;
|
|
||||||
scroll_right();
|
scroll_right();
|
||||||
} else {
|
} else {
|
||||||
cur.col--;
|
cur.col--;
|
||||||
@ -224,7 +307,7 @@ inline constexpr bool buffer<C, R>::cursor_right()
|
|||||||
struct cursor& cur = this->cursor;
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
line<C> const * const l = this->lines[cur.row];
|
line<C> const * const l = this->lines[cur.row];
|
||||||
int32_t length = (l == nullptr) ? 0 : l->length;
|
int32_t length = line_length(l);
|
||||||
if (cur.col >= length) {
|
if (cur.col >= length) {
|
||||||
if (cur.row >= (this->length - 1))
|
if (cur.row >= (this->length - 1))
|
||||||
return false;
|
return false;
|
||||||
@ -254,8 +337,7 @@ inline constexpr bool buffer<C, R>::cursor_up()
|
|||||||
scroll_up();
|
scroll_up();
|
||||||
|
|
||||||
line<C> const * const l = this->lines[cur.row];
|
line<C> const * const l = this->lines[cur.row];
|
||||||
int32_t length = (l == nullptr) ? 0 : l->length;
|
cur.col = min(cur.col, line_length(l));
|
||||||
cur.col = min(cur.col, length);
|
|
||||||
scroll_left();
|
scroll_left();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -272,8 +354,7 @@ inline constexpr bool buffer<C, R>::cursor_down()
|
|||||||
scroll_down();
|
scroll_down();
|
||||||
|
|
||||||
line<C> const * const l = this->lines[cur.row];
|
line<C> const * const l = this->lines[cur.row];
|
||||||
int32_t length = (l == nullptr) ? 0 : l->length;
|
cur.col = min(cur.col, line_length(l));
|
||||||
cur.col = min(cur.col, length);
|
|
||||||
scroll_left();
|
scroll_left();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -282,11 +363,6 @@ template <int C, int R>
|
|||||||
inline constexpr bool buffer<C, R>::cursor_home()
|
inline constexpr bool buffer<C, R>::cursor_home()
|
||||||
{
|
{
|
||||||
struct cursor& cur = this->cursor;
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
line<C> const * const l = this->lines[cur.row];
|
|
||||||
if (l == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
cur.col = 0;
|
cur.col = 0;
|
||||||
scroll_left();
|
scroll_left();
|
||||||
return true;
|
return true;
|
||||||
@ -297,11 +373,7 @@ inline constexpr bool buffer<C, R>::cursor_end()
|
|||||||
{
|
{
|
||||||
struct cursor& cur = this->cursor;
|
struct cursor& cur = this->cursor;
|
||||||
|
|
||||||
line<C> const * const l = this->lines[cur.row];
|
cur.col = line_length(this->lines[cur.row]);
|
||||||
if (l == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
cur.col = l->length;
|
|
||||||
scroll_right();
|
scroll_right();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -321,13 +393,14 @@ inline constexpr bool buffer<C, R>::enter()
|
|||||||
move(&this->lines[cur.row+2], &this->lines[cur.row+1], (sizeof (line<C>*)) * n_lines);
|
move(&this->lines[cur.row+2], &this->lines[cur.row+1], (sizeof (line<C>*)) * n_lines);
|
||||||
}
|
}
|
||||||
// column-wise copy of the cursor position to the newly-created line
|
// column-wise copy of the cursor position to the newly-created line
|
||||||
line<C> * old_l = this->lines[cur.row];
|
line<C> * new_l = this->allocate();
|
||||||
line<C> * new_l = allocate();
|
line<C> *& old_l = this->lines[cur.row];
|
||||||
// v
|
// v
|
||||||
// 01234 (5)
|
// 01234 (5)
|
||||||
if (old_l != nullptr) {
|
if (old_l != nullptr) {
|
||||||
new_l->length = old_l->length - cur.col;
|
new_l->length = old_l->length - cur.col;
|
||||||
if (new_l->length > 0) {
|
if (new_l->length > 0) {
|
||||||
|
old_l = mutref(old_l);
|
||||||
old_l->length -= new_l->length;
|
old_l->length -= new_l->length;
|
||||||
copy(&new_l->buf[0], &old_l->buf[cur.col], new_l->length);
|
copy(&new_l->buf[0], &old_l->buf[cur.col], new_l->length);
|
||||||
fill<uint8_t>(&old_l->buf[cur.col], 0x7f, new_l->length);
|
fill<uint8_t>(&old_l->buf[cur.col], 0x7f, new_l->length);
|
||||||
@ -348,19 +421,230 @@ inline constexpr bool buffer<C, R>::enter()
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <int C, int R>
|
template <int C, int R>
|
||||||
inline constexpr void buffer<C, R>::set_mark()
|
inline constexpr void buffer<C, R>::mark_set()
|
||||||
{
|
{
|
||||||
this->mark.row = this->cursor.row;
|
this->mark.row = this->cursor.row;
|
||||||
this->mark.col = this->cursor.col;
|
this->mark.col = this->cursor.col;
|
||||||
this->mode = mode::mark;
|
this->mode = mode::mark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr selection buffer<C, R>::mark_get()
|
||||||
|
{
|
||||||
|
editor::cursor& cur = this->cursor;
|
||||||
|
editor::cursor& mark = this->mark;
|
||||||
|
selection sel;
|
||||||
|
|
||||||
|
if (cur.row == mark.row) {
|
||||||
|
if (cur.col == mark.col) {
|
||||||
|
sel.min = sel.max = &cur;
|
||||||
|
} else if (cur.col < mark.col) {
|
||||||
|
sel.min = &cur;
|
||||||
|
sel.max = &mark;
|
||||||
|
} else {
|
||||||
|
sel.min = &mark;
|
||||||
|
sel.max = &cur;
|
||||||
|
}
|
||||||
|
} else if (cur.row < mark.row) {
|
||||||
|
sel.min = &cur;
|
||||||
|
sel.max = &mark;
|
||||||
|
} else {
|
||||||
|
sel.min = &mark;
|
||||||
|
sel.max = &cur;
|
||||||
|
}
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::delete_from_line(line<C> *& l,
|
||||||
|
const int32_t col_start_ix,
|
||||||
|
const int32_t col_end_ix)
|
||||||
|
{
|
||||||
|
if (l == nullptr) return;
|
||||||
|
else if (col_start_ix == 0 && col_end_ix == l->length)
|
||||||
|
decref(l);
|
||||||
|
else if (col_end_ix == l->length)
|
||||||
|
mutref(l)->length = col_start_ix;
|
||||||
|
else {
|
||||||
|
// S E
|
||||||
|
// 0123456(7)
|
||||||
|
// 0156
|
||||||
|
l = mutref(l);
|
||||||
|
int32_t length = l->length - col_end_ix;
|
||||||
|
move(&l->buf[col_start_ix], &l->buf[col_end_ix], length);
|
||||||
|
l->length = col_start_ix + length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::selection_delete(const selection& sel)
|
||||||
|
{
|
||||||
|
if (sel.min == sel.max) {
|
||||||
|
return;
|
||||||
|
} else if (sel.min->row == sel.max->row) {
|
||||||
|
// delete from min.col to max.col (excluding max.col)
|
||||||
|
delete_from_line(this->lines[sel.min->row], sel.min->col, sel.max->col);
|
||||||
|
} else {
|
||||||
|
// decref all lines between min.row and max.row, exclusive
|
||||||
|
for (int32_t ix = (sel.min->row + 1); ix < (sel.max->row); ix++) {
|
||||||
|
decref(this->lines[ix]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->lines[sel.max->row] != nullptr) {
|
||||||
|
// combine min/max; truncating overflow
|
||||||
|
line<C> * lmin = mutref(this->lines[sel.min->row]);
|
||||||
|
const line<C> * lmax = this->lines[sel.max->row];
|
||||||
|
|
||||||
|
// v
|
||||||
|
// 0123
|
||||||
|
// v
|
||||||
|
// abcd(4)
|
||||||
|
|
||||||
|
// 0cd
|
||||||
|
|
||||||
|
lmin->length -= (lmin->length - sel.min->col);
|
||||||
|
int32_t move_length = min(static_cast<int32_t>(lmax->length - sel.max->col),
|
||||||
|
static_cast<int32_t>(C - lmin->length));
|
||||||
|
if (move_length > 0)
|
||||||
|
move(&lmin->buf[sel.min->col],
|
||||||
|
&lmax->buf[sel.max->col],
|
||||||
|
move_length);
|
||||||
|
lmin->length += move_length;
|
||||||
|
|
||||||
|
// delete max
|
||||||
|
decref(this->lines[sel.max->row]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// shift rows up
|
||||||
|
int32_t n_lines = this->length - (sel.max->row + 1);
|
||||||
|
move(&this->lines[sel.min->row + 1],
|
||||||
|
&this->lines[sel.max->row + 1],
|
||||||
|
(sizeof (line<C>*)) * n_lines);
|
||||||
|
|
||||||
|
int32_t deleted_rows = (sel.max->row - sel.min->row);
|
||||||
|
|
||||||
|
// don't decref here -- the references were just moved
|
||||||
|
for (int32_t ix = this->length - deleted_rows; ix < this->length; ix++) {
|
||||||
|
this->lines[ix] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->length -= deleted_rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::mark_delete()
|
||||||
|
{
|
||||||
|
if (this->mode != mode::mark)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const selection sel = mark_get();
|
||||||
|
this->selection_delete(sel);
|
||||||
|
|
||||||
|
// move cur to sel.min
|
||||||
|
editor::cursor& cur = this->cursor;
|
||||||
|
const editor::cursor& min = *sel.min;
|
||||||
|
if (min.row < cur.row) { cur.row = min.row; scroll_up(); }
|
||||||
|
else { cur.row = min.row; scroll_down(); }
|
||||||
|
|
||||||
|
if (min.col < cur.col) { cur.col = min.col; scroll_right(); }
|
||||||
|
else { cur.col = min.col; scroll_left(); }
|
||||||
|
|
||||||
|
this->mode = mode::normal;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
template <int C, int R>
|
template <int C, int R>
|
||||||
inline constexpr void buffer<C, R>::quit()
|
inline constexpr void buffer<C, R>::quit()
|
||||||
{
|
{
|
||||||
this->mode = mode::normal;
|
this->mode = mode::normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::shadow_clear()
|
||||||
|
{
|
||||||
|
for (int32_t i = 0; i < this->shadow.length; i++)
|
||||||
|
if (this->shadow.lines[i] != nullptr)
|
||||||
|
decref(this->shadow.lines[i]);
|
||||||
|
|
||||||
|
this->shadow.length = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr void buffer<C, R>::_shadow_cow(line<C> * src,
|
||||||
|
const int32_t dst_row_ix,
|
||||||
|
const int32_t col_start_ix,
|
||||||
|
const int32_t col_end_ix)
|
||||||
|
{
|
||||||
|
if (src == nullptr || (col_start_ix == col_end_ix)) {
|
||||||
|
this->shadow.lines[dst_row_ix] = nullptr;
|
||||||
|
} else if (col_start_ix == 0 && col_end_ix == src->length) {
|
||||||
|
this->shadow.lines[dst_row_ix] = incref(src);
|
||||||
|
} else {
|
||||||
|
line<C> * dst = this->allocate();
|
||||||
|
dst->length = col_end_ix - col_start_ix;
|
||||||
|
copy(&dst->buf[0], &src->buf[col_start_ix], dst->length);
|
||||||
|
this->shadow.lines[dst_row_ix] = dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::shadow_copy()
|
||||||
|
{
|
||||||
|
if (this->mode != mode::mark)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this->shadow_clear();
|
||||||
|
|
||||||
|
selection sel = this->mark_get();
|
||||||
|
|
||||||
|
this->shadow.length = (sel.max->row - sel.min->row) + 1;
|
||||||
|
|
||||||
|
if (sel.min == sel.max) {
|
||||||
|
this->shadow.lines[0] = nullptr;
|
||||||
|
return true;
|
||||||
|
} else if (sel.min->row == sel.max->row) {
|
||||||
|
// copy from min.col to max.col (excluding max.col)
|
||||||
|
_shadow_cow(this->lines[sel.min->row],
|
||||||
|
0, // dst_row_ix
|
||||||
|
sel.min->col, // col_start_ix
|
||||||
|
sel.max->col); // col_end_ix
|
||||||
|
} else {
|
||||||
|
line<C> * src = this->lines[sel.min->row];
|
||||||
|
// copy from min.col to the end of the line (including min.col)
|
||||||
|
_shadow_cow(src,
|
||||||
|
0, // dst_row_ix
|
||||||
|
sel.min->col, // col_start_ix
|
||||||
|
line_length(src)); // col_end_ix
|
||||||
|
|
||||||
|
int32_t shadow_ix = 1;
|
||||||
|
for (int32_t ix = (sel.min->row + 1); ix < (sel.max->row); ix++) {
|
||||||
|
// cow all lines between min.row and max.row, exclusive
|
||||||
|
this->shadow.lines[shadow_ix++] = incref(this->lines[ix]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy from the beginning of the line to max.col (excluding max.col)
|
||||||
|
_shadow_cow(this->lines[sel.max->row],
|
||||||
|
shadow_ix, // row_ix
|
||||||
|
0, // col_start_ix
|
||||||
|
sel.max->col); // col_end_ix
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int C, int R>
|
||||||
|
inline constexpr bool buffer<C, R>::shadow_cut()
|
||||||
|
{
|
||||||
|
if (this->mode != mode::mark)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this->shadow_copy();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
template <int C, int R>
|
template <int C, int R>
|
||||||
inline constexpr void buffer<C, R>::scroll_up()
|
inline constexpr void buffer<C, R>::scroll_up()
|
||||||
{
|
{
|
||||||
@ -402,5 +686,4 @@ inline constexpr void buffer<C, R>::scroll_right()
|
|||||||
if (this->cursor.col >= this->window.left + this->window.cell_width)
|
if (this->cursor.col >= this->window.left + this->window.cell_width)
|
||||||
this->window.left = (this->cursor.col - (this->window.cell_width - 1));
|
this->window.left = (this->cursor.col - (this->window.cell_width - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ void palette_data()
|
|||||||
vdp2.cram.u16[1 + 16] = rgb15(31, 31, 31);
|
vdp2.cram.u16[1 + 16] = rgb15(31, 31, 31);
|
||||||
vdp2.cram.u16[2 + 16] = rgb15( 0, 0, 0);
|
vdp2.cram.u16[2 + 16] = rgb15( 0, 0, 0);
|
||||||
|
|
||||||
vdp2.cram.u16[1 + 32] = rgb15(15, 15, 15);
|
vdp2.cram.u16[1 + 32] = rgb15(10, 10, 10);
|
||||||
vdp2.cram.u16[2 + 32] = rgb15(31, 31, 31);
|
vdp2.cram.u16[2 + 32] = rgb15(31, 31, 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ inline void keyboard_regular_key(const enum keysym k)
|
|||||||
case keysym::ENTER : buffer.enter(); break;
|
case keysym::ENTER : buffer.enter(); break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
uint8_t c = keysym_to_char(k, false);
|
int32_t c = keysym_to_char(k, false);
|
||||||
if (c != -1) buffer.put(c);
|
if (c != -1) buffer.put(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -115,7 +115,7 @@ inline void keyboard_regular_key(const enum keysym k)
|
|||||||
switch (k) {
|
switch (k) {
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
uint8_t c = keysym_to_char(k, true);
|
int32_t c = keysym_to_char(k, true);
|
||||||
if (c != -1) buffer.put(c);
|
if (c != -1) buffer.put(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -126,7 +126,7 @@ inline void keyboard_regular_key(const enum keysym k)
|
|||||||
case MODIFIER_RIGHT_CTRL: [[fallthrough]];
|
case MODIFIER_RIGHT_CTRL: [[fallthrough]];
|
||||||
case MODIFIER_BOTH_CTRL:
|
case MODIFIER_BOTH_CTRL:
|
||||||
switch (k) {
|
switch (k) {
|
||||||
case keysym::SPACE : buffer.set_mark(); break;
|
case keysym::SPACE : buffer.mark_set(); break;
|
||||||
case keysym::G : buffer.quit(); break;
|
case keysym::G : buffer.quit(); break;
|
||||||
case keysym::B : buffer.cursor_left(); break;
|
case keysym::B : buffer.cursor_left(); break;
|
||||||
case keysym::F : buffer.cursor_right(); break;
|
case keysym::F : buffer.cursor_right(); break;
|
||||||
@ -207,23 +207,32 @@ set_char(int32_t x, int32_t y, uint8_t palette, uint8_t c)
|
|||||||
|
|
||||||
void render()
|
void render()
|
||||||
{
|
{
|
||||||
for (int row = 0; row < buffer.window.cell_height; row++) {
|
editor::cursor& cur = buffer.cursor;
|
||||||
const buffer_type::line_type * l = buffer.lines[buffer.window.top + row];
|
|
||||||
|
|
||||||
for (int col = 0; col < buffer.window.cell_width; col++) {
|
editor::selection sel;
|
||||||
uint8_t c;
|
|
||||||
|
|
||||||
c = ' ';
|
if (buffer.mode == editor::mode::mark)
|
||||||
if (l != nullptr && (buffer.window.left + col) < l->length)
|
sel = buffer.mark_get();
|
||||||
c = l->buf[buffer.window.left + col];
|
|
||||||
|
|
||||||
set_char(col, row, 0, c);
|
for (int y = 0; y < buffer.window.cell_height; y++) {
|
||||||
|
const int32_t row = buffer.window.top + y;
|
||||||
|
const buffer_type::line_type * l = buffer.lines[row];
|
||||||
|
|
||||||
|
for (int x = 0; x < buffer.window.cell_width; x++) {
|
||||||
|
const int32_t col = buffer.window.left + x;
|
||||||
|
const uint8_t c = (l != nullptr && col < l->length) ? l->buf[col] : ' ';
|
||||||
|
uint8_t palette = 0;
|
||||||
|
|
||||||
|
if (row == cur.row && col == cur.col) {
|
||||||
|
palette = 1;
|
||||||
|
} else if (buffer.mode == editor::mode::mark) {
|
||||||
|
if (sel.contains(col, row))
|
||||||
|
palette = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_char(x, y, palette, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editor::cursor& cur = buffer.cursor;
|
|
||||||
const buffer_type::line_type * l = buffer.lines[cur.row];
|
|
||||||
uint8_t c = (l != nullptr && cur.col < l->length) ? l->buf[cur.col] : ' ';
|
|
||||||
set_char(cur.col - buffer.window.left, cur.row - buffer.window.top, 1, c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
@ -74,7 +74,6 @@ static void test_put()
|
|||||||
assert(l = &b.row[0]);
|
assert(l = &b.row[0]);
|
||||||
assert(l->length == 1);
|
assert(l->length == 1);
|
||||||
assert(l->buf[0] == 'a');
|
assert(l->buf[0] == 'a');
|
||||||
assert(l->buf[1] == 0x7f);
|
|
||||||
assert(b.cursor.col == 1);
|
assert(b.cursor.col == 1);
|
||||||
assert(b.cursor.row == 0);
|
assert(b.cursor.row == 0);
|
||||||
assert(b.length == 1);
|
assert(b.length == 1);
|
||||||
@ -84,7 +83,6 @@ static void test_put()
|
|||||||
assert(l->length == 2);
|
assert(l->length == 2);
|
||||||
assert(l->buf[0] == 'a');
|
assert(l->buf[0] == 'a');
|
||||||
assert(l->buf[1] == 'b');
|
assert(l->buf[1] == 'b');
|
||||||
assert(l->buf[2] == 0x7f);
|
|
||||||
assert(b.cursor.col == 2);
|
assert(b.cursor.col == 2);
|
||||||
assert(b.cursor.row == 0);
|
assert(b.cursor.row == 0);
|
||||||
assert(b.length == 1);
|
assert(b.length == 1);
|
||||||
@ -96,7 +94,6 @@ static void test_put()
|
|||||||
assert(l->buf[0] == 'a');
|
assert(l->buf[0] == 'a');
|
||||||
assert(l->buf[1] == 'c');
|
assert(l->buf[1] == 'c');
|
||||||
assert(l->buf[2] == 'b');
|
assert(l->buf[2] == 'b');
|
||||||
assert(l->buf[3] == 0x7f);
|
|
||||||
assert(b.cursor.col == 2);
|
assert(b.cursor.col == 2);
|
||||||
assert(b.cursor.row == 0);
|
assert(b.cursor.row == 0);
|
||||||
assert(b.length == 1);
|
assert(b.length == 1);
|
||||||
@ -114,7 +111,6 @@ void test_backspace()
|
|||||||
|
|
||||||
assert(b.backspace() == true);
|
assert(b.backspace() == true);
|
||||||
assert(l->length == 0);
|
assert(l->length == 0);
|
||||||
assert(l->buf[0] == 0x7f);
|
|
||||||
|
|
||||||
assert(b.backspace() == false);
|
assert(b.backspace() == false);
|
||||||
|
|
||||||
@ -130,7 +126,6 @@ void test_backspace()
|
|||||||
assert(l->buf[0] == 'b');
|
assert(l->buf[0] == 'b');
|
||||||
assert(l->buf[1] == 'd');
|
assert(l->buf[1] == 'd');
|
||||||
assert(l->buf[2] == 'e');
|
assert(l->buf[2] == 'e');
|
||||||
assert(l->buf[3] == 0x7f);
|
|
||||||
assert(l->length == 3);
|
assert(l->length == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +145,6 @@ void test_enter()
|
|||||||
b.put('s');
|
b.put('s');
|
||||||
b.put('d');
|
b.put('d');
|
||||||
b.put('f');
|
b.put('f');
|
||||||
|
|
||||||
b.cursor.row = 1;
|
b.cursor.row = 1;
|
||||||
b.cursor.col = 0;
|
b.cursor.col = 0;
|
||||||
b.put('q');
|
b.put('q');
|
||||||
@ -295,6 +289,278 @@ void test_enter_backspace3()
|
|||||||
assert(b.lines[3] == nullptr);
|
assert(b.lines[3] == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_copy()
|
||||||
|
{
|
||||||
|
// 0123
|
||||||
|
|
||||||
|
// asDf
|
||||||
|
// qwer
|
||||||
|
// jKlo
|
||||||
|
|
||||||
|
// asklo
|
||||||
|
//
|
||||||
|
// df
|
||||||
|
// qwer
|
||||||
|
// j
|
||||||
|
|
||||||
|
buffer<8, 8> b {4, 2};
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
b.put('s');
|
||||||
|
b.put('d');
|
||||||
|
b.put('f');
|
||||||
|
b.enter();
|
||||||
|
b.put('q');
|
||||||
|
b.put('w');
|
||||||
|
b.put('e');
|
||||||
|
b.put('r');
|
||||||
|
b.enter();
|
||||||
|
b.put('j');
|
||||||
|
b.put('k');
|
||||||
|
b.put('l');
|
||||||
|
b.put('o');
|
||||||
|
|
||||||
|
b.cursor_up();
|
||||||
|
b.cursor_up();
|
||||||
|
b.cursor_left();
|
||||||
|
b.cursor_left();
|
||||||
|
b.mark_set();
|
||||||
|
b.cursor_down();
|
||||||
|
b.cursor_down();
|
||||||
|
b.cursor_left();
|
||||||
|
|
||||||
|
assert(b.mark.row == 0);
|
||||||
|
assert(b.mark.col == 2);
|
||||||
|
assert(b.cursor.row == 2);
|
||||||
|
assert(b.cursor.col == 1);
|
||||||
|
|
||||||
|
b.shadow_copy();
|
||||||
|
assert(b.shadow.length == 3);
|
||||||
|
assert(b.shadow.lines[0] != nullptr);
|
||||||
|
assert(b.shadow.lines[1] != nullptr);
|
||||||
|
assert(b.shadow.lines[2] != nullptr);
|
||||||
|
assert(b.shadow.lines[3] == nullptr);
|
||||||
|
assert(b.shadow.lines[0]->length == 2);
|
||||||
|
assert(b.shadow.lines[1]->length == 4);
|
||||||
|
assert(b.shadow.lines[2]->length == 1);
|
||||||
|
assert(b.shadow.lines[0]->buf[0] == 'd');
|
||||||
|
assert(b.shadow.lines[0]->buf[1] == 'f');
|
||||||
|
assert(b.shadow.lines[1]->buf[0] == 'q');
|
||||||
|
assert(b.shadow.lines[1]->buf[1] == 'w');
|
||||||
|
assert(b.shadow.lines[1]->buf[2] == 'e');
|
||||||
|
assert(b.shadow.lines[1]->buf[3] == 'r');
|
||||||
|
assert(b.shadow.lines[2]->buf[0] == 'j');
|
||||||
|
assert(b.shadow.lines[0] != b.lines[0]);
|
||||||
|
assert(b.shadow.lines[1] == b.lines[1]);
|
||||||
|
assert(b.shadow.lines[2] != b.lines[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_copy_same_line()
|
||||||
|
{
|
||||||
|
buffer<8, 8> b {4, 2};
|
||||||
|
|
||||||
|
// v
|
||||||
|
// asdF
|
||||||
|
//
|
||||||
|
// sd
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
b.put('s');
|
||||||
|
b.put('d');
|
||||||
|
b.put('f');
|
||||||
|
b.cursor_left();
|
||||||
|
b.mark_set();
|
||||||
|
b.cursor_left();
|
||||||
|
b.cursor_left();
|
||||||
|
|
||||||
|
// non-cow same line
|
||||||
|
b.shadow_copy();
|
||||||
|
assert(b.shadow.length == 1);
|
||||||
|
assert(b.shadow.lines[0] != nullptr);
|
||||||
|
assert(b.shadow.lines[1] == nullptr);
|
||||||
|
assert(b.shadow.lines[0] != b.lines[0]);
|
||||||
|
assert(b.shadow.lines[0]->length == 2);
|
||||||
|
assert(b.shadow.lines[0]->buf[0] == 's');
|
||||||
|
assert(b.shadow.lines[0]->buf[1] == 'd');
|
||||||
|
|
||||||
|
// cow same line
|
||||||
|
b.cursor_home();
|
||||||
|
b.mark_set();
|
||||||
|
b.cursor_end();
|
||||||
|
b.shadow_copy();
|
||||||
|
assert(b.shadow.length == 1);
|
||||||
|
assert(b.shadow.lines[0] == b.lines[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_copy_multi_line_cow()
|
||||||
|
{
|
||||||
|
buffer<8, 8> b {4, 2};
|
||||||
|
|
||||||
|
// v
|
||||||
|
// asdF
|
||||||
|
//
|
||||||
|
// sd
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
b.put('s');
|
||||||
|
b.put('d');
|
||||||
|
b.put('f');
|
||||||
|
b.enter();
|
||||||
|
b.put('q');
|
||||||
|
b.put('w');
|
||||||
|
b.put('e');
|
||||||
|
b.put('r');
|
||||||
|
b.mark_set();
|
||||||
|
b.cursor_up();
|
||||||
|
b.cursor_home();
|
||||||
|
b.shadow_copy();
|
||||||
|
|
||||||
|
assert(b.shadow.length == 2);
|
||||||
|
assert(b.shadow.lines[0] == b.lines[0]);
|
||||||
|
assert(b.shadow.lines[1] == b.lines[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_copy_multi_line_offset()
|
||||||
|
{
|
||||||
|
buffer<8, 8> b {4, 2};
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
b.put('s');
|
||||||
|
b.put('d');
|
||||||
|
b.put('f');
|
||||||
|
b.enter();
|
||||||
|
b.put('q');
|
||||||
|
b.put('w');
|
||||||
|
b.put('e');
|
||||||
|
b.put('r');
|
||||||
|
b.enter();
|
||||||
|
b.put('s');
|
||||||
|
b.put('h');
|
||||||
|
b.put('a');
|
||||||
|
b.put('d');
|
||||||
|
b.enter();
|
||||||
|
b.put('o');
|
||||||
|
b.put('w');
|
||||||
|
|
||||||
|
// qwEr
|
||||||
|
// sHad
|
||||||
|
|
||||||
|
// er
|
||||||
|
// s
|
||||||
|
b.cursor_up();
|
||||||
|
b.cursor_home();
|
||||||
|
b.cursor_right();
|
||||||
|
b.mark_set();
|
||||||
|
b.cursor_up();
|
||||||
|
b.cursor_right();
|
||||||
|
b.shadow_copy();
|
||||||
|
|
||||||
|
assert(b.shadow.length == 2);
|
||||||
|
assert(b.shadow.lines[0]->length == 2);
|
||||||
|
assert(b.shadow.lines[1]->length == 1);
|
||||||
|
assert(b.shadow.lines[0]->buf[0] == 'e');
|
||||||
|
assert(b.shadow.lines[0]->buf[1] == 'r');
|
||||||
|
assert(b.shadow.lines[1]->buf[0] == 's');
|
||||||
|
|
||||||
|
b.cursor_home();
|
||||||
|
b.mark_set();
|
||||||
|
b.cursor_down();
|
||||||
|
b.cursor_down();
|
||||||
|
b.cursor_end();
|
||||||
|
b.shadow_copy();
|
||||||
|
|
||||||
|
assert(b.shadow.length == 3);
|
||||||
|
assert(b.shadow.lines[0] == b.lines[1]);
|
||||||
|
assert(b.shadow.lines[1] == b.lines[2]);
|
||||||
|
assert(b.shadow.lines[2] == b.lines[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_delete_from_line()
|
||||||
|
{
|
||||||
|
buffer<8, 8> b {4, 2};
|
||||||
|
|
||||||
|
b.put('a');
|
||||||
|
b.put('s');
|
||||||
|
b.put('d');
|
||||||
|
b.put('f');
|
||||||
|
|
||||||
|
// S E
|
||||||
|
// asdf
|
||||||
|
// af
|
||||||
|
|
||||||
|
b.delete_from_line(b.lines[0], 1, 3);
|
||||||
|
assert(b.lines[0]->length == 2);
|
||||||
|
assert(b.lines[0]->buf[0] == 'a');
|
||||||
|
assert(b.lines[0]->buf[1] == 'f');
|
||||||
|
|
||||||
|
b.delete_from_line(b.lines[0], 0, 2);
|
||||||
|
assert(b.lines[0] == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_selection_delete()
|
||||||
|
{
|
||||||
|
buffer<8, 8> b {4, 2};
|
||||||
|
|
||||||
|
b.put('s');
|
||||||
|
b.put('p');
|
||||||
|
b.put('a');
|
||||||
|
b.put('m');
|
||||||
|
b.enter();
|
||||||
|
b.put('a');
|
||||||
|
b.put('s');
|
||||||
|
b.put('d');
|
||||||
|
b.put('f');
|
||||||
|
b.enter();
|
||||||
|
b.put('0');
|
||||||
|
b.put('1');
|
||||||
|
b.put('2');
|
||||||
|
b.put('3');
|
||||||
|
b.enter();
|
||||||
|
b.put('j');
|
||||||
|
b.put('k');
|
||||||
|
b.put('l');
|
||||||
|
b.put('p');
|
||||||
|
b.enter();
|
||||||
|
b.put('q');
|
||||||
|
b.put('w');
|
||||||
|
b.put('e');
|
||||||
|
b.put('r');
|
||||||
|
|
||||||
|
// spam (0)
|
||||||
|
// asDf < (1)
|
||||||
|
// 0123 -- (2) (del)
|
||||||
|
// jKlp < (3) (del)
|
||||||
|
// qwer (4)
|
||||||
|
// (5)
|
||||||
|
|
||||||
|
// spam (0)
|
||||||
|
// asklp (1)
|
||||||
|
// qwer (2)
|
||||||
|
|
||||||
|
cursor min { 2, 1 };
|
||||||
|
cursor max { 1, 3 };
|
||||||
|
selection sel { &min, &max };
|
||||||
|
b.selection_delete(sel);
|
||||||
|
|
||||||
|
assert(b.length == 3);
|
||||||
|
assert(b.lines[0]->length == 4);
|
||||||
|
assert(b.lines[0]->buf[0] == 's');
|
||||||
|
assert(b.lines[0]->buf[1] == 'p');
|
||||||
|
assert(b.lines[0]->buf[2] == 'a');
|
||||||
|
assert(b.lines[0]->buf[3] == 'm');
|
||||||
|
assert(b.lines[1]->length == 5);
|
||||||
|
assert(b.lines[1]->buf[0] == 'a');
|
||||||
|
assert(b.lines[1]->buf[1] == 's');
|
||||||
|
assert(b.lines[1]->buf[2] == 'k');
|
||||||
|
assert(b.lines[1]->buf[3] == 'l');
|
||||||
|
assert(b.lines[1]->buf[4] == 'p');
|
||||||
|
assert(b.lines[2]->length == 4);
|
||||||
|
assert(b.lines[2]->buf[0] == 'q');
|
||||||
|
assert(b.lines[2]->buf[1] == 'w');
|
||||||
|
assert(b.lines[2]->buf[2] == 'e');
|
||||||
|
assert(b.lines[2]->buf[3] == 'r');
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
test_allocate();
|
test_allocate();
|
||||||
@ -305,6 +571,20 @@ int main()
|
|||||||
test_enter_backspace3();
|
test_enter_backspace3();
|
||||||
test_enter_scroll();
|
test_enter_scroll();
|
||||||
test_first_enter();
|
test_first_enter();
|
||||||
|
test_copy();
|
||||||
|
test_copy_same_line();
|
||||||
|
test_copy_multi_line_cow();
|
||||||
|
test_copy_multi_line_offset();
|
||||||
|
test_delete_from_line();
|
||||||
|
test_selection_delete();
|
||||||
|
|
||||||
|
editor::buffer<64, 12 * 1024> b {0, 0};
|
||||||
|
std::cerr << "size: " << (sizeof (b)) << '\n';
|
||||||
|
std::cerr << " row: " << (sizeof (b.row)) << '\n';
|
||||||
|
std::cerr << " lines: " << (sizeof (b.lines)) << '\n';
|
||||||
|
std::cerr << " offsetof lines[1]: " << (reinterpret_cast<uint8_t*>(&b.lines[1]) -
|
||||||
|
reinterpret_cast<uint8_t*>(&b.lines[0])) << '\n';
|
||||||
|
std::cerr << " shadow.lines: " << (sizeof (b.shadow.lines)) << '\n';
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user