editor: add delete_word_forward/backward

This also fixes a small ordering bug in selection_delete.
This commit is contained in:
Zack Buhman 2023-06-11 03:58:17 +00:00
parent 46c274d490
commit feb57872e8
3 changed files with 109 additions and 43 deletions

View File

@ -83,8 +83,10 @@ struct buffer {
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 delete_backward();
inline constexpr bool delete_forward();
inline constexpr bool delete_backward();
inline constexpr void delete_word_forward();
inline constexpr void delete_word_backward();
inline constexpr bool cursor_left();
inline constexpr bool cursor_right();
@ -248,6 +250,44 @@ inline constexpr bool buffer<C, R>::put(const uint8_t c)
return true;
}
template <int C, int R>
inline constexpr bool buffer<C, R>::delete_forward()
{
if (this->mode == mode::mark) {
this->mode = mode::normal;
return mark_delete();
}
editor::cursor& cur = this->cursor;
if (cur.col < 0 || cur.col > C)
return false;
if (line_length(this->lines[cur.row]) == 0) {
if (this->length == 1) return false;
decref(this->lines[cur.row]);
// shift all lines up by 1
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;
// no scrolling needed--cursor is already in the correct position
} else {
// c
// 01234
line<C> * l = mutref(this->lines[cur.row]);
int32_t length = l->length - cur.col;
move(&l->buf[cur.col], &l->buf[cur.col+1], length);
l->length--;
// no scrolling needed--cursor is already in the correct position
}
return true;
}
template <int C, int R>
inline constexpr bool buffer<C, R>::delete_backward()
{
@ -288,41 +328,31 @@ inline constexpr bool buffer<C, R>::delete_backward()
}
template <int C, int R>
inline constexpr bool buffer<C, R>::delete_forward()
inline constexpr void buffer<C, R>::delete_word_forward()
{
if (this->mode == mode::mark) {
this->mode = mode::normal;
return mark_delete();
}
mark_set();
cursor_scan_word_forward();
const selection sel = mark_get();
selection_delete(sel);
editor::cursor& cur = this->cursor;
// move cur to sel.min
scroll_new_cursor(*sel.min);
if (cur.col < 0 || cur.col > C)
return false;
this->mode = mode::normal;
}
if (line_length(this->lines[cur.row]) == 0) {
if (this->length == 1) return false;
decref(this->lines[cur.row]);
// shift all lines up by 1
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;
// no scrolling needed--cursor is already in the correct position
} else {
// c
// 01234
line<C> * l = mutref(this->lines[cur.row]);
int32_t length = l->length - cur.col;
move(&l->buf[cur.col], &l->buf[cur.col+1], length);
l->length--;
template <int C, int R>
inline constexpr void buffer<C, R>::delete_word_backward()
{
mark_set();
cursor_scan_word_backward();
const selection sel = mark_get();
selection_delete(sel);
// no scrolling needed--cursor is already in the correct position
}
// move cur to sel.min
scroll_new_cursor(*sel.min);
return true;
this->mode = mode::normal;
}
template <int C, int R>
@ -625,9 +655,12 @@ inline constexpr void buffer<C, R>::selection_delete(const selection& sel)
decref(this->lines[ix]);
}
// remove sel.min->col from lmin
line<C> * lmin = mutref(this->lines[sel.min->row]);
lmin->length -= (lmin->length - sel.min->col);
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
@ -637,7 +670,6 @@ inline constexpr void buffer<C, R>::selection_delete(const selection& sel)
// 0cd
lmin->length -= (lmin->length - sel.min->col);
int32_t move_length = min(lmax->length - sel.max->col, C - lmin->length);
move(&lmin->buf[sel.min->col],
&lmax->buf[sel.max->col],
@ -669,7 +701,7 @@ template <int C, int R>
inline constexpr bool buffer<C, R>::mark_delete()
{
const selection sel = mark_get();
this->selection_delete(sel);
selection_delete(sel);
// move cur to sel.min
scroll_new_cursor(*sel.min);

View File

@ -127,16 +127,18 @@ inline void keyboard_regular_key(const enum keysym k)
case MODIFIER_RIGHT_CTRL: [[fallthrough]];
case MODIFIER_BOTH_CTRL:
switch (k) {
case keysym::SPACE: buffer.mark_set(); break;
case keysym::G : buffer.quit(); break;
case keysym::B : buffer.cursor_left(); break;
case keysym::F : buffer.cursor_right(); break;
case keysym::P : buffer.cursor_up(); break;
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;
case keysym::BACKSPACE: buffer.delete_word_backward(); break;
case keysym::DELETE : buffer.delete_word_forward(); break;
case keysym::SPACE : buffer.mark_set(); break;
case keysym::G : buffer.quit(); break;
case keysym::B : buffer.cursor_left(); break;
case keysym::F : buffer.cursor_right(); break;
case keysym::P : buffer.cursor_up(); break;
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;

View File

@ -763,6 +763,37 @@ void test_delete_forward()
assert(b.lines[1]->buf[1] == 'w');
}
void test_delete_word_backward()
{
buffer<8, 8> b {4, 2};
b.put('q');
b.put('w');
b.put('e');
b.enter();
assert(b.length == 2);
b.delete_word_backward();
assert(b.length == 1);
assert(decltype(b)::line_length(b.lines[0]) == 0);
b.put('f');
b.put('o');
b.put('o');
b.put(' ');
b.put('b');
b.put('a');
b.put('r');
b.enter();
b.put('j');
b.put('k');
b.put('l');
assert(b.length == 2);
b.delete_word_backward();
b.delete_word_backward();
assert(b.length == 1);
assert(decltype(b)::line_length(b.lines[0]) == 4);
}
int main()
{
test_allocate();
@ -782,6 +813,7 @@ int main()
test_shadow_paste_oneline();
test_shadow_paste_multiline();
test_delete_forward();
test_delete_word_backward();
return 0;
}