Commit: 74a07eb867da5ff89d82991b946540bc99610802
Parent: 21f08e5cafd29dae841324424e7debc7f51009c0
Author: Randy Palamar
Date: Thu, 31 Oct 2024 06:14:43 -0600
fix tab advancement for arbitrary tabstop locations
Diffstat:
M | terminal.c | | | 19 | +++++++++++++++---- |
M | test.c | | | 136 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
2 files changed, 107 insertions(+), 48 deletions(-)
diff --git a/terminal.c b/terminal.c
@@ -334,15 +334,26 @@ cursor_step_raw(Term *t, i32 step, i32 rows, i32 cols)
static i32
next_tab_position(Term *t, b32 backwards)
{
+ static_assert(ARRAY_COUNT(t->tabs) == 8 * sizeof(*t->tabs),
+ "Term.tabs must be same length as the bitwidth of it's type");
u32 col = t->cursor.pos.x;
u32 idx = col / ARRAY_COUNT(t->tabs);
u32 bit = col % ARRAY_COUNT(t->tabs);
u32 mask = safe_left_shift(1, bit - backwards) - 1;
- i32 result = 32 * idx;
- if (backwards) result += 32 - _lzcnt_u32(t->tabs[idx] & mask);
- else result += _tzcnt_u32(t->tabs[idx] & ~mask) + 1;
- ASSERT(ABS(result) < t->size.w);
+ u32 result = 32 * idx, zeroes;
+ if (backwards) zeroes = 32 - _lzcnt_u32(t->tabs[idx++] & mask);
+ else zeroes = _tzcnt_u32(t->tabs[idx++] & ~mask);
+ result += zeroes;
+
+ /* NOTE we may need to advance across idx boundaries */
+ while (idx < ARRAY_COUNT(t->tabs) && zeroes == 32) {
+ if (backwards) zeroes = 32 - _lzcnt_u32(t->tabs[idx++]);
+ else zeroes = _tzcnt_u32(t->tabs[idx++]);
+ result += zeroes;
+ }
+ result += !backwards;
+ ASSERT(result < t->size.w);
return result;
}
diff --git a/test.c b/test.c
@@ -60,12 +60,13 @@ struct test_result { b32 status; const char *info; };
#define TEST_FN(name) struct test_result name(Term *term, Arena arena)
typedef TEST_FN(Test_Fn);
-#define TESTS \
- X(csi_embedded_control) \
- X(colour_setting) \
- X(cursor_at_line_boundary) \
- X(cursor_movement) \
- X(cursor_tabs) \
+#define TESTS \
+ X(csi_embedded_control) \
+ X(colour_setting) \
+ X(cursor_at_line_boundary) \
+ X(cursor_movement) \
+ X(cursor_tabs) \
+ X(cursor_tabs_across_boundary) \
X(working_ringbuffer)
#define X(fn) static TEST_FN(fn);
@@ -76,6 +77,7 @@ TESTS
static Test_Fn *tests[] = {
TESTS
};
+#undef X
#define ESC(a) s8("\x1B"#a)
#define CSI(a) ESC([a)
@@ -194,43 +196,6 @@ static TEST_FN(colour_setting)
return result;
}
-static TEST_FN(cursor_movement)
-{
- struct test_result result = {.info = __FUNCTION__};
-
- s8 raw = launder_static_string(term, CSI(17;2H));
- handle_input(term, arena, raw);
-
- raw = launder_static_string(term, CSI(7G));
- handle_input(term, arena, raw);
-
- result.status = term->cursor.pos.y == 16 && term->cursor.pos.x == 6;
-
- return result;
-}
-
-static TEST_FN(cursor_tabs)
-{
- struct test_result result = {.info = __FUNCTION__};
-
- /* NOTE: first test advancing to a tabstop */
- s8 raw = launder_static_string(term, s8("123\t"));
- handle_input(term, arena, raw);
-
- result.status = term->cursor.pos.x == (g_tabstop);
-
- /* NOTE: now test negative tabstop movement and tabstop setting */
- launder_static_string(term, s8("12"));
- launder_static_string(term, ESC(H));
- launder_static_string(term, s8("34\t"));
- raw = launder_static_string(term, CSI(2Z));
- handle_input(term, arena, raw);
-
- result.status &= term->cursor.pos.x == (g_tabstop);
-
- return result;
-}
-
static TEST_FN(cursor_at_line_boundary)
{
/* NOTE: two tests here
@@ -289,6 +254,83 @@ static TEST_FN(cursor_at_line_boundary)
return result;
}
+
+static TEST_FN(cursor_movement)
+{
+ struct test_result result = {.info = __FUNCTION__};
+
+ s8 raw = launder_static_string(term, CSI(17;2H));
+ handle_input(term, arena, raw);
+
+ raw = launder_static_string(term, CSI(7G));
+ handle_input(term, arena, raw);
+
+ result.status = term->cursor.pos.y == 16 && term->cursor.pos.x == 6;
+
+ return result;
+}
+
+static TEST_FN(cursor_tabs)
+{
+ struct test_result result = {.info = __FUNCTION__};
+
+ /* NOTE: first test advancing to a tabstop */
+ s8 raw = launder_static_string(term, s8("123\t"));
+ handle_input(term, arena, raw);
+
+ result.status = term->cursor.pos.x == (g_tabstop);
+
+ /* NOTE: now test negative tabstop movement and tabstop setting */
+ launder_static_string(term, s8("12"));
+ launder_static_string(term, ESC(H));
+ launder_static_string(term, s8("34\t"));
+ raw = launder_static_string(term, CSI(2Z));
+ handle_input(term, arena, raw);
+
+ result.status &= term->cursor.pos.x == (g_tabstop);
+
+ return result;
+}
+
+static TEST_FN(cursor_tabs_across_boundary)
+{
+ struct test_result result = {.info = __FUNCTION__};
+
+ /* NOTE: clear tabstops then set one beyond multiple boundaries */
+ launder_static_string(term, CSI(3g));
+ launder_static_string(term, CSI(1;67H));
+ launder_static_string(term, ESC(H));
+ launder_static_string(term, CSI(1;1H));
+ s8 raw = launder_static_string(term, s8("\t"));
+ handle_input(term, arena, raw);
+
+ result.status = term->cursor.pos.x == 66;
+
+ /* NOTE: now set one exactly on a boundary */
+ launder_static_string(term, CSI(1;34H));
+ launder_static_string(term, ESC(H));
+ launder_static_string(term, CSI(1;1H));
+ raw = launder_static_string(term, s8("\t"));
+ handle_input(term, arena, raw);
+
+ result.status &= term->cursor.pos.x == 33;
+
+ /* NOTE: now set one right before the previous */
+ launder_static_string(term, CSI(1;33H));
+ launder_static_string(term, ESC(H));
+ launder_static_string(term, CSI(1;1H));
+
+ raw = launder_static_string(term, s8("\t"));
+ handle_input(term, arena, raw);
+ result.status &= term->cursor.pos.x == 32;
+
+ raw = launder_static_string(term, s8("\t"));
+ handle_input(term, arena, raw);
+ result.status &= term->cursor.pos.x == 33;
+
+ return result;
+}
+
static TEST_FN(working_ringbuffer)
{
struct test_result result = {.info = __FUNCTION__};
@@ -331,6 +373,12 @@ main(void)
os_alloc_framebuffer(&term.views[0].fb, term.size.h, term.size.w);
os_alloc_framebuffer(&term.views[1].fb, term.size.h, term.size.w);
+ u32 max_name_len = 0;
+ #define X(name) if (sizeof(#name) - 1 > max_name_len) max_name_len = sizeof(#name) - 1;
+ TESTS
+ #undef X
+ max_name_len += 1;
+
for (u32 i = 0; i < ARRAY_COUNT(tests); i++) {
buffer_reset(&term);
term_reset(&term);
@@ -339,7 +387,7 @@ main(void)
stream_push_s8(&log, fn);
stream_push_s8(&log, s8(":"));
size count = fn.len;
- while (count < 26) { stream_push_byte(&log, ' '); count++; }
+ while (count < max_name_len) { stream_push_byte(&log, ' '); count++; }
if (result.status == 0) {
failure_count++;
stream_push_s8(&log, failure_string);