Skip to content

Commit b8454d0

Browse files
eamonnmcmanusgoogle-java-format Team
authored andcommitted
Add support for numbered lists.
As part of that support, allow for the lexer tokens produced by the Markdown parse to have contained text. Specifically, a list item will have text like `"* "` or `"1. "`, which we can then use to determine how far to indent continuation lines. PiperOrigin-RevId: 894215568
1 parent 53e2a76 commit b8454d0

File tree

5 files changed

+54
-11
lines changed

5 files changed

+54
-11
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,12 @@ private ImmutableList<Token> generateTokens() throws LexException {
126126
tokens.add(token);
127127

128128
while (!input.isExhausted()) {
129-
tokens.addAll(markdownPositions.tokensAt(input.position()));
129+
for (Token markdownToken : markdownPositions.tokensAt(input.position())) {
130+
boolean consumed = input.tryConsume(markdownToken.value());
131+
verify(consumed, "Did not consume markdown token: %s", markdownToken);
132+
var unused = input.readAndResetRecorded();
133+
tokens.add(markdownToken);
134+
}
130135
token = readToken();
131136
tokens.add(token);
132137
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,18 @@ void writeListOpen(Token token) {
183183
}
184184

185185
void writeListClose(Token token) {
186-
requestNewline();
186+
if (classicJavadoc) {
187+
requestNewline();
188+
}
187189

188190
continuingListItemStack.popIfNotEmpty();
189191
continuingListStack.popIfNotEmpty();
190192
writeToken(token);
191193
postWriteModifiedContinuingListStack.popIfNotEmpty();
192194

193-
requestBlankLine();
195+
if (classicJavadoc) {
196+
requestBlankLine();
197+
}
194198
}
195199

196200
void writeListItemOpen(Token token) {
@@ -202,7 +206,7 @@ void writeListItemOpen(Token token) {
202206
}
203207
writeToken(token);
204208
continuingListItemOfInnermostList = true;
205-
int indent = token.value().isEmpty() ? 2 : 4; // Indent 2 for Markdown, 4 for HTML.
209+
int indent = token.value().length();
206210
continuingListItemStack.push(indent);
207211
}
208212

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,19 @@
1313
*/
1414
package com.google.googlejavaformat.java.javadoc;
1515

16+
import static com.google.common.base.Verify.verify;
17+
1618
import com.google.common.collect.ArrayListMultimap;
1719
import com.google.common.collect.ImmutableList;
1820
import com.google.common.collect.ImmutableListMultimap;
1921
import com.google.common.collect.ListMultimap;
22+
import java.util.regex.Matcher;
23+
import java.util.regex.Pattern;
2024
import org.commonmark.node.BulletList;
2125
import org.commonmark.node.Heading;
2226
import org.commonmark.node.ListItem;
2327
import org.commonmark.node.Node;
28+
import org.commonmark.node.OrderedList;
2429
import org.commonmark.node.Paragraph;
2530
import org.commonmark.parser.IncludeSourceSpans;
2631
import org.commonmark.parser.Parser;
@@ -50,7 +55,7 @@ private MarkdownPositions(ImmutableListMultimap<Integer, Token> positionToToken)
5055
static MarkdownPositions parse(String input) {
5156
Node document = PARSER.parse(input);
5257
ListMultimap<Integer, Token> positionToToken = ArrayListMultimap.create();
53-
new TokenVisitor(positionToToken).visit(document);
58+
new TokenVisitor(input, positionToToken).visit(document);
5459
return new MarkdownPositions(ImmutableListMultimap.copyOf(positionToToken));
5560
}
5661

@@ -59,9 +64,11 @@ ImmutableList<Token> tokensAt(int position) {
5964
}
6065

6166
private static class TokenVisitor {
67+
private final String input;
6268
private final ListMultimap<Integer, Token> positionToToken;
6369

64-
TokenVisitor(ListMultimap<Integer, Token> positionToToken) {
70+
TokenVisitor(String input, ListMultimap<Integer, Token> positionToToken) {
71+
this.input = input;
6572
this.positionToToken = positionToToken;
6673
}
6774

@@ -74,8 +81,15 @@ void visit(Node node) {
7481
addSpan(positionToToken, paragraph, PARAGRAPH_OPEN_TOKEN, PARAGRAPH_CLOSE_TOKEN);
7582
case BulletList bulletList ->
7683
addSpan(positionToToken, bulletList, LIST_OPEN_TOKEN, LIST_CLOSE_TOKEN);
84+
case OrderedList orderedList ->
85+
addSpan(positionToToken, orderedList, LIST_OPEN_TOKEN, LIST_CLOSE_TOKEN);
7786
case ListItem listItem -> {
78-
addSpan(positionToToken, listItem, LIST_ITEM_OPEN_TOKEN, LIST_ITEM_CLOSE_TOKEN);
87+
int startPosition = listItem.getSourceSpans().getFirst().getInputIndex();
88+
Matcher matcher =
89+
LIST_ITEM_START_PATTERN.matcher(input).region(startPosition, input.length());
90+
verify(matcher.lookingAt());
91+
Token openToken = new Token(Token.Type.LIST_ITEM_OPEN_TAG, matcher.group(1));
92+
addSpan(positionToToken, listItem, openToken, LIST_ITEM_CLOSE_TOKEN);
7993
if (listItem.getFirstChild() instanceof Paragraph paragraph) {
8094
// A ListItem typically contains a Paragraph, but we don't want to visit that Paragraph
8195
// because that would lead us to introduce a line break after the list introduction
@@ -133,12 +147,17 @@ public String toString() {
133147

134148
private static final Parser PARSER =
135149
Parser.builder().includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build();
150+
136151
private static final Token HEADER_OPEN_TOKEN = new Token(Token.Type.HEADER_OPEN_TAG, "");
137152
private static final Token HEADER_CLOSE_TOKEN = new Token(Token.Type.HEADER_CLOSE_TAG, "");
138153
private static final Token PARAGRAPH_OPEN_TOKEN = new Token(Token.Type.PARAGRAPH_OPEN_TAG, "");
139154
private static final Token PARAGRAPH_CLOSE_TOKEN = new Token(Token.Type.PARAGRAPH_CLOSE_TAG, "");
140155
private static final Token LIST_OPEN_TOKEN = new Token(Token.Type.LIST_OPEN_TAG, "");
141156
private static final Token LIST_CLOSE_TOKEN = new Token(Token.Type.LIST_CLOSE_TAG, "");
142-
private static final Token LIST_ITEM_OPEN_TOKEN = new Token(Token.Type.LIST_ITEM_OPEN_TAG, "");
143157
private static final Token LIST_ITEM_CLOSE_TOKEN = new Token(Token.Type.LIST_ITEM_CLOSE_TAG, "");
158+
159+
// The leading \s here works around what appears to be a CommonMark bug. We shouldn't ever see
160+
// space at the purported start of a list item?
161+
private static final Pattern LIST_ITEM_START_PATTERN =
162+
Pattern.compile("(?:\\s*)((-|\\*|[0-9]+\\.)\\s)");
144163
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,7 @@ public void moduleMarkdown() {
16771677
}
16781678

16791679
@Test
1680-
public void markdownBulletList() {
1680+
public void markdownLists() {
16811681
assume().that(MARKDOWN_JAVADOC_SUPPORTED).isTrue();
16821682
String input =
16831683
"""
@@ -1689,6 +1689,14 @@ public void markdownBulletList() {
16891689
/// - a nested list
16901690
/// * nested thing 1
16911691
/// * nested thing 2
1692+
/// - a nested numbered list with unnecessary leading whitespace
1693+
/// 1. nested thing 1
1694+
/// on more than one line
1695+
/// 2. nested thing 2 on only one line but which is long enough that it is going to need to be wrapped
1696+
///
1697+
/// 3. nested thing 3 after a blank line
1698+
///
1699+
/// A following paragraph.
16921700
class Test {}
16931701
""";
16941702
String expected =
@@ -1701,6 +1709,13 @@ class Test {}
17011709
/// - a nested list
17021710
/// * nested thing 1
17031711
/// * nested thing 2
1712+
/// - a nested numbered list with unnecessary leading whitespace
1713+
/// 1. nested thing 1 on more than one line
1714+
/// 2. nested thing 2 on only one line but which is long enough that it is going to need to be
1715+
/// wrapped
1716+
/// 3. nested thing 3 after a blank line
1717+
///
1718+
/// A following paragraph.
17041719
class Test {}
17051720
""";
17061721
doFormatTest(input, expected);

core/src/test/java/com/google/googlejavaformat/java/javadoc/MarkdownPositionsTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ public void list() {
4747
ImmutableListMultimap<Integer, Token> expected =
4848
ImmutableListMultimap.<Integer, Token>builder()
4949
.put(firstBullet, new Token(Token.Type.LIST_OPEN_TAG, ""))
50-
.put(firstBullet, new Token(Token.Type.LIST_ITEM_OPEN_TAG, ""))
50+
.put(firstBullet, new Token(Token.Type.LIST_ITEM_OPEN_TAG, "- "))
5151
.put(secondBullet - 1, new Token(Token.Type.LIST_ITEM_CLOSE_TAG, ""))
52-
.put(secondBullet, new Token(Token.Type.LIST_ITEM_OPEN_TAG, ""))
52+
.put(secondBullet, new Token(Token.Type.LIST_ITEM_OPEN_TAG, "- "))
5353
.put(end, new Token(Token.Type.LIST_ITEM_CLOSE_TAG, ""))
5454
.put(end, new Token(Token.Type.LIST_CLOSE_TAG, ""))
5555
.build();

0 commit comments

Comments
 (0)