Commit: cc1eb1cf3113d81c8432531acf2eedb3463c68b5
Parent: a8b1076b26fee0d81383908dabbffedab42fe068
Author: Randy Palamar
Date: Mon, 18 Nov 2024 05:25:24 -0700
simplify selection iteration and fix some edge cases
Diffstat:
M | terminal.c | | | 37 | +++++++++++++++++++++++++++++-------- |
M | util.c | | | 29 | +++++++++++++++++++++++++++++ |
M | util.h | | | 32 | ++++++++++++++++++++------------ |
M | vtgl.c | | | 119 | +++++++++++++++++++++++++------------------------------------------------------ |
4 files changed, 115 insertions(+), 102 deletions(-)
diff --git a/terminal.c b/terminal.c
@@ -35,23 +35,44 @@ get_word_around_cell(Term *t, iv2 cell)
Range result = {.start = cell, .end = cell};
Cell *row = t->views[t->view_idx].fb.rows[cell.y];
- b32 isspace = ISSPACE(row[cell.x].cp);
+ b32 isspace = ISSPACE(row[cell.x].cp) && !(row[cell.x].bg & ATTR_WDUMMY);
while (result.start.x > 0) {
- Cell nc = row[result.start.x - 1];
- if (!(nc.bg & ATTR_WDUMMY) && isspace != ISSPACE(nc.cp))
+ Cell *nc = row + result.start.x - 1;
+ if (!(nc->bg & ATTR_WDUMMY) && isspace != ISSPACE(nc->cp))
break;
result.start.x--;
}
while (result.end.x < t->size.w - 1) {
- Cell nc = row[result.end.x + 1];
- if (!(nc.bg & ATTR_WDUMMY) && isspace != ISSPACE(nc.cp))
+ Cell *nc = row + result.end.x + 1;
+ if (!(nc->bg & ATTR_WDUMMY) && isspace != ISSPACE(nc->cp))
break;
result.end.x++;
}
- /* NOTE: ATTR_WDUMMY is invalid for start and end of range */
- if (row[result.start.x].bg & ATTR_WDUMMY) result.start.x++;
- if (row[result.end.x].bg & ATTR_WDUMMY) result.end.x--;
+ return result;
+}
+
+static Range
+get_char_around_cell(Term *t, iv2 cell)
+{
+ Range result = {.start = cell, .end = cell};
+
+ /* NOTE: if the cell is WDUMMY move back until we find the starting
+ * WIDE cell. then set the range end to the last WDUMMY cell */
+ Cell *row = t->views[t->view_idx].fb.rows[cell.y];
+ if (row[cell.x].bg & ATTR_WDUMMY) {
+ while (!(row[cell.x].bg & ATTR_WIDE) && cell.x > 0)
+ cell.x--;
+ ASSERT(row[cell.x].bg & ATTR_WIDE);
+ result.start = cell;
+ }
+ if (row[cell.x].bg & ATTR_WIDE) {
+ while ((row[cell.x + 1].bg & ATTR_WDUMMY) && cell.x + 1 < t->size.w)
+ cell.x++;
+ ASSERT(row[cell.x].bg & ATTR_WDUMMY);
+ result.end = cell;
+ }
+
return result;
}
diff --git a/util.c b/util.c
@@ -373,6 +373,35 @@ stream_push_f64(Stream *s, f64 f, i64 prec)
}
}
+static SelectionIterator
+selection_iterator(Range s, Row *rows, u32 term_width)
+{
+ SelectionIterator result;
+ result.rows = rows;
+ result.cursor = (iv2){.x = -1, .y = -1};
+ result.next = s.start;
+ result.end = s.end;
+ result.term_width = term_width;
+ return result;
+}
+
+static Cell *
+selection_next(SelectionIterator *s)
+{
+ Cell *result = 0;
+
+ if (!equal_iv2(s->cursor, s->end)) {
+ s->cursor = s->next;
+ result = s->rows[s->next.y] + s->next.x++;
+ if (s->next.x == s->term_width) {
+ s->next.x = 0;
+ s->next.y++;
+ }
+ }
+
+ return result;
+}
+
static s8
utf8_encode(u32 cp)
{
diff --git a/util.h b/util.h
@@ -366,6 +366,26 @@ struct conversion_result {
union { i32 i; f32 f; Colour colour;};
};
+enum selection_states {
+ SS_NONE,
+ SS_CHAR,
+ SS_WORDS,
+};
+
+typedef struct {
+ Range range;
+ Range anchor;
+ enum selection_states state;
+} Selection;
+
+typedef struct {
+ Row *rows;
+ iv2 cursor;
+ iv2 next;
+ iv2 end;
+ u32 term_width;
+} SelectionIterator;
+
#include <immintrin.h>
#include "util.c"
@@ -488,18 +508,6 @@ typedef struct {
iv2 last_cell_report;
} InteractionState;
-enum selection_states {
- SS_NONE,
- SS_CHAR,
- SS_WORDS,
-};
-
-typedef struct {
- Range range;
- Range anchor;
- enum selection_states state;
-} Selection;
-
#define ESC_ARG_SIZ 6
typedef struct {
s8 raw;
diff --git a/vtgl.c b/vtgl.c
@@ -534,35 +534,10 @@ render_framebuffer(Term *t, RenderCell *render_buf)
/* NOTE: draw selection if active */
if (is_valid_range(t->selection.range)) {
- Range sel = t->selection.range;
- iv2 curs = sel.start;
- iv2 end = sel.end;
- /* NOTE: do full rows first */
- for (; curs.y < end.y; curs.y++) {
- for (u32 i = curs.x; i < t->size.w; i++) {
- Cell *c = &tv->fb.rows[curs.y][i];
- RenderCell *rc = render_buf + curs.y * t->size.w + i;
- rc->fg ^= SHADER_PACK_ATTR(ATTR_INVERSE);
- if (c->bg & ATTR_WIDE) {
- for (u32 j = 1; c[j].bg & ATTR_WDUMMY; j++) {
- rc[j].fg ^= SHADER_PACK_ATTR(ATTR_INVERSE);
- i++;
- }
- }
- }
- curs.x = 0;
- }
- /* NOTE: do the last row */
- for (u32 i = curs.x; i <= end.x; i++) {
- Cell *c = &tv->fb.rows[curs.y][i];
- RenderCell *rc = render_buf + curs.y * t->size.w + i;
+ SelectionIterator si = selection_iterator(t->selection.range, tv->fb.rows, t->size.w);
+ for (Cell *c = selection_next(&si); c; c = selection_next(&si)) {
+ RenderCell *rc = render_buf + si.cursor.y * t->size.w + si.cursor.x;
rc->fg ^= SHADER_PACK_ATTR(ATTR_INVERSE);
- if (c->bg & ATTR_WIDE) {
- for (u32 j = 1; c[j].bg & ATTR_WDUMMY; j++) {
- rc[j].fg ^= SHADER_PACK_ATTR(ATTR_INVERSE);
- i++;
- }
- }
}
}
@@ -606,39 +581,27 @@ mouse_to_cell_space(Term *t, v2 mouse)
}
static void
-stream_push_selection(Stream *s, TermView *tv, Range sel, u32 term_width)
+stream_push_selection(Stream *s, Row *rows, Range sel, u32 term_width)
{
- iv2 curs = sel.start;
- iv2 end = sel.end;
-
s->errors |= !is_valid_range(sel);
if (s->errors) return;
- /* NOTE: do full rows first */
+ SelectionIterator si = selection_iterator(sel, rows, term_width);
u32 last_non_space_idx = 0;
- for (; curs.y < end.y; curs.y++) {
- for (; curs.x < term_width; curs.x++) {
- Cell c = tv->fb.rows[curs.y][curs.x];
- if (c.bg & ATTR_WDUMMY)
- continue;
- stream_push_s8(s, utf8_encode(c.cp));
- if (!ISSPACE(c.cp))
- last_non_space_idx = s->widx;
- }
- s->widx = last_non_space_idx;
- stream_push_byte(s, '\n');
- curs.x = 0;
- }
-
- /* NOTE: do the last row */
- for (; curs.x <= end.x; curs.x++) {
- Cell c = tv->fb.rows[curs.y][curs.x];
- if (c.bg & ATTR_WDUMMY)
+ for (Cell *c = selection_next(&si); c; c = selection_next(&si)) {
+ if (c->bg & ATTR_WDUMMY)
continue;
- stream_push_s8(s, utf8_encode(c.cp));
- if (!ISSPACE(c.cp))
+
+ stream_push_s8(s, utf8_encode(c->cp));
+ if (!ISSPACE(c->cp))
last_non_space_idx = s->widx;
+
+ if (si.next.y != si.cursor.y) {
+ s->widx = last_non_space_idx;
+ stream_push_byte(s, '\n');
+ }
}
+
s->widx = last_non_space_idx;
}
@@ -650,14 +613,10 @@ begin_selection(Term *t, u32 click_count, v2 mouse)
sel->range.end = INVALID_RANGE_END;
iv2 cell = mouse_to_cell_space(t, mouse);
- if (t->views[t->view_idx].fb.rows[cell.y][cell.x].bg & ATTR_WDUMMY) {
- ASSERT(t->views[t->view_idx].fb.rows[cell.y][cell.x - 1].bg & ATTR_WIDE);
- cell.x--;
- }
switch (sel->state) {
case SS_WORDS: sel->anchor = sel->range = get_word_around_cell(t, cell); break;
- case SS_CHAR: sel->anchor = (Range){.start = cell, .end = cell}; break;
+ case SS_CHAR: sel->anchor = get_char_around_cell(t, cell); break;
case SS_NONE: break;
}
@@ -675,36 +634,30 @@ update_selection(Term *t, TerminalInput *input)
Selection *sel = &t->selection;
iv2 new_p = mouse_to_cell_space(t, input->mouse);
-
- if (t->views[t->view_idx].fb.rows[new_p.y][new_p.x].bg & ATTR_WDUMMY) {
- ASSERT(t->views[t->view_idx].fb.rows[new_p.y][new_p.x - 1].bg & ATTR_WIDE);
- new_p.x--;
+ Range new, old_range = sel->range;
+ switch (sel->state) {
+ case SS_WORDS: new = get_word_around_cell(t, new_p); break;
+ case SS_CHAR: new = get_char_around_cell(t, new_p); break;
+ case SS_NONE: INVALID_CODE_PATH; break;
}
- Range old_range = sel->range;
-
- if (sel->state != SS_WORDS) {
+ if (sel->anchor.start.y < new.start.y) {
sel->range.start = sel->anchor.start;
- sel->range.end = new_p;
+ sel->range.end = new.end;
+ } else if (sel->anchor.start.y > new.start.y) {
+ sel->range.start = new.start;
+ sel->range.end = sel->anchor.end;
} else {
- Range word = get_word_around_cell(t, new_p);
- if (sel->anchor.start.y < word.start.y) {
- sel->range.start = sel->anchor.start;
- sel->range.end = word.end;
- } else if (sel->anchor.start.y > word.start.y) {
- sel->range.start = word.start;
+ if (new.start.x < sel->anchor.start.x) {
+ sel->range.start = new.start;
sel->range.end = sel->anchor.end;
} else {
- if (word.start.x < sel->anchor.start.x) {
- sel->range.start = word.start;
- sel->range.end = sel->anchor.end;
- } else {
- sel->range.start = sel->anchor.start;
- sel->range.end = word.end;
- }
+ sel->range.start = sel->anchor.start;
+ sel->range.end = new.end;
}
}
- sel->range = normalize_range(sel->range);
+ sel->range = normalize_range(sel->range);
+
if (!equal_range(old_range, sel->range))
t->gl.flags |= UPDATE_RENDER_BUFFER;
}
@@ -712,7 +665,7 @@ update_selection(Term *t, TerminalInput *input)
KEYBIND_FN(copy)
{
Stream buf = arena_stream(t->arena_for_frame);
- stream_push_selection(&buf, t->views + t->view_idx, t->selection.range, t->size.w);
+ stream_push_selection(&buf, t->views[t->view_idx].fb.rows, t->selection.range, t->size.w);
platform->set_clipboard(&buf, a.i);
return 1;
}
@@ -1235,7 +1188,9 @@ DEBUG_EXPORT VTGL_ACTIVE_SELECTION_FN(vtgl_active_selection)
{
Term *t = memory->memory;
Range result = t->selection.range;
- if (out) stream_push_selection(out, t->views + t->view_idx, t->selection.range, t->size.w);
+ if (out)
+ stream_push_selection(out, t->views[t->view_idx].fb.rows,
+ t->selection.range, t->size.w);
return result;
}