editor: add cursor_scan_word_forward/backward

'backward' feels very mildly hacky, but not in a terrible way.
This commit is contained in:
Zack Buhman 2023-06-11 03:19:15 +00:00
parent 5944e3a360
commit 46c274d490
2 changed files with 137 additions and 33 deletions

View File

@ -85,12 +85,19 @@ struct buffer {
inline constexpr bool put(uint8_t c);
inline constexpr bool delete_backward();
inline constexpr bool delete_forward();
inline constexpr bool cursor_left();
inline constexpr bool cursor_right();
inline constexpr bool cursor_up();
inline constexpr bool cursor_down();
inline constexpr bool cursor_home();
inline constexpr bool cursor_end();
inline constexpr uint8_t cursor_get(const editor::cursor& cur);
inline constexpr bool cursor_increment(editor::cursor& cur);
inline constexpr void cursor_scan_word_forward();
inline constexpr bool cursor_decrement(editor::cursor& cur);
inline constexpr void cursor_scan_word_backward();
inline constexpr bool enter();
inline constexpr void mark_set();
inline constexpr selection mark_get();
@ -116,6 +123,8 @@ struct buffer {
inline constexpr void scroll_right();
inline constexpr void scroll_up();
inline constexpr void scroll_down();
inline constexpr void scroll_new_cursor(const editor::cursor& cur);
inline constexpr void scroll_new_col(const int32_t col);
};
template <int C, int R>
@ -262,13 +271,7 @@ inline constexpr bool buffer<C, R>::delete_backward()
cur.row--;
scroll_up();
if (min.col < cur.col) {
cur.col = min.col;
scroll_right();
} else {
cur.col = min.col;
scroll_left();
}
scroll_new_col(min.col);
} else {
// c
// 01234
@ -422,6 +425,94 @@ inline constexpr bool buffer<C, R>::cursor_end()
return true;
}
static inline constexpr bool word_boundary(int8_t c)
{
return ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9'));
}
template <int C, int R>
inline constexpr uint8_t buffer<C, R>::cursor_get(const editor::cursor& cur)
{
return this->lines[cur.row] == nullptr ? 0 :
(cur.col == this->lines[cur.row]->length ? '\n' :
this->lines[cur.row]->buf[cur.col]);
}
template <int C, int R>
inline constexpr bool buffer<C, R>::cursor_increment(editor::cursor& cur)
{
if (cur.col >= line_length(this->lines[cur.row])) {
if (cur.row + 1 >= this->length) {
return false;
} else {
cur.row++;
cur.col = 0;
}
} else {
cur.col++;
}
return true;
}
template <int C, int R>
inline constexpr void buffer<C, R>::cursor_scan_word_forward()
{
// copy of this->cursor
editor::cursor cur = this->cursor;
// if we are not inside a word boundary, scan in `dir` direction until we are
// inside a word boundary
while (!word_boundary(cursor_get(cur))) {
if (!cursor_increment(cur)) { scroll_new_cursor(cur); return; }
}
// scan in `dir` direction until we are not inside a word boundary
while (word_boundary(cursor_get(cur))) {
if (!cursor_increment(cur)) { scroll_new_cursor(cur); return; }
}
scroll_new_cursor(cur);
}
template <int C, int R>
inline constexpr bool buffer<C, R>::cursor_decrement(editor::cursor& cur)
{
if (cur.col == 0) {
if (cur.row - 1 < 0) {
return false;
} else {
cur.row--;
cur.col = this->lines[cur.row]->length;
}
} else {
cur.col--;
}
return true;
}
template <int C, int R>
inline constexpr void buffer<C, R>::cursor_scan_word_backward()
{
// copy of this->cursor
editor::cursor cur = this->cursor;
cursor_decrement(cur);
// if we are not inside a word boundary, scan in `dir` direction until we are
// inside a word boundary
while (!word_boundary(cursor_get(cur))) {
if (!cursor_decrement(cur)) { scroll_new_cursor(cur); return; }
}
// scan in `dir` direction until we are not inside a word boundary
while (word_boundary(cursor_get(cur))) {
if (!cursor_decrement(cur)) { scroll_new_cursor(cur); return; }
}
cursor_increment(cur);
scroll_new_cursor(cur);
}
template <int C, int R>
inline constexpr bool buffer<C, R>::enter()
{
@ -581,13 +672,7 @@ inline constexpr bool buffer<C, R>::mark_delete()
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(); }
scroll_new_cursor(*sel.min);
return true;
}
@ -790,14 +875,8 @@ inline constexpr bool buffer<C, R>::shadow_paste()
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();
}
int32_t col = line_length(last_line_src);
scroll_new_col(col);
}
return true;
@ -844,4 +923,25 @@ inline constexpr void buffer<C, R>::scroll_right()
if (this->cursor.col >= this->window.left + this->window.cell_width)
this->window.left = (this->cursor.col - (this->window.cell_width - 1));
}
template <int C, int R>
inline constexpr void buffer<C, R>::scroll_new_cursor(const editor::cursor& oth)
{
editor::cursor& cur = this->cursor;
if (oth.row < cur.row) { cur.row = oth.row; scroll_up(); }
else { cur.row = oth.row; scroll_down(); }
if (oth.col < cur.col) { cur.col = oth.col; scroll_right(); }
else { cur.col = oth.col; scroll_left(); }
}
template <int C, int R>
inline constexpr void buffer<C, R>::scroll_new_col(const int32_t col)
{
editor::cursor& cur = this->cursor;
if (col < cur.col) { cur.col = col; scroll_right(); }
else { cur.col = col; scroll_left(); }
}
}

View File

@ -145,6 +145,10 @@ inline void keyboard_regular_key(const enum keysym k)
case MODIFIER_RIGHT_ALT: [[fallthrough]];
case MODIFIER_BOTH_ALT:
switch (k) {
case keysym::ARROW_LEFT : buffer.cursor_scan_word_backward(); break;
case keysym::B : buffer.cursor_scan_word_backward(); break;
case keysym::ARROW_RIGHT: buffer.cursor_scan_word_forward(); break;
case keysym::F : buffer.cursor_scan_word_forward(); break;
case keysym::W : buffer.shadow_copy(); break;
default: break;
}