Files
Hermit/tests/test_textsel.c
T
WHC 1c26cc6e30
ci / linux (clang) (push) Failing after 21m53s
ci / linux (gcc) (push) Failing after 48s
ci / musl (push) Canceled after 0s
ci / macos (push) Canceled after 0s
ci / sanitizers (push) Canceled after 11m39s
TUI: mouse drag selection with OSC 52 copy; Markdown emphasis follows CommonMark; v0.5.0
- Drag, double-click (word) and triple-click (line) selection in the log,
  auto-scroll at the edges, soft-wrapped lines joined on copy.
- sashidown ANSI renderer: delimiter runs with flanking rules, so
  intraword underscores (test_apply_patch.c) stay literal; unmatched
  * _ and backticks are shown as-is; * and _ do not close each other.
- tests: test_textsel, test_markdown.
2026-10-06 14:24:42 +08:00

113 lines
3.8 KiB
C

#include "cst_textsel.h"
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHECK(c) \
do { \
if (!(c)) { \
fprintf(stderr, "%s:%d: CHECK(%s) failed\n", __FILE__, __LINE__, #c); \
exit(1); \
} \
} while (0)
static char got[512];
static const char *slice(const char *line, int indent, int c1, int c2,
int trim) {
cst_selbuf_t b = {0};
CHECK(cst_textsel_append(&b, line, indent, c1, c2, trim) == 0);
snprintf(got, sizeof(got), "%s", b.s ? b.s : "");
cst_selbuf_free(&b);
return got;
}
static void test_ascii(void) {
CHECK(strcmp(slice("hello world", 0, 0, 4, 1), "hello") == 0);
CHECK(strcmp(slice("hello world", 0, 6, -1, 1), "world") == 0);
CHECK(strcmp(slice("hello world", 0, 3, 7, 1), "lo wo") == 0);
/* indent: columns before the content are blank */
CHECK(strcmp(slice("abc", 2, 0, 2, 1), "a") == 0);
CHECK(strcmp(slice("abc", 2, 0, 1, 1), "") == 0);
/* trailing spaces */
CHECK(strcmp(slice("ab ", 0, 0, -1, 1), "ab") == 0);
CHECK(strcmp(slice("ab ", 0, 0, -1, 0), "ab ") == 0);
/* beyond the end of the content */
CHECK(strcmp(slice("ab", 0, 5, 9, 1), "") == 0);
}
static void test_ansi(void) {
const char *s = "\033[1;31mred\033[0m and \033[32mgreen\033[0m";
CHECK(strcmp(slice(s, 0, 0, -1, 1), "red and green") == 0);
CHECK(strcmp(slice(s, 0, 4, 6, 1), "and") == 0);
CHECK(strcmp(slice(s, 0, 8, 12, 1), "green") == 0);
int c1, c2;
cst_textsel_word(s, 0, 10, &c1, &c2);
CHECK(c1 == 8 && c2 == 12);
}
static void test_words(void) {
const char *s = " open src/tui.c:42 now";
int c1, c2;
cst_textsel_word(s, 0, 9, &c1, &c2);
CHECK(c1 == 7 && c2 == 18);
CHECK(strcmp(slice(s, 0, c1, c2, 1), "src/tui.c:42") == 0);
cst_textsel_word(s, 0, 6, &c1, &c2); /* on a space */
CHECK(c1 == 6 && c2 == 6);
cst_textsel_word(s, 0, 40, &c1, &c2); /* past the end */
CHECK(c1 == 40 && c2 == 40);
cst_textsel_word(s, 1, 3, &c1, &c2); /* indent shifts columns */
CHECK(c1 == 3 && c2 == 6);
}
static void test_wide(void) {
/* 中文 occupies columns 0-1 and 2-3 */
const char *s = "中文 ok";
CHECK(strcmp(slice(s, 0, 1, 1, 1), "中") == 0);
CHECK(strcmp(slice(s, 0, 1, 2, 1), "中文") == 0);
CHECK(strcmp(slice(s, 0, 3, 6, 1), "文 ok") == 0);
int c1, c2;
cst_textsel_word(s, 0, 3, &c1, &c2);
CHECK(c1 == 0 && c2 == 3);
cst_textsel_word("「x」", 0, 1, &c1, &c2); /* wide punctuation */
CHECK(c1 == 0 && c2 == 1);
/* highlight edges never split a wide character */
c1 = 1, c2 = 2;
cst_textsel_snap(s, 0, &c1, &c2);
CHECK(c1 == 0 && c2 == 3);
c1 = 2, c2 = 5;
cst_textsel_snap(s, 0, &c1, &c2);
CHECK(c1 == 2 && c2 == 5);
c1 = 3, c2 = -1;
cst_textsel_snap(s, 1, &c1, &c2); /* indent 1: 中 at 1-2, 文 at 3-4 */
CHECK(c1 == 3 && c2 == -1);
c1 = 4, c2 = 1;
cst_textsel_snap(s, 1, &c1, &c2);
CHECK(c1 == 3 && c2 == 2);
}
static void test_join(void) {
cst_selbuf_t b = {0};
CHECK(cst_textsel_append(&b, "first line ", 0, 6, -1, 1) == 0);
CHECK(cst_selbuf_putc(&b, '\n') == 0);
CHECK(cst_textsel_append(&b, "second", 2, 0, 4, 1) == 0);
CHECK(strcmp(b.s, "line\nsec") == 0);
cst_selbuf_free(&b);
CHECK(b.s == NULL && b.n == 0);
}
int main(void) {
test_ascii();
test_ansi();
test_words();
if (setlocale(LC_CTYPE, "C.UTF-8") || setlocale(LC_CTYPE, "en_US.UTF-8"))
test_wide();
else
printf("test_textsel: no UTF-8 locale, wide-character checks skipped\n");
test_join();
printf("test_textsel: ok\n");
return 0;
}