editor: finish implementing shadow_paste
This adds a new overwrite_line is very useful for dealing with cow-related boilerplate. There is some refactoring possible (and perhaps accidental correctness improvement) if more functions used 'overwrite_line'.
This commit is contained in:
parent
6a5ebab01f
commit
a11dadcde8
@ -10,8 +10,8 @@ namespace editor {
|
||||
template <int C>
|
||||
struct line {
|
||||
uint8_t buf[C];
|
||||
int16_t length;
|
||||
int16_t refcount;
|
||||
int32_t length;
|
||||
int32_t refcount;
|
||||
|
||||
static_assert(C < 32768);
|
||||
};
|
||||
@ -106,6 +106,11 @@ struct buffer {
|
||||
const int32_t col_end_ix);
|
||||
inline constexpr bool shadow_copy();
|
||||
inline constexpr bool shadow_cut();
|
||||
inline constexpr void overwrite_line(line<C> *& dst,
|
||||
const int32_t dst_col,
|
||||
line<C> * src,
|
||||
const int32_t src_col);
|
||||
inline constexpr bool shadow_paste();
|
||||
inline constexpr void scroll_left();
|
||||
inline constexpr void scroll_right();
|
||||
inline constexpr void scroll_up();
|
||||
@ -237,6 +242,7 @@ template <int C, int R>
|
||||
inline constexpr bool buffer<C, R>::backspace()
|
||||
{
|
||||
if (this->mode == mode::mark) {
|
||||
this->mode = mode::normal;
|
||||
return mark_delete();
|
||||
}
|
||||
|
||||
@ -266,8 +272,8 @@ inline constexpr bool buffer<C, R>::backspace()
|
||||
// c
|
||||
// 01234
|
||||
line<C> * l = mutref(this->lines[cur.row]);
|
||||
auto length = l->length - cur.col;
|
||||
if (length > 0) move(&l->buf[cur.col-1], &l->buf[cur.col], length);
|
||||
int32_t length = l->length - cur.col;
|
||||
move(&l->buf[cur.col-1], &l->buf[cur.col], length);
|
||||
|
||||
cur.col--;
|
||||
scroll_left();
|
||||
@ -503,9 +509,7 @@ inline constexpr void buffer<C, R>::selection_delete(const selection& sel)
|
||||
// 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)
|
||||
int32_t move_length = min(lmax->length - sel.max->col, C - lmin->length);
|
||||
move(&lmin->buf[sel.min->col],
|
||||
&lmax->buf[sel.max->col],
|
||||
move_length);
|
||||
@ -535,9 +539,6 @@ inline constexpr void buffer<C, R>::selection_delete(const selection& sel)
|
||||
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);
|
||||
|
||||
@ -550,8 +551,6 @@ inline constexpr bool buffer<C, R>::mark_delete()
|
||||
if (min.col < cur.col) { cur.col = min.col; scroll_right(); }
|
||||
else { cur.col = min.col; scroll_left(); }
|
||||
|
||||
this->mode = mode::normal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -642,6 +641,126 @@ inline constexpr bool buffer<C, R>::shadow_cut()
|
||||
|
||||
this->shadow_copy();
|
||||
|
||||
this->mark_delete();
|
||||
|
||||
this->mode = mode::normal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <int C, int R>
|
||||
inline constexpr void buffer<C, R>::overwrite_line(line<C> *& dst,
|
||||
const int32_t dst_col,
|
||||
line<C> * src,
|
||||
const int32_t src_col)
|
||||
{
|
||||
if (src == nullptr) {
|
||||
if (dst_col == 0) dst = nullptr;
|
||||
//else do nothing
|
||||
} else if (dst_col == 0 && src_col == 0 &&
|
||||
line_length(src) >= line_length(dst)) {
|
||||
if (dst != nullptr) decref(dst);
|
||||
dst = incref(src);
|
||||
} else {
|
||||
line<C> * dstmut = mutref(dst);
|
||||
int32_t copy_length = min(src->length - src_col, C - dst_col);
|
||||
move(&dstmut->buf[dst_col],
|
||||
&src->buf[src_col],
|
||||
copy_length);
|
||||
|
||||
// v
|
||||
// 012345
|
||||
// 2 + 4 = 6
|
||||
dstmut->length = max(dstmut->length, dst_col + copy_length);
|
||||
}
|
||||
}
|
||||
|
||||
template <int C, int R>
|
||||
inline constexpr bool buffer<C, R>::shadow_paste()
|
||||
{
|
||||
if (this->mode == mode::mark)
|
||||
this->mode = mode::normal;
|
||||
|
||||
editor::cursor& cur = this->cursor;
|
||||
|
||||
if (this->shadow.length == 1) {
|
||||
|
||||
line<C> * ls = this->shadow.lines[0];
|
||||
line<C> *& lc = this->lines[cur.row];
|
||||
// dst, dst_col
|
||||
overwrite_line(lc, cur.col + ls->length, // (dst_col = 2 + 3 = 5)
|
||||
// src, src_col
|
||||
lc, cur.col); // (length = 5 - 2 = 3)
|
||||
// now copy shadow to lc
|
||||
overwrite_line(lc, cur.col,
|
||||
ls, 0);
|
||||
|
||||
cur.col += ls->length;
|
||||
scroll_right();
|
||||
} else {
|
||||
// (qw
|
||||
// er
|
||||
// gh)
|
||||
//
|
||||
// aBcd
|
||||
// zyx (1)
|
||||
//
|
||||
// aqw
|
||||
// er (cow)
|
||||
// ghBcd
|
||||
// zyx (3)
|
||||
|
||||
int32_t last_line_offset = (this->shadow.length - 1); // (2)
|
||||
|
||||
line<C> *& first_line_dst = this->lines[cur.row];
|
||||
line<C> * first_line_src = this->shadow.lines[0];
|
||||
line<C> *& last_line_dst = this->lines[cur.row + last_line_offset];
|
||||
line<C> * last_line_src = this->shadow.lines[last_line_offset];
|
||||
|
||||
int32_t shift_first_ix = cur.row + 1;
|
||||
int32_t shift_lines = this->length - shift_first_ix; // (2 - 1)
|
||||
|
||||
move(&this->lines[cur.row + this->shadow.length], // 3
|
||||
&this->lines[shift_first_ix], // 1
|
||||
(sizeof (line<C>*)) * shift_lines);
|
||||
|
||||
// cow all lines other than the first and last
|
||||
for (int ix = 1; ix < last_line_offset; ix++) { // ix < 2, max(ix) == 1
|
||||
this->lines[cur.row + ix] = incref(this->shadow.lines[ix]);
|
||||
}
|
||||
|
||||
// copy 'bcd' to last_line_dst
|
||||
// dst, dst_col
|
||||
overwrite_line(last_line_dst, line_length(last_line_src),
|
||||
// src, src_col
|
||||
first_line_dst, cur.col);
|
||||
|
||||
// shrink first_line_dst to '1'
|
||||
if (first_line_dst != nullptr) first_line_dst->length = cur.col;
|
||||
|
||||
// copy 'qw' to first_line_dst
|
||||
overwrite_line(first_line_dst, cur.col,
|
||||
first_line_src, 0);
|
||||
|
||||
// copy 'gh' to last_line_dst
|
||||
overwrite_line(last_line_dst, 0,
|
||||
last_line_src, 0);
|
||||
|
||||
this->length += last_line_offset;
|
||||
|
||||
cur.row += last_line_offset;
|
||||
scroll_down();
|
||||
|
||||
int32_t new_col = line_length(last_line_src);
|
||||
if (new_col < cur.col) {
|
||||
cur.col = new_col;
|
||||
scroll_right();
|
||||
} else {
|
||||
cur.col = new_col;
|
||||
scroll_left();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -134,6 +134,8 @@ inline void keyboard_regular_key(const enum keysym k)
|
||||
case keysym::N : buffer.cursor_down(); break;
|
||||
case keysym::A : buffer.cursor_home(); break;
|
||||
case keysym::E : buffer.cursor_end(); break;
|
||||
case keysym::Y : buffer.shadow_paste(); break;
|
||||
case keysym::W : buffer.shadow_cut(); break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
@ -141,6 +143,10 @@ inline void keyboard_regular_key(const enum keysym k)
|
||||
case MODIFIER_LEFT_ALT: [[fallthrough]];
|
||||
case MODIFIER_RIGHT_ALT: [[fallthrough]];
|
||||
case MODIFIER_BOTH_ALT:
|
||||
switch (k) {
|
||||
case keysym::W : buffer.shadow_copy(); break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
|
@ -561,6 +561,176 @@ void test_selection_delete()
|
||||
assert(b.lines[2]->buf[3] == 'r');
|
||||
}
|
||||
|
||||
void test_shadow_paste_oneline()
|
||||
{
|
||||
buffer<8, 8> b {4, 2};
|
||||
|
||||
b.put('q');
|
||||
b.put('w');
|
||||
b.put('r');
|
||||
b.mark_set();
|
||||
b.cursor_home();
|
||||
b.shadow_copy();
|
||||
b.mark_delete();
|
||||
assert(decltype(b)::line_length(b.lines[0]) == 0);
|
||||
assert(b.cursor.col == 0);
|
||||
|
||||
b.put('a');
|
||||
b.put('b');
|
||||
b.put('c');
|
||||
b.put('d');
|
||||
b.put('e');
|
||||
|
||||
b.cursor_left();
|
||||
b.cursor_left();
|
||||
b.cursor_left();
|
||||
|
||||
// (qwr)
|
||||
// 01234567
|
||||
// abCde
|
||||
// abqwrCde
|
||||
|
||||
assert(decltype(b)::line_length(b.lines[0]) == 5);
|
||||
b.shadow_paste();
|
||||
assert(decltype(b)::line_length(b.lines[0]) == 8);
|
||||
assert(b.lines[0]->buf[0] == 'a');
|
||||
assert(b.lines[0]->buf[1] == 'b');
|
||||
assert(b.lines[0]->buf[2] == 'q');
|
||||
assert(b.lines[0]->buf[3] == 'w');
|
||||
assert(b.lines[0]->buf[4] == 'r');
|
||||
assert(b.lines[0]->buf[5] == 'c');
|
||||
assert(b.lines[0]->buf[6] == 'd');
|
||||
assert(b.lines[0]->buf[7] == 'e');
|
||||
|
||||
// also handles these other cases:
|
||||
// abc|
|
||||
// abcqwr
|
||||
|
||||
// |
|
||||
// qwr (cow)
|
||||
b.decref(b.lines[0]);
|
||||
b.cursor_home();
|
||||
assert(b.shadow.lines[0]->buf[0] == 'q');
|
||||
assert(b.shadow.lines[0]->buf[1] == 'w');
|
||||
assert(b.shadow.lines[0]->buf[2] == 'r');
|
||||
|
||||
b.put('a');
|
||||
b.put('b');
|
||||
b.put('c');
|
||||
assert(decltype(b)::line_length(b.lines[0]) == 3);
|
||||
b.shadow_paste();
|
||||
assert(decltype(b)::line_length(b.lines[0]) == 6);
|
||||
assert(b.lines[0]->buf[0] == 'a');
|
||||
assert(b.lines[0]->buf[1] == 'b');
|
||||
assert(b.lines[0]->buf[2] == 'c');
|
||||
assert(b.lines[0]->buf[3] == 'q');
|
||||
assert(b.lines[0]->buf[4] == 'w');
|
||||
assert(b.lines[0]->buf[5] == 'r');
|
||||
|
||||
b.decref(b.lines[0]);
|
||||
b.cursor_home();
|
||||
|
||||
b.shadow_paste();
|
||||
assert(b.lines[0] == b.shadow.lines[0]);
|
||||
}
|
||||
|
||||
void test_shadow_paste_multiline()
|
||||
{
|
||||
buffer<8, 8> b {4, 2};
|
||||
|
||||
// (qw
|
||||
// er
|
||||
// gh)
|
||||
//
|
||||
// jklopr
|
||||
// aBcd
|
||||
// zyx (2)
|
||||
//
|
||||
// jklopr
|
||||
// aqw
|
||||
// er (cow)
|
||||
// ghBcd
|
||||
// zyx (4)
|
||||
|
||||
b.put('q');
|
||||
b.put('w');
|
||||
b.enter();
|
||||
b.put('e');
|
||||
b.put('r');
|
||||
b.enter();
|
||||
b.put('g');
|
||||
b.put('h');
|
||||
b.mark_set();
|
||||
b.cursor_home();
|
||||
b.cursor_up();
|
||||
b.cursor_up();
|
||||
assert(b.cursor.row == 0);
|
||||
assert(b.cursor.col == 0);
|
||||
b.shadow_copy();
|
||||
b.mark_delete();
|
||||
assert(b.shadow.length == 3);
|
||||
assert(decltype(b)::line_length(b.shadow.lines[0]) == 2);
|
||||
assert(decltype(b)::line_length(b.shadow.lines[1]) == 2);
|
||||
assert(decltype(b)::line_length(b.shadow.lines[2]) == 2);
|
||||
assert(b.length == 1);
|
||||
assert(decltype(b)::line_length(b.lines[0]) == 0);
|
||||
|
||||
// jkl
|
||||
// aBcd
|
||||
// zyx
|
||||
|
||||
b.put('j');
|
||||
b.put('k');
|
||||
b.put('l');
|
||||
b.put('o');
|
||||
b.put('p');
|
||||
b.put('r');
|
||||
b.enter();
|
||||
b.put('a');
|
||||
b.put('b');
|
||||
b.put('c');
|
||||
b.put('d');
|
||||
b.enter();
|
||||
b.put('z');
|
||||
b.put('y');
|
||||
b.put('x');
|
||||
b.cursor_home();
|
||||
b.cursor_up();
|
||||
b.cursor_right();
|
||||
assert(b.cursor.row == 1);
|
||||
assert(b.cursor.col == 1);
|
||||
|
||||
b.shadow_paste();
|
||||
|
||||
// jklopr
|
||||
// aqw
|
||||
// er (cow)
|
||||
// ghBcd
|
||||
// zyx (4)
|
||||
|
||||
assert(b.length == 5);
|
||||
assert(b.lines[0]->length == 6);
|
||||
assert(b.lines[1]->length == 3);
|
||||
assert(b.lines[2]->length == 2);
|
||||
assert(b.lines[3]->length == 5);
|
||||
assert(b.lines[4]->length == 3);
|
||||
|
||||
assert(b.lines[1]->buf[0] == 'a');
|
||||
assert(b.lines[1]->buf[1] == 'q');
|
||||
assert(b.lines[1]->buf[2] == 'w');
|
||||
assert(b.lines[2] == b.shadow.lines[1]);
|
||||
assert(b.lines[2]->buf[0] == 'e');
|
||||
assert(b.lines[2]->buf[1] == 'r');
|
||||
assert(b.lines[3]->buf[0] == 'g');
|
||||
assert(b.lines[3]->buf[1] == 'h');
|
||||
assert(b.lines[3]->buf[2] == 'b');
|
||||
assert(b.lines[3]->buf[3] == 'c');
|
||||
assert(b.lines[3]->buf[4] == 'd');
|
||||
assert(b.lines[4]->buf[0] == 'z');
|
||||
assert(b.lines[4]->buf[1] == 'y');
|
||||
assert(b.lines[4]->buf[2] == 'x');
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_allocate();
|
||||
@ -577,14 +747,8 @@ int main()
|
||||
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';
|
||||
test_shadow_paste_oneline();
|
||||
test_shadow_paste_multiline();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user