Skip to content

Commit 86b0e4f

Browse files
committed
Add support to recognize Markdown Snippet comments & Markdown Tables
1 parent e7cdd28 commit 86b0e4f

File tree

5 files changed

+178
-7
lines changed

5 files changed

+178
-7
lines changed

org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16648,4 +16648,74 @@ class Mark {
1664816648
""";
1664916649
formatSource(input, expected);
1665016650
}
16651+
16652+
public void testMarkdownSnippetComments() throws JavaModelException {
16653+
setComplianceLevel(CompilerOptions.VERSION_23);
16654+
String input = """
16655+
/// Markdown Snippet
16656+
/// ```
16657+
/// public class HelloWorld2 { public static void main(String... args) {
16658+
/// System.out.println("Hello World!"); // the traditional example
16659+
/// }
16660+
/// }
16661+
/// ```
16662+
class Test2 {
16663+
}
16664+
""";
16665+
String expected = """
16666+
/// Markdown Snippet
16667+
/// ```
16668+
/// public class HelloWorld2 {
16669+
/// public static void main(String...args) {
16670+
/// System.out.println("Hello World!");// the traditional example
16671+
/// }
16672+
/// }
16673+
/// ```
16674+
class Test2 {
16675+
}
16676+
""";
16677+
formatSource(input, expected);
16678+
}
16679+
16680+
public void testMarkdownMultiSnippetComments() throws JavaModelException {
16681+
setComplianceLevel(CompilerOptions.VERSION_23);
16682+
String input = """
16683+
/// ```
16684+
/// public class HelloWorld {
16685+
/// public static void main(String... args) {
16686+
/// System.out.println("Hello World!");// the traditional example
16687+
/// }
16688+
/// }
16689+
/// ```
16690+
///
16691+
/// ```
16692+
/// public class HelloWorld2 {public static void main(String...args) {
16693+
/// System.out.println("ssdd World!");// the traditional example
16694+
/// }
16695+
/// }
16696+
/// ```
16697+
class Test3 {
16698+
}
16699+
""";
16700+
String expected = """
16701+
/// ```
16702+
/// public class HelloWorld {
16703+
/// public static void main(String...args) {
16704+
/// System.out.println("Hello World!");// the traditional example
16705+
/// }
16706+
/// }
16707+
/// ```
16708+
///
16709+
/// ```
16710+
/// public class HelloWorld2 {
16711+
/// public static void main(String...args) {
16712+
/// System.out.println("ssdd World!");// the traditional example
16713+
/// }
16714+
/// }
16715+
/// ```
16716+
class Test3 {
16717+
}
16718+
""";
16719+
formatSource(input, expected);
16720+
}
1665116721
}

org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ public class CommentsPreparator extends ASTVisitor {
105105

106106
private final static Pattern MARKDOWN_HEADINGS_PATTERN_2 = Pattern.compile("(?<!\\S)[ \\t]*([=-])\\1*[ \\t]*(?=\\n|$)"); //$NON-NLS-1$
107107

108+
private final static Pattern MARKDOWN_CODE_SNIPPET_PATTERN = Pattern.compile("[ \\t]*(?:///[ \\t]*)?```[ \\t]*(?:\\R)?"); //$NON-NLS-1$
109+
110+
private final static Pattern MARKDOWN_TABLE_PATTERN = Pattern.compile("(?m)(?s)(?:\\s*///\\s*)?\\|[^\\r\\n]*?\\|[ \\t]*(?:\\r?\\n|$)(?:\\s*///\\s*)?\\|(?:\\s*[:-]+\\s*\\|)+[ \\t]*(?:\\r?\\n|$)(?:(?:\\s*///\\s*)?\\|[^\\r\\n]*?\\|[ \\t]*(?:\\r?\\n|$))+"); //$NON-NLS-1$
111+
108112
// Param tags list copied from IJavaDocTagConstants in legacy formatter for compatibility.
109113
// There were the following comments:
110114
// TODO (frederic) should have another name than 'param' for the following tags
@@ -141,6 +145,8 @@ public class CommentsPreparator extends ASTVisitor {
141145
private DefaultCodeFormatter preTagCodeFormatter;
142146
private DefaultCodeFormatter snippetCodeFormatter;
143147

148+
private boolean snippetForMarkdown = false;
149+
144150
public CommentsPreparator(TokenManager tm, DefaultCodeFormatterOptions options, String sourceLevel) {
145151
this.tm = tm;
146152
this.options = options;
@@ -1377,8 +1383,10 @@ private boolean formatCode(int openingIndex, int closingIndex, boolean snippetTa
13771383
formattedTokens = translateFormattedTokens(codeStartPosition, formattedTokens, positionMapping, null);
13781384

13791385
Token openingToken = this.ctm.get(openingIndex);
1380-
for (Token token : formattedTokens)
1386+
for (Token token : formattedTokens) {
13811387
token.setAlign(token.getAlign() + openingToken.getAlign() + openingToken.getIndent());
1388+
token.setSnippetForMarkdown(this.snippetForMarkdown);
1389+
}
13821390
fixJavadocTagAlign(openingToken, closingIndex);
13831391

13841392
// there are too few linebreaks at the start and end
@@ -1447,6 +1455,9 @@ private void getCodeToFormat(int startPos, int endPos, StringBuilder sb, int[] p
14471455
} else if (!ScannerHelper.isWhitespace(c)) {
14481456
if (c == '*')
14491457
lineStart = (this.ctm.charAt(i + 1) == ' ') ? i + 2 : i + 1;
1458+
if (c == '/' && this.snippetForMarkdown)
1459+
lineStart = ((this.ctm.charAt(i + 1) == '/') && (this.ctm.charAt(i + 2) == '/')) ? i + 4
1460+
: i + 1;
14501461
break;
14511462
}
14521463
}
@@ -1616,6 +1627,7 @@ private void handleMarkdown(TagElement node) {
16161627
previousLevel = currentIndent;
16171628
tokenPositions.put(currentIndent, listToken);
16181629
}
1630+
16191631
matcher = MARKDOWN_HEADINGS_PATTERN_1.matcher(text); // Check for MarkDown headings #h1 - #h6
16201632
while (matcher.find()) {
16211633
int startPos = matcher.start() + node.getStartPosition();
@@ -1637,6 +1649,88 @@ private void handleMarkdown(TagElement node) {
16371649
listToken.breakBefore();
16381650
}
16391651
}
1652+
1653+
matcher = MARKDOWN_CODE_SNIPPET_PATTERN.matcher(text); // Check for MarkDown snippet with styles '``` & ```'
1654+
while (matcher.find()) {
1655+
int startPos = matcher.end() + node.getStartPosition();
1656+
Token openingToken;
1657+
int tokenIndex = this.ctm.findIndex(startPos, ANY, true);
1658+
if (matcher.find()) {
1659+
int endPos = matcher.start() + node.getStartPosition();
1660+
openingToken = this.ctm.get(tokenIndex > 2 ? tokenIndex - 1 : tokenIndex);
1661+
openingToken.breakBefore();
1662+
openingToken.breakAfter();
1663+
int tokenIndexLast = this.ctm.findIndex(endPos, ANY, true);
1664+
if (this.ctm.size() - 1 != tokenIndexLast) {
1665+
Token closingToken = this.ctm.get(tokenIndexLast);
1666+
closingToken.putLineBreaksBefore(2);
1667+
closingToken.putLineBreaksAfter(2);
1668+
}
1669+
this.snippetForMarkdown = true;
1670+
formatCode(tokenIndex - 1, tokenIndexLast, true);
1671+
this.snippetForMarkdown = false;
1672+
}
1673+
}
1674+
1675+
matcher = MARKDOWN_TABLE_PATTERN.matcher(text); // Check for MarkDown tables
1676+
while (matcher.find()) {
1677+
int startPos = matcher.start() + node.getStartPosition();
1678+
int tokenIndex = tokenStartingAt(startPos);
1679+
int endPos = matcher.end() + node.getStartPosition();
1680+
int tokenIndexLast = tokenStartingAt(endPos);
1681+
Token endToken = this.ctm.get(tokenIndexLast);
1682+
this.snippetForMarkdown = false;
1683+
Token currentToken;
1684+
boolean firstRow = false;
1685+
boolean firstRowSecondCol = false;
1686+
int currentColumnLen = -1;
1687+
int alignDistance = 0;
1688+
int rowCount = 0;
1689+
for (int i = tokenIndex; i < tokenIndexLast; i++) {
1690+
currentToken = this.ctm.get(i);
1691+
if (this.ctm.getSource().charAt(currentToken.originalStart) == '\n') {
1692+
continue;
1693+
}
1694+
if (this.ctm.toString(currentToken).equals("|")) { //$NON-NLS-1$
1695+
if (currentColumnLen < 0) {
1696+
currentToken.spaceAfter();
1697+
currentToken.spaceBefore();
1698+
} else {
1699+
if (firstRow) {
1700+
firstRowSecondCol = true;
1701+
firstRow = false;
1702+
} else if (firstRowSecondCol) {
1703+
Token prev = this.ctm.get(i - 1);
1704+
int previousLen = this.ctm.toString(prev).length();
1705+
int previousAlign = prev.getIndent();
1706+
if (prev.isSpaceBefore()) {
1707+
previousAlign++;
1708+
}
1709+
alignDistance += currentColumnLen - previousLen + previousAlign + rowCount + 2;
1710+
rowCount++;
1711+
currentToken.setAlign(alignDistance);
1712+
}
1713+
}
1714+
if (this.ctm.getSource().charAt(currentToken.originalStart + 1) == '\n'
1715+
&& currentToken != endToken) {
1716+
currentToken.breakAfter();
1717+
alignDistance = 0;
1718+
firstRowSecondCol = false;
1719+
firstRow = true;
1720+
rowCount = 0;
1721+
}
1722+
} else if (this.ctm.toString(currentToken).startsWith("|--") //$NON-NLS-1$
1723+
&& this.ctm.toString(currentToken).endsWith("--|")) { //$NON-NLS-1$
1724+
String column = this.ctm.toString(currentToken);
1725+
currentColumnLen = column.indexOf("-|"); //$NON-NLS-1$
1726+
currentToken.breakBefore();
1727+
currentToken.breakAfter();
1728+
firstRow = true;
1729+
}
1730+
}
1731+
}
1732+
16401733
}
16411734
}
1735+
16421736
}

org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,11 @@ private void bufferLineSeparator(Token token, boolean emptyLine) {
212212
}
213213
boolean markerCharFound = false;
214214
int searchLimit = token != null ? token.originalStart : this.sourceLimit;
215-
216-
char markerChar = isMarkdown ? '/' : '*';
215+
char markerChar = isMarkdown ? '/' : token.isSnippetForMarkdown() ? '/' : '*';
217216
for (int i = this.counter; i < searchLimit; i++) {
218217
char c = this.source.charAt(i);
219218
if (c == markerChar) {
220-
if (!isMarkdown)
219+
if (!isMarkdown && !token.isSnippetForMarkdown())
221220
this.buffer.append(' ');
222221
flushBuffer(i);
223222
while (i + 1 < this.sourceLimit && this.source.charAt(i + 1) == markerChar)
@@ -234,7 +233,7 @@ private void bufferLineSeparator(Token token, boolean emptyLine) {
234233
}
235234

236235
if (!markerCharFound) {
237-
this.buffer.append(isMarkdown ? "/// " : " * "); //$NON-NLS-1$ //$NON-NLS-2$
236+
this.buffer.append(isMarkdown ? "/// " : token.isSnippetForMarkdown() ? "/// " : " * "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
238237
}
239238

240239
}

org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ public WrapPolicy(WrapMode wrapMode, int wrapParentIndex, int extraIndent) {
107107

108108
private List<Token> internalStructure;
109109

110+
private boolean isSnippetForMarkdown = false;
111+
110112
public Token(int sourceStart, int sourceEnd, TerminalToken tokenType) {
111113
assert sourceStart <= sourceEnd;
112114
this.originalStart = sourceStart;
@@ -353,4 +355,12 @@ public String toString() {
353355
// return toString(source);
354356
return "[" + this.originalStart + "-" + this.originalEnd + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
355357
}
358+
359+
public boolean isSnippetForMarkdown() {
360+
return this.isSnippetForMarkdown;
361+
}
362+
363+
public void setSnippetForMarkdown(boolean isSnippetForMarkdown) {
364+
this.isSnippetForMarkdown = isSnippetForMarkdown;
365+
}
356366
}

org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
package org.eclipse.jdt.internal.formatter;
1515

1616
import static org.eclipse.jdt.internal.compiler.parser.TerminalToken.TokenNameCOMMENT_JAVADOC;
17-
import static org.eclipse.jdt.internal.compiler.parser.TerminalToken.TokenNameCOMMENT_LINE;
1817
import static org.eclipse.jdt.internal.compiler.parser.TerminalToken.TokenNameNotAToken;
1918
import static org.eclipse.jdt.internal.compiler.parser.TerminalToken.TokenNameStringLiteral;
2019
import static org.eclipse.jdt.internal.compiler.parser.TerminalToken.TokenNameTextBlock;
@@ -281,7 +280,6 @@ protected boolean token(Token traversed, int index) {
281280
this.counter += getLength(lines.get(lines.size() - 1), this.counter);
282281
}
283282
} else if (traversed.isComment()) {
284-
assert traversed.tokenType != TokenNameCOMMENT_LINE;
285283
this.counter = TokenManager.this.commentWrapper.wrapMultiLineComment(traversed, this.counter, true,
286284
this.isNLSTagInLine);
287285
} else {

0 commit comments

Comments
 (0)