Commit: 2f79cb6313b4316457fe9f2f3333575ec8c69386
Parent: d49dcdf55714cc86063d211aa80d0ee7c541b153
Author: Randy Palamar
Date: Sun, 11 Aug 2024 15:53:28 -0600
properly ensure utf-8 is added to line_end and add this test case
Diffstat:
M | terminal.c | | | 19 | ++++++++++++++----- |
M | test.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
2 files changed, 79 insertions(+), 7 deletions(-)
diff --git a/terminal.c b/terminal.c
@@ -711,7 +711,6 @@ split_raw_input_to_lines(Term *t, s8 raw)
lb->buf[lb->widx].has_unicode |= _mm_movemask_epi8(hasutf8);
raw = consume(raw, data - raw.data);
- u32 last_c = 0;
if (raw.len) {
if (peek(raw, 0) == 0x1B) {
s8 old = raw;
@@ -727,12 +726,22 @@ split_raw_input_to_lines(Term *t, s8 raw)
default: break;
}
} else {
- last_c = get_ascii(&raw);
- if (last_c == '\n') {
+ u32 cp = peek(raw, 0);
+ if (cp == '\n') {
+ raw = consume(raw, 1);
parsed_lines++;
feed_line(lb, raw.data, t->cursor.state);
- } else if (last_c & 0x80) {
+ } else if (cp & 0x80) {
lb->buf[lb->widx].has_unicode = 1;
+ /* TODO: this is probably slow */
+ size old_len = raw.len;
+ if (get_utf8(&raw) == (u32)-1) {
+ /* NOTE: Need More Bytes! */
+ t->unprocessed_bytes = old_len;
+ return parsed_lines;
+ }
+ } else {
+ raw = consume(raw, 1);
}
}
}
@@ -740,7 +749,7 @@ split_raw_input_to_lines(Term *t, s8 raw)
lb->buf[lb->widx].end = raw.data;
//if (tv->lines.buf[tv->lines.widx].end < tv->lines.buf[tv->lines.widx].start)
// tv->lines.buf[tv->lines.widx].end += tv->log.cap;
- if (line_length(lb->buf + lb->widx) > SPLIT_LONG && !(last_c & 0x80)) {
+ if (line_length(lb->buf + lb->widx) > SPLIT_LONG) {
parsed_lines++;
feed_line(lb, raw.data, t->cursor.state);
}
diff --git a/test.c b/test.c
@@ -5,6 +5,8 @@ static const char *glfwGetWindowTitle(GLFWwindow *w) { return "test"; };
#include "util.h"
+#include <string.h> /* memcmp */
+
#define ESC(a) s8("\x1B"#a)
#define CSI(a) ESC([a)
@@ -12,8 +14,9 @@ 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(colour_setting) \
+#define TESTS \
+ X(colour_setting) \
+ X(cursor_at_line_boundary) \
X(cursor_movement)
#define X(fn) static TEST_FN(fn);
@@ -25,6 +28,16 @@ static Test_Fn *tests[] = {
TESTS
};
+static void
+buffer_reset(Term *t)
+{
+ t->log.widx = 0;
+ t->log.filled = 0;
+ t->unprocessed_bytes = 0;
+ t->log_lines.widx = 0;
+ t->log_lines.filled = 0;
+}
+
static size
copy_into_ringbuf(RingBuf *rb, s8 raw)
{
@@ -107,6 +120,55 @@ static TEST_FN(colour_setting)
return result;
}
+static TEST_FN(cursor_at_line_boundary)
+{
+ /* NOTE: two tests here
+ * 1. split_raw_input_to_lines should modify cursor style properties
+ * 2. split_raw_input_to_lines should not split utf-8 characters into separate lines
+ */
+ struct test_result result = {.info = __FUNCTION__};
+
+ s8 long_line = s8alloc(&arena, 8192);
+ mem_clear(long_line.data, ' ', long_line.len);
+
+ /* NOTE: change the cursor state in the middle of the line */
+ long_line.data[128 + 0] = 0x1B;
+ long_line.data[128 + 1] = '[';
+ long_line.data[128 + 2] = '5';
+ long_line.data[128 + 3] = 'm';
+
+ /* NOTE: place a non-ASCII character on the long line edge */
+ s8 red_dragon = utf8_encode(0x1F004);
+ long_line.data[SPLIT_LONG - 1] = red_dragon.data[0];
+ long_line.data[SPLIT_LONG + 0] = red_dragon.data[1];
+ long_line.data[SPLIT_LONG + 1] = red_dragon.data[2];
+ long_line.data[SPLIT_LONG + 2] = red_dragon.data[3];
+
+ /* NOTE: shove a newline at the end so that finish parsing the second line */
+ long_line.data[long_line.len - 1] = '\n';
+
+ #if 0
+ long_line.data[SPLIT_LONG + 3] = 0;
+ printf("encoded char: %s\n", (char *)(long_line.data + SPLIT_LONG - 1));
+ long_line.data[SPLIT_LONG + 3] = ' ';
+ #endif
+
+ s8 raw = launder_static_string(term, long_line);
+
+ /* NOTE: check if line actually split and that it was split after red dragon */
+ size parsed_lines = split_raw_input_to_lines(term, raw);
+ result.status = parsed_lines == 2;
+ result.status &= line_length(term->log_lines.buf) > SPLIT_LONG;
+
+ /* NOTE: check that cursor state was transferred */
+ Cursor line_end = simulate_line(term, term->log_lines.buf);
+ result.status &= !memcmp(&line_end.state,
+ &term->log_lines.buf[1].cursor_state,
+ sizeof(term->log_lines.buf[0].cursor_state));
+
+ return result;
+}
+
static u32 failure_count;
int
@@ -125,6 +187,7 @@ main(void)
os_alloc_framebuffer(&term.fb, term.size.h, term.size.w);
for (u32 i = 0; i < ARRAY_COUNT(tests); i++) {
+ buffer_reset(&term);
cursor_reset(&term);
struct test_result result = tests[i](&term, memory);
if (result.status == 0) {