Files
Hermit/tests/test_markdown.c
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

110 lines
3.3 KiB
C

#include "sashidown.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)
/* SASHIDOWN_FMT_ANSI_PLAIN theme */
#define ITALIC "\033[3m"
#define ITALIC_OFF "\033[23m"
#define BOLD "\033[1m"
#define BOLD_OFF "\033[22m"
#define CODE "\033[7m"
static char out[4096], plain[4096];
/* Renders md; `out` keeps the escapes, `plain` has them stripped. */
static void render(const char *md) {
size_t n = 0;
CHECK(sashidown_convert(md, strlen(md), out, sizeof(out) - 1, &n,
SASHIDOWN_FMT_ANSI_PLAIN) == 0);
out[n] = 0;
size_t o = 0;
for (size_t i = 0; i < n;) {
if (out[i] == '\033' && out[i + 1] == '[') {
i += 2;
while (i < n && !(out[i] >= 0x40 && out[i] <= 0x7e))
i++;
i++;
continue;
}
plain[o++] = out[i++];
}
plain[o] = 0;
}
static int has(const char *s, const char *needle) {
return strstr(s, needle) != NULL;
}
static void test_intraword(void) {
render("see test_apply_patch.c now");
CHECK(has(plain, "test_apply_patch.c"));
CHECK(!has(out, ITALIC));
render("snake_case_x and 中文_变量_名 and a__b__c");
CHECK(has(plain, "snake_case_x and 中文_变量_名 and a__b__c"));
CHECK(!has(out, ITALIC) && !has(out, BOLD));
render("foo_bar_ baz");
CHECK(has(plain, "foo_bar_ baz") && !has(out, ITALIC));
}
static void test_pairs(void) {
render("*it* and _it_");
CHECK(has(out, ITALIC "it" ITALIC_OFF " and " ITALIC "it" ITALIC_OFF));
render("**b** __b__");
CHECK(has(out, BOLD "b" BOLD_OFF " " BOLD "b" BOLD_OFF));
render("***x***");
CHECK(has(out, BOLD ITALIC "x" ITALIC_OFF BOLD_OFF));
render("*a_b* end");
CHECK(has(out, ITALIC "a_b" ITALIC_OFF " end"));
/* CommonMark: intraword '*' pairs, a standalone __init__ is bold */
render("2*3*4");
CHECK(has(out, "2" ITALIC "3" ITALIC_OFF "4"));
render("call __init__ now");
CHECK(has(out, BOLD "init" BOLD_OFF " now"));
}
static void test_unmatched(void) {
render("a * b and 2 * 3 * 4");
CHECK(has(plain, "a * b and 2 * 3 * 4") && !has(out, ITALIC));
render("trailing star*");
CHECK(has(plain, "trailing star*") && !has(out, ITALIC));
render("*mix_ words");
CHECK(has(plain, "*mix_ words") && !has(out, ITALIC));
render("**open bold only");
CHECK(has(plain, "**open bold only") && !has(out, BOLD));
render("a `b");
CHECK(has(plain, "a `b") && !has(out, CODE));
}
static void test_code(void) {
render("run `a_b_c` and `*x*`");
CHECK(has(plain, "run a_b_c and *x*"));
CHECK(has(out, CODE) && !has(out, ITALIC));
}
static void test_cjk(void) {
render("**粗体**,后文");
CHECK(has(out, BOLD "粗体" BOLD_OFF ",后文"));
render("看**“引号”**这里");
CHECK(has(out, BOLD "“引号”" BOLD_OFF "这里"));
}
int main(void) {
test_intraword();
test_pairs();
test_unmatched();
test_code();
test_cjk();
printf("test_markdown: ok\n");
return 0;
}