Skip to content

Commit 4a46c77

Browse files
eamonnmcmanusgoogle-java-format Team
authored andcommitted
Add support for Markdown tables.
This is very basic, and the main intent is just to avoid mangling tables. We essentially preserve the formatting of any table we find, without attempting to adjust it in any way. PiperOrigin-RevId: 905301629
1 parent 2519066 commit 4a46c77

6 files changed

Lines changed: 78 additions & 28 deletions

File tree

core/pom.xml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@
3939
<groupId>com.google.guava</groupId>
4040
<artifactId>guava</artifactId>
4141
</dependency>
42-
<dependency>
43-
<groupId>org.commonmark</groupId>
44-
<artifactId>commonmark</artifactId>
45-
<version>0.28.0</version>
46-
</dependency>
4742

4843
<!-- Compile-time dependencies -->
4944
<dependency>
@@ -66,6 +61,16 @@
6661
<artifactId>auto-service-annotations</artifactId>
6762
<optional>true</optional>
6863
</dependency>
64+
<dependency>
65+
<groupId>org.commonmark</groupId>
66+
<artifactId>commonmark</artifactId>
67+
<version>0.28.0</version>
68+
</dependency>
69+
<dependency>
70+
<groupId>org.commonmark</groupId>
71+
<artifactId>commonmark-ext-gfm-tables</artifactId>
72+
<version>0.28.0</version>
73+
</dependency>
6974

7075
<!-- Test dependencies -->
7176
<dependency>

