git » sdk » commit 3710773

unstyled lines are still lines

author Stephen Paul Weber
2026-04-11 03:44:09 UTC
committer Stephen Paul Weber
2026-04-11 03:59:23 UTC
parent 12360ae68615dd11bb2f18c970c8a7b4eac90ba0

unstyled lines are still lines

fix whitespace handling in XEP0393 parser to not keep newlines inside of
divs for no reason

borogove/ChatMessage.hx +5 -1
borogove/XEP0393.hx +2 -1
test/TestAll.hx +1 -0
test/TestChatMessage.hx +47 -0
test/TestXEP0393.hx +7 -18

diff --git a/borogove/ChatMessage.hx b/borogove/ChatMessage.hx
index affe8c9..e5a15f5 100644
--- a/borogove/ChatMessage.hx
+++ b/borogove/ChatMessage.hx
@@ -392,7 +392,11 @@ class ChatMessage {
 				codepoints.splice(fallback.start, (fallback.end - fallback.start));
 			}
 			final body = codepoints.join("");
-			return payloads.find((p) -> p.attr.get("xmlns") == "urn:xmpp:styling:0" && p.name == "unstyled") == null ? XEP0393.parse(body).map(s -> Element(s)) : [CData(new TextNode(body))];
+			if (payloads.find((p) -> p.attr.get("xmlns") == "urn:xmpp:styling:0" && p.name == "unstyled") == null) {
+				return XEP0393.parse(body).map(s -> Element(s));
+			} else {
+				return body.split("\n").map(line -> Element(new Stanza("div").text(line)));
+			}
 		}
 	}
 
diff --git a/borogove/XEP0393.hx b/borogove/XEP0393.hx
index a66f382..4ad6c7a 100644
--- a/borogove/XEP0393.hx
+++ b/borogove/XEP0393.hx
@@ -186,8 +186,9 @@ class XEP0393 {
 			var end = 0;
 			final styledLength = styled.length;
 			while (end < styledLength && styled.charAt(end) != "\n") end++;
+			final lineEnd = end;
 			if (end < styledLength && styled.charAt(end) == "\n") end++;
-			return { block: new Stanza("div").addChildNodes(parseSpans(styled.substr(0, end))), rest: styled.substr(end) };
+			return { block: new Stanza("div").addChildNodes(parseSpans(styled.substr(0, lineEnd))), rest: styled.substr(end) };
 		}
 	}
 
