Skip to content

Commit 0b8c9f6

Browse files
committedSep 24, 2024
8338525: Leading and trailing code blocks by indentation
Reviewed-by: hannesw, prappo
1 parent b639661 commit 0b8c9f6

File tree

5 files changed

+197
-24
lines changed

5 files changed

+197
-24
lines changed
 

‎src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java

+16-4
Original file line numberDiff line numberDiff line change
@@ -286,16 +286,17 @@ protected List<DCTree> content(Phase phase) {
286286
int depth = 1; // only used when phase is INLINE
287287
int pos = bp; // only used when phase is INLINE
288288

289+
if (textKind == DocTree.Kind.MARKDOWN) {
290+
initMarkdownLine();
291+
}
292+
289293
loop:
290294
while (bp < buflen) {
291295
switch (ch) {
292296
case '\n', '\r' -> {
293297
nextChar();
294298
if (textKind == DocTree.Kind.MARKDOWN) {
295-
markdown.update();
296-
if (markdown.isIndentedCodeBlock()) {
297-
markdown.skipLine();
298-
}
299+
initMarkdownLine();
299300
}
300301
}
301302

@@ -488,6 +489,17 @@ void defaultContentCharacter() {
488489
nextChar();
489490
}
490491

492+
void initMarkdownLine() {
493+
if (textStart == -1) {
494+
textStart = bp;
495+
}
496+
markdown.update();
497+
if (markdown.isIndentedCodeBlock()) {
498+
markdown.skipLine();
499+
lastNonWhite = bp - 1; // do not include newline or EOF
500+
}
501+
}
502+
491503
private IllegalStateException unknownTextKind(DocTree.Kind textKind) {
492504
return new IllegalStateException(textKind.toString());
493505
}

‎src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java

+30-19
Original file line numberDiff line numberDiff line change
@@ -592,27 +592,33 @@ Pair<List<DCTree>, List<DCTree>> splitBody(List<? extends DocTree> list) {
592592
case TEXT, MARKDOWN -> {
593593
var peekedNext = iter.hasNext() ? alist.get(iter.nextIndex()) : null;
594594
var content = getContent(dt);
595-
int breakOffset = getSentenceBreak(dt.getKind(), content, peekedNext);
596-
if (breakOffset > 0) {
597-
// the end of sentence is within the current node;
598-
// split it, skipping whitespace in between the two parts
599-
var fsPart = newNode(dt.getKind(), dt.pos, content.substring(0, breakOffset).stripTrailing());
600-
fs.add(fsPart);
601-
int wsOffset = skipWhiteSpace(content, breakOffset);
602-
if (wsOffset > 0) {
603-
var bodyPart = newNode(dt.getKind(), dt.pos + wsOffset, content.substring(wsOffset));
604-
body.add(bodyPart);
605-
}
606-
foundFirstSentence = true;
607-
} else if (peekedNext != null && isSentenceBreak(peekedNext, false)) {
608-
// the next node is a sentence break, so this is the end of the first sentence;
609-
// remove trailing spaces
610-
var fsPart = newNode(dt.getKind(), dt.pos, content.stripTrailing());
611-
fs.add(fsPart);
595+
if (isFirst && dt.getKind() == Kind.MARKDOWN && isIndented(content)) {
596+
// begins with an indented code block (unusual), so no first sentence
597+
body.add(dt);
612598
foundFirstSentence = true;
613599
} else {
614-
// no sentence break found; keep scanning
615-
fs.add(dt);
600+
int breakOffset = getSentenceBreak(dt.getKind(), content, peekedNext);
601+
if (breakOffset > 0) {
602+
// the end of sentence is within the current node;
603+
// split it, skipping whitespace in between the two parts
604+
var fsPart = newNode(dt.getKind(), dt.pos, content.substring(0, breakOffset).stripTrailing());
605+
fs.add(fsPart);
606+
int wsOffset = skipWhiteSpace(content, breakOffset);
607+
if (wsOffset > 0) {
608+
var bodyPart = newNode(dt.getKind(), dt.pos + wsOffset, content.substring(wsOffset));
609+
body.add(bodyPart);
610+
}
611+
foundFirstSentence = true;
612+
} else if (peekedNext != null && isSentenceBreak(peekedNext, false)) {
613+
// the next node is a sentence break, so this is the end of the first sentence;
614+
// remove trailing spaces
615+
var fsPart = newNode(dt.getKind(), dt.pos, content.stripTrailing());
616+
fs.add(fsPart);
617+
foundFirstSentence = true;
618+
} else {
619+
// no sentence break found; keep scanning
620+
fs.add(dt);
621+
}
616622
}
617623
}
618624

@@ -651,6 +657,11 @@ private String getContent(DCTree dt) {
651657
};
652658
}
653659

660+
private static final Pattern INDENT = Pattern.compile(" {4}| {0,3}\t");
661+
private boolean isIndented(String s) {
662+
return INDENT.matcher(s).lookingAt();
663+
}
664+
654665
private DCTree newNode(DocTree.Kind kind, int pos, String text) {
655666
return switch (kind) {
656667
case TEXT -> m.at(pos).newTextTree(text);

‎test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownCodeBlocks.java

+103
Original file line numberDiff line numberDiff line change
@@ -486,4 +486,107 @@ public int hashCode() {
486486
<dd><code><a href="NullPointerException.html" title="class in p">NullPointerException</a></code> - if other is <code>null</code></dd>
487487
</dl>""");
488488
}
489+
490+
@Test
491+
public void testLeadingCodeBlock(Path base) throws Exception {
492+
Path src = base.resolve("src");
493+
tb.writeJavaFiles(src,
494+
"""
495+
package p;
496+
/// Leading code block
497+
/// Lorum ipsum.
498+
public class C { }
499+
""");
500+
501+
javadoc("-d", base.resolve("api").toString(),
502+
"--no-platform-links",
503+
"--source-path", src.toString(),
504+
"p");
505+
checkExit(Exit.OK);
506+
507+
// check first sentence is empty in package summary file
508+
checkOutput("p/package-summary.html", true,
509+
"""
510+
<div class="col-first even-row-color class-summary class-summary-tab2"><a href="C.html" title="class in p">C</a></div>
511+
<div class="col-last even-row-color class-summary class-summary-tab2">&nbsp;</div>""");
512+
513+
checkOutput("p/C.html", true,
514+
"""
515+
<div class="block"><pre><code>Leading code block
516+
</code></pre>
517+
<p>Lorum ipsum.</p>""");
518+
519+
}
520+
521+
@Test
522+
public void testTrailingCodeBlock(Path base) throws Exception {
523+
Path src = base.resolve("src");
524+
tb.writeJavaFiles(src,
525+
"""
526+
package p;
527+
/// Lorum ipsum.
528+
///
529+
/// Trailing code block
530+
public class C { }
531+
""");
532+
533+
javadoc("-d", base.resolve("api").toString(),
534+
"--no-platform-links",
535+
"--source-path", src.toString(),
536+
"p");
537+
checkExit(Exit.OK);
538+
539+
checkOutput("p/C.html", true,
540+
"""
541+
<div class="block"><p>Lorum ipsum.</p>
542+
<pre><code>Trailing code block
543+
</code></pre>
544+
</div>""");
545+
}
546+
547+
// this example is derived from the test case in JDK-8338525
548+
@Test
549+
public void testLeadingTrailingCodeBlockWithAnnotations(Path base) throws Exception {
550+
Path src = base.resolve("src");
551+
tb.writeJavaFiles(src,
552+
"""
553+
package p;
554+
public class C {
555+
/// @Override
556+
/// void m() {}
557+
///
558+
/// Plain text
559+
///
560+
/// @Override
561+
/// void m() {}
562+
public void m() {}
563+
}""");
564+
565+
javadoc("-d", base.resolve("api").toString(),
566+
"--no-platform-links",
567+
"--source-path", src.toString(),
568+
"p");
569+
checkExit(Exit.OK);
570+
571+
checkOutput("p/C.html", true,
572+
"""
573+
<div class="col-first even-row-color method-summary-table method-summary-table-tab2 \
574+
method-summary-table-tab4"><code>void</code></div>
575+
<div class="col-second even-row-color method-summary-table method-summary-table-tab2 \
576+
method-summary-table-tab4"><code><a href="#m()" class="member-name-link">m</a>()</code></div>
577+
<div class="col-last even-row-color method-summary-table method-summary-table-tab2 \
578+
method-summary-table-tab4">&nbsp;</div>""",
579+
"""
580+
<div class="member-signature"><span class="modifiers">public</span>&nbsp;\
581+
<span class="return-type">void</span>&nbsp;<span class="element-name">m</span>()</div>
582+
<div class="block"><pre><code>@Override
583+
void m() {}
584+
</code></pre>
585+
<p>Plain text</p>
586+
<pre><code>@Override
587+
void m() {}
588+
</code></pre>
589+
</div>""");
590+
591+
}
489592
}

‎test/langtools/tools/javac/doctree/DocCommentTester.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -1043,8 +1043,9 @@ String normalize(String s, boolean isLineComment, boolean normalizeTags) {
10431043
.replaceAll("(\\{@value\\s+[^}]+)\\s+(})", "$1$2");
10441044
}
10451045

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

10501051
int copyLiteral(String s, int start, StringBuilder sb) {

‎test/langtools/tools/javac/doctree/MarkdownTest.java

+46
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* In the tests for code spans and code blocks, "@dummy" is used as a dummy inline
4141
* or block tag to verify that it is skipped as part of the code span or code block.
4242
* In other words, "@dummy" should appear as a literal part of the Markdown content.
43+
* ("@Override" is also treated the same way, as a commonly found annotation.)
4344
* Conversely, standard tags are used to verify that a fragment of text is not being
4445
* skipped as a code span or code block. In other words, they should be recognized as tags
4546
* and not skipped as part of any Markdown content.
@@ -409,6 +410,32 @@ void indentedCodeBlock_afterFencedCodeBlock() { }
409410
RawText[MARKDOWN, pos:85, .]
410411
block tags: empty
411412
]
413+
*/
414+
415+
/// Indented Code Block
416+
/// Lorum ipsum.
417+
void indentedCodeBlock_leading() { }
418+
/*
419+
DocComment[DOC_COMMENT, pos:0
420+
firstSentence: empty
421+
body: 1
422+
RawText[MARKDOWN, pos:0, ____Indented_Code_Block|Lorum_ipsum.]
423+
block tags: empty
424+
]
425+
*/
426+
427+
/// Lorum ipsum.
428+
///
429+
/// Indented Code Block
430+
void indentedCodeBlock_trailing() { }
431+
/*
432+
DocComment[DOC_COMMENT, pos:0
433+
firstSentence: 1
434+
RawText[MARKDOWN, pos:0, Lorum_ipsum.]
435+
body: 1
436+
RawText[MARKDOWN, pos:18, Indented_Code_Block]
437+
block tags: empty
438+
]
412439
*/
413440

414441
///123.
@@ -613,5 +640,24 @@ void indent8() { }
613640
]
614641
*/
615642

643+
// The following test case is derived from the test case in JDK-8338525.
644+
645+
/// @Override
646+
/// void m() { }
647+
///
648+
/// Plain text
649+
///
650+
/// @Override
651+
/// void m() { }
652+
void leadingTrailingCodeBlocksWithAnnos() { }
653+
/*
654+
DocComment[DOC_COMMENT, pos:0
655+
firstSentence: empty
656+
body: 1
657+
RawText[MARKDOWN, pos:0, ____@Override|____void_m()_{_}||...||____@Override|____void_m()_{_}]
658+
block tags: empty
659+
]
660+
*/
661+
616662

617663
}

0 commit comments

Comments
 (0)
Please sign in to comment.