Commit: 8b63ad912b83a4103f5745d979af7c6a19b93228
Parent: bf4d6cd4d7ace61e9aff996f6ab1f00b6cacfd63
Author: Randy Palamar
Date: Sun, 25 Aug 2024 12:46:33 -0600
CBT: cursor backwards tabulation (move cursor backwards to tabstop)
Diffstat:
M | terminal.c | | | 19 | ++++++++++++------- |
M | test.c | | | 54 | +++++++++++++++++++++++++++++++++++++++--------------- |
M | util.h | | | 6 | ++++-- |
3 files changed, 55 insertions(+), 24 deletions(-)
diff --git a/terminal.c b/terminal.c
@@ -247,10 +247,13 @@ cursor_alt(Term *t, b32 save)
}
+/* NOTE: advance the cursor by <n> cells; handles reverse movement */
static void
-cursor_step_column(Term *t, i32 step)
+cursor_step_column(Term *t, i32 n)
{
- t->cursor.pos.x += step;
+ t->cursor.pos.x += n;
+ if (t->cursor.pos.x < 0)
+ t->cursor.pos.x = 0;
if (t->cursor.pos.x < t->size.w)
return;
t->cursor.pos.x = 0;
@@ -581,11 +584,12 @@ push_newline(Term *t)
}
static void
-push_tab(Term *t)
+push_tab(Term *t, i32 n)
{
- u32 advance = g_tabstop - (t->cursor.pos.x % g_tabstop);
- fb_clear_region(t, t->cursor.pos.y, t->cursor.pos.y, t->cursor.pos.x, t->cursor.pos.x + advance);
- cursor_step_column(t, advance);
+ i32 n_abs = ABS(n);
+ i32 n_sgn = SGN(n);
+ i32 advance = n_abs * g_tabstop - (t->cursor.pos.x % g_tabstop);
+ cursor_step_column(t, n_sgn * advance);
}
static i32
@@ -647,6 +651,7 @@ handle_csi(Term *t, s8 *raw)
case 'J': erase_in_display(t, &csi); break;
case 'K': erase_in_line(t, &csi); break;
case 'X': erase_characters(t, ORONE(csi.argv[0])); break;
+ case 'Z': push_tab(t, -(ORONE(csi.argv[0]))); break;
case 'a': cursor_step_raw(t, ORONE(csi.argv[0]), 0, 1); break;
case 'd': cursor_move_to(t, csi.argv[0] - 1, p.x); break;
case 'e': cursor_step_raw(t, ORONE(csi.argv[0]), 1, 0); break;
@@ -988,7 +993,7 @@ push_line(Term *t, Line *line, Arena a)
case 0x1B: handle_escape(t, &l, a); break;
case '\r': t->cursor.pos.x = 0; break;
case '\n': push_newline(t); break;
- case '\t': push_tab(t); break;
+ case '\t': push_tab(t, 1); break;
case '\a': /* TODO: ding ding? */ break;
case '\b':
cursor_move_to(t, t->cursor.pos.y, t->cursor.pos.x - 1);
diff --git a/test.c b/test.c
@@ -20,6 +20,7 @@ typedef TEST_FN(Test_Fn);
X(colour_setting) \
X(cursor_at_line_boundary) \
X(cursor_movement) \
+ X(cursor_tabs) \
X(working_ringbuffer)
#define X(fn) static TEST_FN(fn);
@@ -86,6 +87,31 @@ launder_static_string(Term *term, s8 static_str)
return raw;
}
+static TEST_FN(colour_setting)
+{
+ struct test_result result = {.info = __FUNCTION__};
+
+ launder_static_string(term, CSI(8m));
+ launder_static_string(term, CSI(4m));
+ launder_static_string(term, CSI(9m));
+ launder_static_string(term, CSI(24m));
+ launder_static_string(term, CSI(33m));
+ launder_static_string(term, CSI(48;2;75;63;42m));
+ s8 raw = launder_static_string(term, s8("A"));
+
+ size parsed_lines = split_raw_input_to_lines(term, raw);
+ blit_lines(term, arena, parsed_lines);
+
+ Cell c = { .cp = 'A', .style = {
+ .bg = (Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF},
+ .fg = g_colours.data[3],
+ .attr = (ATTR_INVISIBLE|ATTR_STRUCK),
+ }};
+ result.status = check_cells_equal(&c, term->views[term->view_idx].fb.rows[0]);
+
+ return result;
+}
+
static TEST_FN(cursor_movement)
{
struct test_result result = {.info = __FUNCTION__};
@@ -103,27 +129,24 @@ static TEST_FN(cursor_movement)
return result;
}
-static TEST_FN(colour_setting)
+static TEST_FN(cursor_tabs)
{
struct test_result result = {.info = __FUNCTION__};
- launder_static_string(term, CSI(8m));
- launder_static_string(term, CSI(4m));
- launder_static_string(term, CSI(9m));
- launder_static_string(term, CSI(24m));
- launder_static_string(term, CSI(33m));
- launder_static_string(term, CSI(48;2;75;63;42m));
- s8 raw = launder_static_string(term, s8("A"));
-
+ /* NOTE: first test advancing to a tabstop */
+ s8 raw = launder_static_string(term, s8("123\t"));
size parsed_lines = split_raw_input_to_lines(term, raw);
blit_lines(term, arena, parsed_lines);
- Cell c = { .cp = 'A', .style = {
- .bg = (Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF},
- .fg = g_colours.data[3],
- .attr = (ATTR_INVISIBLE|ATTR_STRUCK),
- }};
- result.status = check_cells_equal(&c, term->views[term->view_idx].fb.rows[0]);
+ result.status = term->cursor.pos.x == (g_tabstop);
+
+ /* NOTE: now test negative tabstop movement */
+ launder_static_string(term, s8("\t1234\t"));
+ raw = launder_static_string(term, CSI(2Z));
+ parsed_lines = split_raw_input_to_lines(term, raw);
+ blit_lines(term, arena, parsed_lines);
+
+ result.status &= term->cursor.pos.x == (g_tabstop);
return result;
}
@@ -217,6 +240,7 @@ main(void)
for (u32 i = 0; i < ARRAY_COUNT(tests); i++) {
buffer_reset(&term);
cursor_reset(&term);
+ cursor_move_to(&term, 0, 0);
struct test_result result = tests[i](&term, memory);
if (result.status == 0) {
failure_count++;
diff --git a/util.h b/util.h
@@ -24,10 +24,12 @@
#define MEGABYTE (1024ULL * 1024ULL)
#define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a))
+#define ABS(a) ((a) < 0 ? (-a) : (a))
#define BETWEEN(x, a, b) ((x) >= (a) && (x) <= (b))
-#define MIN(a, b) ((a) <= (b) ? (a) : (b))
-#define MAX(a, b) ((a) >= (b) ? (a) : (b))
#define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+#define SGN(a) ((a) < 0 ? (-1) : (1))
#define ISSPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
#define ISPRINT(c) BETWEEN((c), ' ', '~')