core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.google.googlejavaformat.java.javadoc.Token.MarkdownCodeSpanStart;
4545
import com.google.googlejavaformat.java.javadoc.Token.MarkdownFencedCodeBlock;
4646
import com.google.googlejavaformat.java.javadoc.Token.MarkdownHardLineBreak;
47+
import com.google.googlejavaformat.java.javadoc.Token.MarkdownTable;
4748
import com.google.googlejavaformat.java.javadoc.Token.MoeBeginStripComment;
4849
import com.google.googlejavaformat.java.javadoc.Token.MoeEndStripComment;
4950
import com.google.googlejavaformat.java.javadoc.Token.OptionalLineBreak;
@@ -137,6 +138,7 @@ private static String render(List<Token> input, int blockIndent, boolean classic
137138
case MarkdownHardLineBreak unused -> output.writeMarkdownHardLineBreak();
138139
case Literal t -> output.writeLiteral(t);
139140
case MarkdownFencedCodeBlock t -> output.writeMarkdownFencedCodeBlock(t);
141+
case MarkdownTable t -> output.writeMarkdownTable(t);
140142
case ListItemCloseTag unused -> {}
141143
case OptionalLineBreak unused -> {}
142144
case ParagraphCloseTag unused -> {}

core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.googlejavaformat.java.javadoc.Token.ListOpenTag;
3636
import com.google.googlejavaformat.java.javadoc.Token.Literal;
3737
import com.google.googlejavaformat.java.javadoc.Token.MarkdownFencedCodeBlock;
38+
import com.google.googlejavaformat.java.javadoc.Token.MarkdownTable;
3839
import com.google.googlejavaformat.java.javadoc.Token.MoeBeginStripComment;
3940
import com.google.googlejavaformat.java.javadoc.Token.MoeEndStripComment;
4041
import com.google.googlejavaformat.java.javadoc.Token.PreCloseTag;
@@ -44,6 +45,7 @@
4445
import com.google.googlejavaformat.java.javadoc.Token.StartOfLineToken;
4546
import com.google.googlejavaformat.java.javadoc.Token.TableCloseTag;
4647
import com.google.googlejavaformat.java.javadoc.Token.TableOpenTag;
48+
import java.util.List;
4749

4850
/**
4951
* Stateful object that accepts "requests" and "writes," producing formatted Javadoc.
@@ -354,6 +356,20 @@ void writeMarkdownFencedCodeBlock(MarkdownFencedCodeBlock token) {
354356
requestBlankLine();
355357
}
356358

359+
void writeMarkdownTable(MarkdownTable token) {
360+
if (wroteAnythingSignificant && !atStartOfLine) {
361+
requestBlankLine();
362+
}
363+
flushWhitespace();
364+
List<String> lines = token.value().lines().toList();
365+
output.append(lines.get(0));
366+
for (String line : lines.subList(1, lines.size())) {
367+
writeNewline(AutoIndent.NO_AUTO_INDENT);
368+
output.append(line);
369+
}
370+
requestBlankLine();
371+
}
372+
357373
@Override
358374
public String toString() {
359375
return output.toString();

core/src/main/java/com/google/googlejavaformat/java/javadoc/MarkdownPositions.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@
2828
import com.google.googlejavaformat.java.javadoc.Token.MarkdownCodeSpanEnd;
2929
import com.google.googlejavaformat.java.javadoc.Token.MarkdownCodeSpanStart;
3030
import com.google.googlejavaformat.java.javadoc.Token.MarkdownFencedCodeBlock;
31+
import com.google.googlejavaformat.java.javadoc.Token.MarkdownTable;
3132
import com.google.googlejavaformat.java.javadoc.Token.ParagraphCloseTag;
3233
import com.google.googlejavaformat.java.javadoc.Token.ParagraphOpenTag;
3334
import java.util.Objects;
3435
import java.util.regex.Matcher;
3536
import java.util.regex.Pattern;
37+
import org.commonmark.ext.gfm.tables.TableBlock;
38+
import org.commonmark.ext.gfm.tables.TablesExtension;
3639
import org.commonmark.node.BulletList;
3740
import org.commonmark.node.Code;
3841
import org.commonmark.node.FencedCodeBlock;
@@ -93,6 +96,10 @@ void visit(Node node) {
9396
case OrderedList orderedList -> addSpan(orderedList, LIST_OPEN_TOKEN, LIST_CLOSE_TOKEN);
9497
case ListItem listItem -> alreadyVisitedChildren = visitListItem(listItem);
9598
case FencedCodeBlock fencedCodeBlock -> visitFencedCodeBlock(fencedCodeBlock);
99+
case TableBlock tableBlock -> {
100+
visitTableBlock(tableBlock);
101+
alreadyVisitedChildren = true;
102+
}
96103
case Code code -> visitCodeSpan(code);
97104
// TODO: others
98105
default -> {}
@@ -143,6 +150,12 @@ private void visitFencedCodeBlock(FencedCodeBlock fencedCodeBlock) {
143150
positionToToken.get(start).addLast(token);
144151
}
145152

153+
private void visitTableBlock(TableBlock tableBlock) {
154+
int start = startPosition(tableBlock);
155+
int end = endPosition(tableBlock);
156+
positionToToken.get(start).addLast(new MarkdownTable(input.substring(start, end)));
157+
}
158+
146159
private void visitCodeSpan(Code code) {
147160
int start = startPosition(code);
148161
int end = endPosition(code);
@@ -200,7 +213,10 @@ public String toString() {
200213
}
201214

202215
private static final Parser PARSER =
203-
Parser.builder().includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build();
216+
Parser.builder()
217+
.includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES)
218+
.extensions(ImmutableList.of(TablesExtension.create()))
219+
.build();
204220

205221
private static final HeaderOpenTag HEADER_OPEN_TOKEN = new HeaderOpenTag("");
206222
private static final HeaderCloseTag HEADER_CLOSE_TOKEN = new HeaderCloseTag("");

core/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,20 @@ record MarkdownCodeSpanEnd(String value) implements Token {}
140140
record MarkdownFencedCodeBlock(String value, String start, String end, String literal)
141141
implements Token {}
142142

143+
/**
144+
* A Markdown table, like:
145+
*
146+
* {@snippet :
147+
* | foo | bar |
148+
* | --- | --- |
149+
* | baz | qux |
150+
* }
151+
*
152+
* @param value the full text of the table as it appeared in the input, including the delimiters
153+
* and the literal content.
154+
*/
155+
record MarkdownTable(String value) implements Token {}
156+
143157
/**
144158
* Whitespace that is not in a {@code <pre>} or {@code <table>} section. Whitespace includes
145159
* leading newlines, asterisks, and tabs and spaces. In the output, it is translated to newlines

core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,15 +1516,6 @@ class Test {}
15161516
doFormatTest(input, expected);
15171517
}
15181518

1519-
private void doFormatTest(String input, String expected) {
1520-
try {
1521-
String actual = formatter.formatSource(input);
1522-
assertThat(actual).isEqualTo(expected);
1523-
} catch (FormatterException e) {
1524-
throw new AssertionError(e);
1525-
}
1526-
}
1527-
15281519
@Test
15291520
public void windowsLineSeparator() throws FormatterException {
15301521
String input =
@@ -2016,25 +2007,37 @@ public void markdownTables() {
20162007
assume().that(MARKDOWN_JAVADOC_SUPPORTED).isTrue();
20172008
String input =
20182009
"""
2010+
/// Table McTableface
2011+
///
20192012
/// | foo | bar |
20202013
/// | --- | --- |
20212014
/// | baz | qux |
20222015
///
20232016
/// - |foo|bar|
20242017
/// |--:|:--|
20252018
/// |baz|qux|
2019+
///
2020+
/// - Another list.
2021+
///
2022+
/// | which | contains |
2023+
/// | ----- | -------- |
2024+
/// | a | table |
20262025
class Test {}
20272026
""";
2028-
// TODO: unmangle the tables
2029-
String expected =
2030-
"""
2031-
/// | foo | bar | | --- | --- | | baz | qux |
2032-
/// - |foo|bar| |--:|:--| |baz|qux|
2033-
class Test {}
2034-
""";
2027+
// We don't currently try to align the column markers in the rows of the last table.
2028+
String expected = input;
20352029
doFormatTest(input, expected);
20362030
}
20372031

2032+
private void doFormatTest(String input, String expected) {
2033+
try {
2034+
String actual = formatter.formatSource(input);
2035+
assertThat(actual).isEqualTo(expected);
2036+
} catch (FormatterException e) {
2037+
throw new AssertionError(e);
2038+
}
2039+
}
2040+
20382041
// TODO: b/346668798 - Test the following Markdown constructs, and make the tests work as needed.
20392042
// We can assume that the CommonMark parser correctly handles Markdown, so the question is whether
20402043
// they are subsequently mishandled by our formatting logic. So for example the CommonMark parser
@@ -2074,10 +2077,4 @@ class Test {}
20742077
//
20752078
// - Autolinks
20762079
// <http://example.com> should be preserved. https://spec.commonmark.org/0.31.2/#autolink
2077-
//
2078-
// - Tables
2079-
// | foo | bar |
2080-
// | --- | --- |
2081-
// | baz | qux |
2082-
// Probably we should just try not to mangle them. https://spec.commonmark.org/0.31.2/#tables
20832080
}

0 commit comments

Comments
 (0)