Skip to content

Commit

Permalink
[23] Parsing details for markdown comments eclipse-jdt#2824
Browse files Browse the repository at this point in the history
+ codeblock cannot interrupt a paragraph
+ separate handling of code blocks indented vs fenced
  + fence does not terminate indented code block

fixes eclipse-jdt#2824
  • Loading branch information
stephan-herrmann committed Aug 17, 2024
1 parent e742bea commit 31052e2
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ protected boolean commentParse() {
consumeToken();
}

if (this.markdown && !Character.isWhitespace(nextCharacter) && nextCharacter != '/' && nextCharacter != '`') {
this.markdownHelper.recordText();
}
// Consume rules depending on the read character
switch (nextCharacter) {
case '@' :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public interface IMarkdownCommentHelper {
*/
boolean recordSignificantLeadingSpace();

void recordText();

boolean isInCodeBlock();

/** Retrieve the start of the current text, possibly including significant leading whitespace. */
Expand All @@ -56,7 +58,6 @@ static IMarkdownCommentHelper create(AbstractCommentParser parser) {
return new NullMarkdownHelper();
}
}

}

class NullMarkdownHelper implements IMarkdownCommentHelper {
Expand All @@ -75,6 +76,10 @@ public boolean recordSignificantLeadingSpace() {
return false;
}
@Override
public void recordText() {
// nop
}
@Override
public boolean isInCodeBlock() {
return false;
}
Expand All @@ -98,7 +103,10 @@ class MarkdownCommentHelper implements IMarkdownCommentHelper {
int leadingSpaces = 0;
int markdownLineStart = -1;
int backTickCount = 0;
boolean insideFence = false;
boolean insideIndentedCodeBlock = false;
boolean insideFencedCodeBlock = false;
boolean isBlankLine = true;
boolean previousIsBlankLine = true;

public MarkdownCommentHelper(int lineStart, int commonIndent) {
this.markdownLineStart = lineStart;
Expand All @@ -117,28 +125,41 @@ public void recordSlash(int nextIndex) {

@Override
public void recordBackTick(boolean lineStarted) {
if (this.insideIndentedCodeBlock) {
return;
}
if (this.backTickCount < 3) {
if (this.backTickCount == 0 && lineStarted) {
return;
}
if (++this.backTickCount == 3) {
this.insideFence^=true;
this.insideFencedCodeBlock^=true;
}
}
}

@Override
public boolean recordSignificantLeadingSpace() {
if (this.markdownLineStart != -1) {
if (++this.leadingSpaces > this.commonIndent)
if (++this.leadingSpaces > this.commonIndent) {
if (!this.insideFencedCodeBlock && this.previousIsBlankLine && this.leadingSpaces - this.commonIndent >= 4)
this.insideIndentedCodeBlock = true;
return true;
}
}
return false;
}

@Override
public void recordText() {
this.isBlankLine = false;
if (this.leadingSpaces - this.commonIndent < 4)
this.insideIndentedCodeBlock = false;
}

@Override
public boolean isInCodeBlock() {
return this.insideFence || (this.leadingSpaces - this.commonIndent >= 4);
return this.insideIndentedCodeBlock || this.insideFencedCodeBlock;
}

@Override
Expand All @@ -156,6 +177,8 @@ public void resetLineStart() {

@Override
public void resetAtLineEnd() {
this.previousIsBlankLine = this.isBlankLine;
this.isBlankLine = true;
this.slashCount = 0;
this.leadingSpaces = 0;
this.markdownLineStart = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3368,4 +3368,154 @@ void numberOfSpaces2() { }
}
}
}

public void testGH2808_codeAfterPara() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy("/Converter_23/src/markdown/gh2808/CodeAfterPara.java",
"""
package markdown.gh2808;
public class CodeAfterPara {
/// Plain Text
/// @Override public void four() // four significant spaces but no blank line
void noBlankLine() { }
/// Plain Text
/// \s
/// @Override public void four() // four significant spaces after blank line
void withBlankLine() { }
}
"""
);
CompilationUnit compilUnit = (CompilationUnit) runConversion(this.workingCopies[0], true);
if (this.docCommentSupport.equals(JavaCore.ENABLED)) {
List unitComments = compilUnit.getCommentList();
assertEquals("Wrong number of comments", 2, unitComments.size());

{ // comments on noBlankLine()
Comment comment = (Comment) unitComments.get(0);
assertEquals("Comment should be javadoc", comment.getNodeType(), ASTNode.JAVADOC);
List tagList = ((Javadoc) comment).tags();

String[] tags = {
null,
"@Override" // parsed as tag, due to lack of blank line
};
String[][] lines = {
{"Plain Text"},
{" public void four() // four significant spaces but no blank line"}
};
assertTagsAndTexts(tagList, tags, lines);
}

{ // comments on withBlankLine()
Comment comment = (Comment) unitComments.get(1);
assertEquals("Comment should be javadoc", comment.getNodeType(), ASTNode.JAVADOC);
String[] tags = {
null
};
String[][] lines = {
{ // one TagElement with 2 TextElements
"Plain Text",
" @Override public void four() // four significant spaces after blank line"
}
};
assertTagsAndTexts(((Javadoc) comment).tags(), tags, lines);

}
}
}

public void testGH2808_terminatingAnIndentedCodeBlock() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy("/Converter_23/src/markdown/gh2808/BlockEnding.java",
"""
package markdown.gh2808;
public class BlockEnding {
/// Plain Text
///
/// @Override public void four()
/// ```
/// /// doc
/// /// ```
/// /// @Override Nested Code
/// /// ```
/// ```
void indentedWithFence() { }
/// Plain Text
///
/// @Override public void four()
/// Plain again
void paraAfterCode() { }
}
"""
);
CompilationUnit compilUnit = (CompilationUnit) runConversion(this.workingCopies[0], true);
if (this.docCommentSupport.equals(JavaCore.ENABLED)) {
List unitComments = compilUnit.getCommentList();
assertEquals("Wrong number of comments", 2, unitComments.size());

{ // comments on indentedWithFence(): fence does not terminate indented code block, even nested doc comment is give verbatim
Comment comment = (Comment) unitComments.get(0);
assertEquals("Comment should be javadoc", comment.getNodeType(), ASTNode.JAVADOC);
List tagList = ((Javadoc) comment).tags();

String[] tags = {
null
};
String[][] lines = {
{ // one TagElement with many TextElements
"Plain Text",
" @Override public void four()",
" ```",
" /// doc",
" /// ```",
" /// @Override Nested Code",
" /// ```",
" ```"
}
};
assertTagsAndTexts(tagList, tags, lines);
}

{ // comments on paraAfterCode() (requires jdt.ui to see what is rendered as code)
Comment comment = (Comment) unitComments.get(1);
assertEquals("Comment should be javadoc", comment.getNodeType(), ASTNode.JAVADOC);
String[] tags = {
null
};
String[][] lines = {
{
"Plain Text",
" @Override public void four()",
"Plain again"
}
};
assertTagsAndTexts(((Javadoc) comment).tags(), tags, lines);
}
}
}

protected void assertTagsAndTexts(List<ASTNode> tagList, String[] tags, String[][] liness) {
assertEquals(this.prefix+"Wrong number of tags", tags.length, tagList.size());

for (int i = 0; i < liness.length; i++) {
ASTNode tagNode = tagList.get(i);
String tag = tags[i];
assertEquals(this.prefix+"Invalid type for fragment ["+tagNode+"]", ASTNode.TAG_ELEMENT, tagNode.getNodeType());
TagElement tagElement = (TagElement) tagNode;
assertEquals(this.prefix+"Invalid tag", tag, tagElement.getTagName());
List<? extends ASTNode> fragments = tagElement.fragments();
String[] lines = liness[i];
assertEquals(this.prefix+"Wrong number of fragments", lines.length, fragments.size());
for (int j = 0; j < lines.length; j++) {
ASTNode fragment = fragments.get(j);
String line = lines[j];
assertEquals(this.prefix+"Invalid type for fragment ["+fragment+"]", ASTNode.TEXT_ELEMENT, fragment.getNodeType());
assertEquals(this.prefix+"Wrong text content", line, ((TextElement) fragment).getText());
}
}
}
}

0 comments on commit 31052e2

Please sign in to comment.