Skip to content

Commit

Permalink
8338525: Leading and trailing code blocks by indentation
Browse files Browse the repository at this point in the history
Reviewed-by: hannesw, prappo
  • Loading branch information
jonathan-gibbons committed Sep 24, 2024
1 parent b639661 commit 0b8c9f6
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -286,16 +286,17 @@ protected List<DCTree> content(Phase phase) {
int depth = 1; // only used when phase is INLINE
int pos = bp; // only used when phase is INLINE

if (textKind == DocTree.Kind.MARKDOWN) {
initMarkdownLine();
}

loop:
while (bp < buflen) {
switch (ch) {
case '\n', '\r' -> {
nextChar();
if (textKind == DocTree.Kind.MARKDOWN) {
markdown.update();
if (markdown.isIndentedCodeBlock()) {
markdown.skipLine();
}
initMarkdownLine();
}
}

Expand Down Expand Up @@ -488,6 +489,17 @@ void defaultContentCharacter() {
nextChar();
}

void initMarkdownLine() {
if (textStart == -1) {
textStart = bp;
}
markdown.update();
if (markdown.isIndentedCodeBlock()) {
markdown.skipLine();
lastNonWhite = bp - 1; // do not include newline or EOF
}
}

private IllegalStateException unknownTextKind(DocTree.Kind textKind) {
return new IllegalStateException(textKind.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,27 +592,33 @@ Pair<List<DCTree>, List<DCTree>> splitBody(List<? extends DocTree> list) {
case TEXT, MARKDOWN -> {
var peekedNext = iter.hasNext() ? alist.get(iter.nextIndex()) : null;
var content = getContent(dt);
int breakOffset = getSentenceBreak(dt.getKind(), content, peekedNext);
if (breakOffset > 0) {
// the end of sentence is within the current node;
// split it, skipping whitespace in between the two parts
var fsPart = newNode(dt.getKind(), dt.pos, content.substring(0, breakOffset).stripTrailing());
fs.add(fsPart);
int wsOffset = skipWhiteSpace(content, breakOffset);
if (wsOffset > 0) {
var bodyPart = newNode(dt.getKind(), dt.pos + wsOffset, content.substring(wsOffset));
body.add(bodyPart);
}
foundFirstSentence = true;
} else if (peekedNext != null && isSentenceBreak(peekedNext, false)) {
// the next node is a sentence break, so this is the end of the first sentence;
// remove trailing spaces
var fsPart = newNode(dt.getKind(), dt.pos, content.stripTrailing());
fs.add(fsPart);
if (isFirst && dt.getKind() == Kind.MARKDOWN && isIndented(content)) {
// begins with an indented code block (unusual), so no first sentence
body.add(dt);
foundFirstSentence = true;
} else {
// no sentence break found; keep scanning
fs.add(dt);
int breakOffset = getSentenceBreak(dt.getKind(), content, peekedNext);
if (breakOffset > 0) {
// the end of sentence is within the current node;
// split it, skipping whitespace in between the two parts
var fsPart = newNode(dt.getKind(), dt.pos, content.substring(0, breakOffset).stripTrailing());
fs.add(fsPart);
int wsOffset = skipWhiteSpace(content, breakOffset);
if (wsOffset > 0) {
var bodyPart = newNode(dt.getKind(), dt.pos + wsOffset, content.substring(wsOffset));
body.add(bodyPart);
}
foundFirstSentence = true;
} else if (peekedNext != null && isSentenceBreak(peekedNext, false)) {
// the next node is a sentence break, so this is the end of the first sentence;
// remove trailing spaces
var fsPart = newNode(dt.getKind(), dt.pos, content.stripTrailing());
fs.add(fsPart);
foundFirstSentence = true;
} else {
// no sentence break found; keep scanning
fs.add(dt);
}
}
}

Expand Down Expand Up @@ -651,6 +657,11 @@ private String getContent(DCTree dt) {
};
}

private static final Pattern INDENT = Pattern.compile(" {4}| {0,3}\t");
private boolean isIndented(String s) {
return INDENT.matcher(s).lookingAt();
}

private DCTree newNode(DocTree.Kind kind, int pos, String text) {
return switch (kind) {
case TEXT -> m.at(pos).newTextTree(text);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,4 +486,107 @@ public int hashCode() {
<dd><code><a href="NullPointerException.html" title="class in p">NullPointerException</a></code> - if other is <code>null</code></dd>
</dl>""");
}

@Test
public void testLeadingCodeBlock(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src,
"""
package p;
/// Leading code block
/// Lorum ipsum.
public class C { }
""");

javadoc("-d", base.resolve("api").toString(),
"--no-platform-links",
"--source-path", src.toString(),
"p");
checkExit(Exit.OK);

// check first sentence is empty in package summary file
checkOutput("p/package-summary.html", true,
"""
<div class="col-first even-row-color class-summary class-summary-tab2"><a href="C.html" title="class in p">C</a></div>
<div class="col-last even-row-color class-summary class-summary-tab2">&nbsp;</div>""");

checkOutput("p/C.html", true,
"""
<div class="block"><pre><code>Leading code block
</code></pre>
<p>Lorum ipsum.</p>""");

}

@Test
public void testTrailingCodeBlock(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src,
"""
package p;
/// Lorum ipsum.
///
/// Trailing code block
public class C { }
""");

javadoc("-d", base.resolve("api").toString(),
"--no-platform-links",
"--source-path", src.toString(),
"p");
checkExit(Exit.OK);

checkOutput("p/C.html", true,
"""
<div class="block"><p>Lorum ipsum.</p>
<pre><code>Trailing code block
</code></pre>
</div>""");
}

// this example is derived from the test case in JDK-8338525
@Test
public void testLeadingTrailingCodeBlockWithAnnotations(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src,
"""
package p;
public class C {
/// @Override
/// void m() {}
///
/// Plain text
///
/// @Override
/// void m() {}
public void m() {}
}""");

javadoc("-d", base.resolve("api").toString(),
"--no-platform-links",
"--source-path", src.toString(),
"p");
checkExit(Exit.OK);

checkOutput("p/C.html", true,
"""
<div class="col-first even-row-color method-summary-table method-summary-table-tab2 \
method-summary-table-tab4"><code>void</code></div>
<div class="col-second even-row-color method-summary-table method-summary-table-tab2 \
method-summary-table-tab4"><code><a href="#m()" class="member-name-link">m</a>()</code></div>
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 \
method-summary-table-tab4">&nbsp;</div>""",
"""
<div class="member-signature"><span class="modifiers">public</span>&nbsp;\
<span class="return-type">void</span>&nbsp;<span class="element-name">m</span>()</div>
<div class="block"><pre><code>@Override
void m() {}
</code></pre>
<p>Plain text</p>
<pre><code>@Override
void m() {}
</code></pre>
</div>""");

}
}
3 changes: 2 additions & 1 deletion test/langtools/tools/javac/doctree/DocCommentTester.java
Original file line number Diff line number Diff line change
Expand Up @@ -1043,8 +1043,9 @@ String normalize(String s, boolean isLineComment, boolean normalizeTags) {
.replaceAll("(\\{@value\\s+[^}]+)\\s+(})", "$1$2");
}

// See comment in MarkdownTest for explanation of dummy and Override
String normalizeFragment(String s) {
return s.replaceAll("\n[ \t]+@(?!([@*]|dummy))", "\n@");
return s.replaceAll("\n[ \t]+@(?!([@*]|(dummy|Override)))", "\n@");
}

int copyLiteral(String s, int start, StringBuilder sb) {
Expand Down
46 changes: 46 additions & 0 deletions test/langtools/tools/javac/doctree/MarkdownTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* In the tests for code spans and code blocks, "@dummy" is used as a dummy inline
* or block tag to verify that it is skipped as part of the code span or code block.
* In other words, "@dummy" should appear as a literal part of the Markdown content.
* ("@Override" is also treated the same way, as a commonly found annotation.)
* Conversely, standard tags are used to verify that a fragment of text is not being
* skipped as a code span or code block. In other words, they should be recognized as tags
* and not skipped as part of any Markdown content.
Expand Down Expand Up @@ -409,6 +410,32 @@ void indentedCodeBlock_afterFencedCodeBlock() { }
RawText[MARKDOWN, pos:85, .]
block tags: empty
]
*/

/// Indented Code Block
/// Lorum ipsum.
void indentedCodeBlock_leading() { }
/*
DocComment[DOC_COMMENT, pos:0
firstSentence: empty
body: 1
RawText[MARKDOWN, pos:0, ____Indented_Code_Block|Lorum_ipsum.]
block tags: empty
]
*/

/// Lorum ipsum.
///
/// Indented Code Block
void indentedCodeBlock_trailing() { }
/*
DocComment[DOC_COMMENT, pos:0
firstSentence: 1
RawText[MARKDOWN, pos:0, Lorum_ipsum.]
body: 1
RawText[MARKDOWN, pos:18, Indented_Code_Block]
block tags: empty
]
*/

///123.
Expand Down Expand Up @@ -613,5 +640,24 @@ void indent8() { }
]
*/

// The following test case is derived from the test case in JDK-8338525.

/// @Override
/// void m() { }
///
/// Plain text
///
/// @Override
/// void m() { }
void leadingTrailingCodeBlocksWithAnnos() { }
/*
DocComment[DOC_COMMENT, pos:0
firstSentence: empty
body: 1
RawText[MARKDOWN, pos:0, ____@Override|____void_m()_{_}||...||____@Override|____void_m()_{_}]
block tags: empty
]
*/


}

0 comments on commit 0b8c9f6

Please sign in to comment.