From 7f2f7f8dff99fb1df3d23eddb604298d9f8951d8 Mon Sep 17 00:00:00 2001 From: zayeem shaikh Date: Tue, 13 Oct 2026 23:53:37 +0530 Subject: [PATCH] fix hard line break round-trip in commonmark renderer --- api_test/main.c | 31 +++++++++++++++++++++++++++++++ src/commonmark.c | 6 +++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/api_test/main.c b/api_test/main.c index 877d72e9f..0df1a7d8d 100644 --- a/api_test/main.c +++ b/api_test/main.c @@ -773,6 +773,37 @@ static void render_commonmark(test_batch_runner *runner) { free(commonmark); cmark_node_free(empty_item_doc); + + // Hard line breaks are written as a backslash break in every position. + // Two trailing spaces would be stripped as insignificant whitespace at the + // start of a line, or after a space, losing the break on a round trip. + static const char lead_break[] = "\\\nfoo\n"; + cmark_node *lead_break_doc = + cmark_parse_document(lead_break, sizeof(lead_break) - 1, CMARK_OPT_DEFAULT); + commonmark = cmark_render_commonmark(lead_break_doc, CMARK_OPT_DEFAULT, 0); + STR_EQ(runner, commonmark, "\\\nfoo\n", + "render hard break at start of line"); + free(commonmark); + cmark_node_free(lead_break_doc); + + static const char space_break[] = "foo \\\nbar\n"; + cmark_node *space_break_doc = + cmark_parse_document(space_break, sizeof(space_break) - 1, CMARK_OPT_DEFAULT); + commonmark = cmark_render_commonmark(space_break_doc, CMARK_OPT_DEFAULT, 0); + STR_EQ(runner, commonmark, "foo \\\nbar\n", + "render hard break after a space"); + free(commonmark); + cmark_node_free(space_break_doc); + + static const char plain_break[] = "foo\\\nbar\n"; + cmark_node *plain_break_doc = + cmark_parse_document(plain_break, sizeof(plain_break) - 1, CMARK_OPT_DEFAULT); + commonmark = cmark_render_commonmark(plain_break_doc, CMARK_OPT_DEFAULT, 0); + STR_EQ(runner, commonmark, "foo\\\nbar\n", + "render ordinary hard break"); + free(commonmark); + cmark_node_free(plain_break_doc); + cmark_node_free(text); cmark_node_free(doc); } diff --git a/src/commonmark.c b/src/commonmark.c index 8a2bc5184..44ee695d2 100644 --- a/src/commonmark.c +++ b/src/commonmark.c @@ -331,7 +331,11 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node, case CMARK_NODE_LINEBREAK: if (!(CMARK_OPT_HARDBREAKS & options)) { - LIT(" "); + // A backslash line break is unambiguous in every position. Two trailing + // spaces are only recognized as a hard break when they follow text on + // the line; at the start of a line, or after a space, they are stripped + // as insignificant whitespace and the break is lost on a round trip. + LIT("\\"); } CR(); break;