From 26ac7e1c42821cd166e7cf91d657936dcc63accb Mon Sep 17 00:00:00 2001
From: Robin Stocker <robin@nibor.org>
Date: Mon, 22 Jan 2024 22:09:49 +1100
Subject: [PATCH] Don't discard trailing empty lines in code blocks

---
 .../markdown/CoreMarkdownNodeRenderer.java    | 25 +++++++++++++++----
 .../markdown/SpecMarkdownRendererTest.java    |  2 +-
 2 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
index 5e883333..a135d73e 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
@@ -8,6 +8,7 @@ import org.commonmark.renderer.NodeRenderer;
 
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -189,7 +190,7 @@ public class CoreMarkdownNodeRenderer extends AbstractVisitor implements NodeRen
         }
         writer.line();
         if (!literal.isEmpty()) {
-            String[] lines = literal.split("\n");
+            List<String> lines = getLines(literal);
             for (String line : lines) {
                 writer.write(line);
                 writer.line();
@@ -243,15 +244,15 @@ public class CoreMarkdownNodeRenderer extends AbstractVisitor implements NodeRen
     @Override
     public void visit(IndentedCodeBlock indentedCodeBlock) {
         String literal = indentedCodeBlock.getLiteral();
-        String[] lines = literal.split("\n");
         // We need to respect line prefixes which is why we need to write it line by line (e.g. an indented code block
         // within a block quote)
         writer.write("    ");
         writer.pushPrefix("    ");
-        for (int i = 0; i < lines.length; i++) {
-            String line = lines[i];
+        List<String> lines = getLines(literal);
+        for (int i = 0; i < lines.size(); i++) {
+            String line = lines.get(i);
             writer.write(line);
-            if (i != lines.length - 1) {
+            if (i != lines.size() - 1) {
                 writer.line();
             }
         }
@@ -329,6 +330,20 @@ public class CoreMarkdownNodeRenderer extends AbstractVisitor implements NodeRen
         return sb.toString();
     }
 
+    private static List<String> getLines(String literal) {
+        // Without -1, split would discard all trailing empty strings, which is not what we want, e.g. it would
+        // return the same result for "abc", "abc\n" and "abc\n\n".
+        // With -1, it returns ["abc"], ["abc", ""] and ["abc", "", ""].
+        String[] parts = literal.split("\n", -1);
+        if (parts[parts.length - 1].isEmpty()) {
+            // But we don't want the last empty string, as "\n" is used as a line terminator (not a separator),
+            // so return without the last element.
+            return Arrays.asList(parts).subList(0, parts.length - 1);
+        } else {
+            return Arrays.asList(parts);
+        }
+    }
+
     private void writeLinkLike(String title, String destination, Node node, String opener) {
         writer.write(opener);
         visitChildren(node);
diff --git a/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java b/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java
index d50349d3..4e9396e7 100644
--- a/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java
+++ b/commonmark/src/test/java/org/commonmark/renderer/markdown/SpecMarkdownRendererTest.java
@@ -62,7 +62,7 @@ public class SpecMarkdownRendererTest {
             System.out.println();
         }
 
-        int expectedPassed = 621;
+        int expectedPassed = 622;
         assertTrue("Expected at least " + expectedPassed + " examples to pass but was " + passes.size(), passes.size() >= expectedPassed);
     }
 
-- 
GitLab