diff --git a/test/TestAll.hx b/test/TestAll.hx
index 535cc9b..6eb702e 100644
--- a/test/TestAll.hx
+++ b/test/TestAll.hx
@@ -6,6 +6,7 @@ import utest.ui.Report;
 class TestAll {
 	public static function main() {
 		utest.UTest.run([
+			new TestChatMessage(),
 			new TestSessionDescription(),
 			new TestChatMessageBuilder(),
 			new TestStanza(),
diff --git a/test/TestChatMessage.hx b/test/TestChatMessage.hx
new file mode 100644
index 0000000..bd6238f
--- /dev/null
+++ b/test/TestChatMessage.hx
@@ -0,0 +1,47 @@
+package test;
+
+import utest.Assert;
+import borogove.Stanza;
+import borogove.ChatMessage;
+import borogove.JID;
+import borogove.Message;
+
+@:access(borogove)
+class TestChatMessage extends utest.Test {
+	public function testUnstyledBody() {
+		final stanza = new Stanza("message");
+		stanza.attr.set("id", "test-id-1");
+		stanza.attr.set("from", "alice@example.com");
+		stanza.attr.set("to", "bob@example.com");
+		stanza.attr.set("type", "chat");
+		stanza.addChild(new Stanza("body").text("line 1\n*line 2*"));
+		stanza.addChild(new Stanza("unstyled", {xmlns: "urn:xmpp:styling:0"}));
+
+		final msg = Message.fromStanza(stanza, JID.parse("bob@example.com"));
+		switch (msg.parsed) {
+			case ChatMessageStanza(m):
+				Assert.equals("<div>line 1</div><div>*line 2*</div>", m.body().toString());
+				Assert.equals("line 1\n*line 2*", m.body().toPlainText());
+			default:
+				Assert.fail("Expected ChatMessageStanza");
+		}
+	}
+
+	public function testStyledBody() {
+		final stanza = new Stanza("message");
+		stanza.attr.set("id", "test-id-1");
+		stanza.attr.set("from", "alice@example.com");
+		stanza.attr.set("to", "bob@example.com");
+		stanza.attr.set("type", "chat");
+		stanza.addChild(new Stanza("body").text("line 1\n*line 2*"));
+
+		final msg = Message.fromStanza(stanza, JID.parse("bob@example.com"));
+		switch (msg.parsed) {
+			case ChatMessageStanza(m):
+				Assert.equals("<div>line 1</div><div><strong>line 2</strong></div>", m.body().toString());
+				Assert.equals("line 1\n*line 2*", m.body().toPlainText());
+			default:
+				Assert.fail("Expected ChatMessageStanza");
+		}
+	}
+}
diff --git a/test/TestXEP0393.hx b/test/TestXEP0393.hx
index 757988b..6698278 100644
--- a/test/TestXEP0393.hx
+++ b/test/TestXEP0393.hx
@@ -13,9 +13,7 @@ class TestXEP0393 extends utest.Test {
 
 	public function testSpansDoNotEscapeBlocks() {
 		Assert.equals(
-			"<div>There are three blocks in this body, one per line,
-</div><div>but there is no *formatting
-</div><div>as spans* may not escape blocks.</div>",
+			"<div>There are three blocks in this body, one per line,</div><div>but there is no *formatting</div><div>as spans* may not escape blocks.</div>",
 			toHtml("There are three blocks in this body, one per line,
 but there is no *formatting
 as spans* may not escape blocks.")
@@ -25,8 +23,7 @@ as spans* may not escape blocks.")
 	public function testPreformattedBlock() {
 		Assert.equals(
 			"<pre>(println \"Hello, world!\")
-</pre><div>
-</div><div>This should show up as monospace, preformatted text ⤴</div>",
+</pre><div/><div>This should show up as monospace, preformatted text ⤴</div>",
 			toHtml("```ignored
 (println \"Hello, world!\")
 ```
@@ -38,9 +35,7 @@ This should show up as monospace, preformatted text ⤴")
 	public function testPreformattedBlockUnterminated() {
 		Assert.equals(
 			"<blockquote><pre>(println \"Hello, world!\")
-</pre></blockquote><div>
-</div><div>The entire blockquote is a preformatted text block, but this line
-</div><div>is plaintext!</div>",
+</pre></blockquote><div/><div>The entire blockquote is a preformatted text block, but this line</div><div>is plaintext!</div>",
 			toHtml("> ```ignored
 > (println \"Hello, world!\")
 
@@ -51,9 +46,7 @@ is plaintext!")
 
 	public function testQuotation() {
 		Assert.equals(
-			"<blockquote><div>That that is, is.
-</div></blockquote><div>
-</div><div>Said the old hermit of Prague.</div>",
+			"<blockquote><div>That that is, is.</div></blockquote><div/><div>Said the old hermit of Prague.</div>",
 			toHtml("> That that is, is.
 
 Said the old hermit of Prague.")
@@ -62,10 +55,7 @@ Said the old hermit of Prague.")
 
 	public function testNestedQuotation() {
 		Assert.equals(
-			"<blockquote><blockquote><div>That that is, is.
-</div></blockquote><div>Said the old hermit of Prague.
-</div></blockquote><div>
-</div><div>Who?</div>",
+			"<blockquote><blockquote><div>That that is, is.</div></blockquote><div>Said the old hermit of Prague.</div></blockquote><div/><div>Who?</div>",
 			toHtml(">> That that is, is.
 > Said the old hermit of Prague.
 
@@ -158,7 +148,7 @@ Who?")
 
 	public function testNotStrong3() {
 		Assert.equals(
-			"<div>*not \n</div><div> strong</div>",
+			"<div>*not </div><div> strong</div>",
 			toHtml("*not \n strong")
 		);
 	}
@@ -210,8 +200,7 @@ Who?")
 
 	public function testAutolink() {
 		Assert.equals(
-			"<blockquote><div><a href=\"https://example.com\">example.com</a>
-</div></blockquote>",
+			"<blockquote><div><a href=\"https://example.com\">example.com</a></div></blockquote>",
 			toHtml("> example.com")
 		);
 	}