diff --git a/core/pom.xml b/core/pom.xml index d3235cb18..273e314fb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -39,11 +39,6 @@ com.google.guava guava - - org.commonmark - commonmark - 0.28.0 - @@ -66,6 +61,16 @@ auto-service-annotations true + + org.commonmark + commonmark + 0.28.0 + + + org.commonmark + commonmark-ext-gfm-tables + 0.28.0 + diff --git a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java index dbc2be841..f801a4d75 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java @@ -44,6 +44,7 @@ import com.google.googlejavaformat.java.javadoc.Token.MarkdownCodeSpanStart; import com.google.googlejavaformat.java.javadoc.Token.MarkdownFencedCodeBlock; import com.google.googlejavaformat.java.javadoc.Token.MarkdownHardLineBreak; +import com.google.googlejavaformat.java.javadoc.Token.MarkdownTable; import com.google.googlejavaformat.java.javadoc.Token.MoeBeginStripComment; import com.google.googlejavaformat.java.javadoc.Token.MoeEndStripComment; import com.google.googlejavaformat.java.javadoc.Token.OptionalLineBreak; @@ -137,6 +138,7 @@ private static String render(List input, int blockIndent, boolean classic case MarkdownHardLineBreak unused -> output.writeMarkdownHardLineBreak(); case Literal t -> output.writeLiteral(t); case MarkdownFencedCodeBlock t -> output.writeMarkdownFencedCodeBlock(t); + case MarkdownTable t -> output.writeMarkdownTable(t); case ListItemCloseTag unused -> {} case OptionalLineBreak unused -> {} case ParagraphCloseTag unused -> {} diff --git a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java index 182762a7f..53f6f1a29 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java @@ -35,6 +35,7 @@ import com.google.googlejavaformat.java.javadoc.Token.ListOpenTag; import com.google.googlejavaformat.java.javadoc.Token.Literal; import com.google.googlejavaformat.java.javadoc.Token.MarkdownFencedCodeBlock; +import com.google.googlejavaformat.java.javadoc.Token.MarkdownTable; import com.google.googlejavaformat.java.javadoc.Token.MoeBeginStripComment; import com.google.googlejavaformat.java.javadoc.Token.MoeEndStripComment; import com.google.googlejavaformat.java.javadoc.Token.PreCloseTag; @@ -44,6 +45,7 @@ import com.google.googlejavaformat.java.javadoc.Token.StartOfLineToken; import com.google.googlejavaformat.java.javadoc.Token.TableCloseTag; import com.google.googlejavaformat.java.javadoc.Token.TableOpenTag; +import java.util.List; /** * Stateful object that accepts "requests" and "writes," producing formatted Javadoc. @@ -354,6 +356,20 @@ void writeMarkdownFencedCodeBlock(MarkdownFencedCodeBlock token) { requestBlankLine(); } + void writeMarkdownTable(MarkdownTable token) { + if (wroteAnythingSignificant && !atStartOfLine) { + requestBlankLine(); + } + flushWhitespace(); + List lines = token.value().lines().toList(); + output.append(lines.get(0)); + for (String line : lines.subList(1, lines.size())) { + writeNewline(AutoIndent.NO_AUTO_INDENT); + output.append(line); + } + requestBlankLine(); + } + @Override public String toString() { return output.toString(); diff --git a/core/src/main/java/com/google/googlejavaformat/java/javadoc/MarkdownPositions.java b/core/src/main/java/com/google/googlejavaformat/java/javadoc/MarkdownPositions.java index d5fdde5d9..0bbb56fee 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/javadoc/MarkdownPositions.java +++ b/core/src/main/java/com/google/googlejavaformat/java/javadoc/MarkdownPositions.java @@ -28,11 +28,14 @@ import com.google.googlejavaformat.java.javadoc.Token.MarkdownCodeSpanEnd; import com.google.googlejavaformat.java.javadoc.Token.MarkdownCodeSpanStart; import com.google.googlejavaformat.java.javadoc.Token.MarkdownFencedCodeBlock; +import com.google.googlejavaformat.java.javadoc.Token.MarkdownTable; import com.google.googlejavaformat.java.javadoc.Token.ParagraphCloseTag; import com.google.googlejavaformat.java.javadoc.Token.ParagraphOpenTag; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.commonmark.ext.gfm.tables.TableBlock; +import org.commonmark.ext.gfm.tables.TablesExtension; import org.commonmark.node.BulletList; import org.commonmark.node.Code; import org.commonmark.node.FencedCodeBlock; @@ -93,6 +96,10 @@ void visit(Node node) { case OrderedList orderedList -> addSpan(orderedList, LIST_OPEN_TOKEN, LIST_CLOSE_TOKEN); case ListItem listItem -> alreadyVisitedChildren = visitListItem(listItem); case FencedCodeBlock fencedCodeBlock -> visitFencedCodeBlock(fencedCodeBlock); + case TableBlock tableBlock -> { + visitTableBlock(tableBlock); + alreadyVisitedChildren = true; + } case Code code -> visitCodeSpan(code); // TODO: others default -> {} @@ -143,6 +150,12 @@ private void visitFencedCodeBlock(FencedCodeBlock fencedCodeBlock) { positionToToken.get(start).addLast(token); } + private void visitTableBlock(TableBlock tableBlock) { + int start = startPosition(tableBlock); + int end = endPosition(tableBlock); + positionToToken.get(start).addLast(new MarkdownTable(input.substring(start, end))); + } + private void visitCodeSpan(Code code) { int start = startPosition(code); int end = endPosition(code); @@ -200,7 +213,10 @@ public String toString() { } private static final Parser PARSER = - Parser.builder().includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build(); + Parser.builder() + .includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES) + .extensions(ImmutableList.of(TablesExtension.create())) + .build(); private static final HeaderOpenTag HEADER_OPEN_TOKEN = new HeaderOpenTag(""); private static final HeaderCloseTag HEADER_CLOSE_TOKEN = new HeaderCloseTag(""); diff --git a/core/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java b/core/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java index 42b73b069..caa35d3fa 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java +++ b/core/src/main/java/com/google/googlejavaformat/java/javadoc/Token.java @@ -140,6 +140,20 @@ record MarkdownCodeSpanEnd(String value) implements Token {} record MarkdownFencedCodeBlock(String value, String start, String end, String literal) implements Token {} + /** + * A Markdown table, like: + * + * {@snippet : + * | foo | bar | + * | --- | --- | + * | baz | qux | + * } + * + * @param value the full text of the table as it appeared in the input, including the delimiters + * and the literal content. + */ + record MarkdownTable(String value) implements Token {} + /** * Whitespace that is not in a {@code
} or {@code } section. Whitespace includes
    * leading newlines, asterisks, and tabs and spaces. In the output, it is translated to newlines
diff --git a/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java b/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java
index 02af3d137..5f2f12526 100644
--- a/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java
+++ b/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java
@@ -1516,15 +1516,6 @@ class Test {}
     doFormatTest(input, expected);
   }
 
-  private void doFormatTest(String input, String expected) {
-    try {
-      String actual = formatter.formatSource(input);
-      assertThat(actual).isEqualTo(expected);
-    } catch (FormatterException e) {
-      throw new AssertionError(e);
-    }
-  }
-
   @Test
   public void windowsLineSeparator() throws FormatterException {
     String input =
@@ -2016,6 +2007,8 @@ public void markdownTables() {
     assume().that(MARKDOWN_JAVADOC_SUPPORTED).isTrue();
     String input =
 """
+/// Table McTableface
+///
 /// | foo | bar |
 /// | --- | --- |
 /// | baz | qux |
@@ -2023,18 +2016,28 @@ public void markdownTables() {
 /// - |foo|bar|
 ///   |--:|:--|
 ///   |baz|qux|
+///
+/// - Another list.
+///
+///   | which | contains |
+///   | ----- | -------- |
+///   | a | table |
 class Test {}
 """;
-    // TODO: unmangle the tables
-    String expected =
-"""
-/// | foo | bar | | --- | --- | | baz | qux |
-/// - |foo|bar| |--:|:--| |baz|qux|
-class Test {}
-""";
+    // We don't currently try to align the column markers in the rows of the last table.
+    String expected = input;
     doFormatTest(input, expected);
   }
 
+  private void doFormatTest(String input, String expected) {
+    try {
+      String actual = formatter.formatSource(input);
+      assertThat(actual).isEqualTo(expected);
+    } catch (FormatterException e) {
+      throw new AssertionError(e);
+    }
+  }
+
   // TODO: b/346668798 - Test the following Markdown constructs, and make the tests work as needed.
   // We can assume that the CommonMark parser correctly handles Markdown, so the question is whether
   // they are subsequently mishandled by our formatting logic. So for example the CommonMark parser
@@ -2074,10 +2077,4 @@ class Test {}
   //
   // - Autolinks
   //    should be preserved. https://spec.commonmark.org/0.31.2/#autolink
-  //
-  // - Tables
-  //   | foo | bar |
-  //   | --- | --- |
-  //   | baz | qux |
-  //   Probably we should just try not to mangle them. https://spec.commonmark.org/0.31.2/#tables
 }