Skip to content

Commit 585edc3

Browse files
Fix print idempotency for Javadoc <pre><code> blocks on Java 25 (#7464)
Java 25's `DocCommentTree` strips leading whitespace (including newlines) from text nodes, so the source string had a `\n` that the `DCText` body didn't. `visitText` silently advanced past it without emitting a `LineBreak`, and the orphaned line break was flushed at the end of the comment as a stray ` *` line. Consume any source newline that's missing from the text node before processing each character, emitting the corresponding `LineBreak`.
1 parent 090c2ad commit 585edc3

2 files changed

Lines changed: 96 additions & 0 deletions

File tree

rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25JavadocVisitor.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,19 @@ public List<Javadoc> visitText(String node) {
10551055
StringBuilder text = new StringBuilder();
10561056
for (int i = 0; i < node.length(); i++) {
10571057
char c = node.charAt(i);
1058+
// Java 25's AST may strip leading line breaks from text nodes (e.g. after `<pre><code>`).
1059+
// If source has a newline here that's missing from the node, emit a LineBreak first.
1060+
while (c != '\n' && cursor < source.length() && source.charAt(cursor) == '\n') {
1061+
if (text.length() > 0) {
1062+
texts.add(new Javadoc.Text(randomId(), Markers.EMPTY, text.toString()));
1063+
text = new StringBuilder();
1064+
}
1065+
cursor++;
1066+
Javadoc.LineBreak lineBreak = lineBreaks.remove(cursor);
1067+
if (lineBreak != null) {
1068+
texts.add(lineBreak);
1069+
}
1070+
}
10581071
if (c == '\n') {
10591072
if (text.length() > 0) {
10601073
texts.add(new Javadoc.Text(randomId(), Markers.EMPTY, text.toString()));

rewrite-java-tck/src/main/java/org/openrewrite/java/tree/JavadocTest.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,89 @@ void method() {}
15441544
);
15451545
}
15461546

1547+
@Test
1548+
void preCodeBlockWithNewlineAfterOpeningTag() {
1549+
rewriteRun(
1550+
java(
1551+
"""
1552+
/**
1553+
* <p>Example:
1554+
*
1555+
* <pre><code>
1556+
* class Outer {
1557+
* static class Inner {
1558+
* }
1559+
* }
1560+
* </code></pre>
1561+
*/
1562+
public @interface Test {
1563+
}
1564+
"""
1565+
)
1566+
);
1567+
}
1568+
1569+
@Test
1570+
void preCodeBlockTwoInstances() {
1571+
rewriteRun(
1572+
java(
1573+
"""
1574+
/**
1575+
* <pre><code>
1576+
* first block
1577+
* </code></pre>
1578+
*
1579+
* <pre><code>
1580+
* second block
1581+
* </code></pre>
1582+
*/
1583+
public @interface Test {
1584+
}
1585+
"""
1586+
)
1587+
);
1588+
}
1589+
1590+
@Test
1591+
void preCodeBlockWithInlineLiteralTag() {
1592+
rewriteRun(
1593+
java(
1594+
"""
1595+
/**
1596+
* Defines a Hilt component.
1597+
*
1598+
* <p>Example defining a root component, {@code ParentComponent}:
1599+
*
1600+
* <pre><code>
1601+
* {@literal @}ParentScoped
1602+
* {@literal @}DefineComponent
1603+
* interface ParentComponent {}
1604+
* </code></pre>
1605+
*/
1606+
public @interface Test {
1607+
}
1608+
"""
1609+
)
1610+
);
1611+
}
1612+
1613+
@Test
1614+
void preCodeBlockSameLineAsText() {
1615+
rewriteRun(
1616+
java(
1617+
"""
1618+
/**
1619+
* Some text <pre><code>
1620+
* content
1621+
* </code></pre>
1622+
*/
1623+
public @interface Test {
1624+
}
1625+
"""
1626+
)
1627+
);
1628+
}
1629+
15471630
@Issue("https://github.com/openrewrite/rewrite/issues/1409")
15481631
@Test
15491632
void trailingWhitespaceWithWhitespaceOnEmptyLine() {

0 commit comments

Comments
 (0)