8338525: Leading and trailing code blocks by indentation
Reviewed-by: hannesw, prappo
This commit is contained in:
parent
b639661e79
commit
0b8c9f6d23
@ -286,16 +286,17 @@ public class DocCommentParser {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,6 +489,17 @@ public class DocCommentParser {
|
||||
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());
|
||||
}
|
||||
|
@ -592,27 +592,33 @@ public class DocTreeMaker implements DocTreeFactory {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,6 +657,11 @@ public class DocTreeMaker implements DocTreeFactory {
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -486,4 +486,107 @@ public class TestMarkdownCodeBlocks extends JavadocTester {
|
||||
<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"> </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"> </div>""",
|
||||
"""
|
||||
<div class="member-signature"><span class="modifiers">public</span> \
|
||||
<span class="return-type">void</span> <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>""");
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1043,8 +1043,9 @@ public class DocCommentTester {
|
||||
.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) {
|
||||
|
@ -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.
|
||||
@ -409,6 +410,32 @@ DocComment[DOC_COMMENT, pos:0
|
||||
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.
|
||||
@ -613,5 +640,24 @@ DocComment[DOC_COMMENT, pos:0
|
||||
]
|
||||
*/
|
||||
|
||||
// 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
|
||||
]
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user