Skip to content

Commit ebf9b52

Browse files
cpovirkgoogle-java-format Team
authored and
google-java-format Team
committedAug 20, 2024·
Avoid mangling {@snippet ...}.
This CL also implements some minimal formatting behavior _around_ `{@snippet ...}` (namely, a blank line before and after). It does not make any effort to change formatting _inside_ `{@snippet ...}`, only to stop reflowing it blindly. PiperOrigin-RevId: 665555661
1 parent b3c7c11 commit ebf9b52

File tree

5 files changed

+107
-2
lines changed

5 files changed

+107
-2
lines changed
 

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

+6
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ private static String render(List<Token> input, int blockIndent) {
6666
case FOOTER_JAVADOC_TAG_START:
6767
output.writeFooterJavadocTagStart(token);
6868
break;
69+
case SNIPPET_BEGIN:
70+
output.writeSnippetBegin(token);
71+
break;
72+
case SNIPPET_END:
73+
output.writeSnippetEnd(token);
74+
break;
6975
case LIST_OPEN_TAG:
7076
output.writeListOpen(token);
7177
break;

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

+23-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import static com.google.googlejavaformat.java.javadoc.Token.Type.PARAGRAPH_OPEN_TAG;
4343
import static com.google.googlejavaformat.java.javadoc.Token.Type.PRE_CLOSE_TAG;
4444
import static com.google.googlejavaformat.java.javadoc.Token.Type.PRE_OPEN_TAG;
45+
import static com.google.googlejavaformat.java.javadoc.Token.Type.SNIPPET_BEGIN;
46+
import static com.google.googlejavaformat.java.javadoc.Token.Type.SNIPPET_END;
4547
import static com.google.googlejavaformat.java.javadoc.Token.Type.TABLE_CLOSE_TAG;
4648
import static com.google.googlejavaformat.java.javadoc.Token.Type.TABLE_OPEN_TAG;
4749
import static com.google.googlejavaformat.java.javadoc.Token.Type.WHITESPACE;
@@ -97,6 +99,7 @@ private static String stripJavadocBeginAndEnd(String input) {
9799
private final NestingCounter preDepth = new NestingCounter();
98100
private final NestingCounter codeDepth = new NestingCounter();
99101
private final NestingCounter tableDepth = new NestingCounter();
102+
private boolean outerInlineTagIsSnippet;
100103
private boolean somethingSinceNewline;
101104

102105
private JavadocLexer(CharStream input) {
@@ -158,13 +161,26 @@ private Type consumeToken() throws LexException {
158161
}
159162
somethingSinceNewline = true;
160163

161-
if (input.tryConsumeRegex(INLINE_TAG_OPEN_PATTERN)) {
164+
if (input.tryConsumeRegex(SNIPPET_TAG_OPEN_PATTERN)) {
165+
if (braceDepth.value() == 0) {
166+
braceDepth.increment();
167+
outerInlineTagIsSnippet = true;
168+
return SNIPPET_BEGIN;
169+
}
170+
braceDepth.increment();
171+
return LITERAL;
172+
} else if (input.tryConsumeRegex(INLINE_TAG_OPEN_PATTERN)) {
162173
braceDepth.increment();
163174
return LITERAL;
164175
} else if (input.tryConsume("{")) {
165176
braceDepth.incrementIfPositive();
166177
return LITERAL;
167178
} else if (input.tryConsume("}")) {
179+
if (outerInlineTagIsSnippet && braceDepth.value() == 1) {
180+
braceDepth.decrementIfPositive();
181+
outerInlineTagIsSnippet = false;
182+
return SNIPPET_END;
183+
}
168184
braceDepth.decrementIfPositive();
169185
return LITERAL;
170186
}
@@ -239,7 +255,10 @@ private Type consumeToken() throws LexException {
239255
}
240256

241257
private boolean preserveExistingFormatting() {
242-
return preDepth.isPositive() || tableDepth.isPositive() || codeDepth.isPositive();
258+
return preDepth.isPositive()
259+
|| tableDepth.isPositive()
260+
|| codeDepth.isPositive()
261+
|| outerInlineTagIsSnippet;
243262
}
244263

245264
private void checkMatchingTags() throws LexException {
@@ -400,6 +419,7 @@ private static ImmutableList<Token> optionalizeSpacesAfterLinks(List<Token> inpu
400419
* <p>Also trim leading and trailing blank lines, and move the trailing `}` to its own line.
401420
*/
402421
private static ImmutableList<Token> deindentPreCodeBlocks(List<Token> input) {
422+
// TODO: b/323389829 - De-indent {@snippet ...} blocks, too.
403423
ImmutableList.Builder<Token> output = ImmutableList.builder();
404424
for (PeekingIterator<Token> tokens = peekingIterator(input.iterator()); tokens.hasNext(); ) {
405425
if (tokens.peek().getType() != PRE_OPEN_TAG) {
@@ -528,6 +548,7 @@ private static boolean hasMultipleNewlines(String s) {
528548
private static final Pattern BLOCKQUOTE_OPEN_PATTERN = openTagPattern("blockquote");
529549
private static final Pattern BLOCKQUOTE_CLOSE_PATTERN = closeTagPattern("blockquote");
530550
private static final Pattern BR_PATTERN = openTagPattern("br");
551+
private static final Pattern SNIPPET_TAG_OPEN_PATTERN = compile("^[{]@snippet\\b");
531552
private static final Pattern INLINE_TAG_OPEN_PATTERN = compile("^[{]@\\w*");
532553
/*
533554
* We exclude < so that we don't swallow following HTML tags. This lets us fix up "foo<p>" (~400

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

+32
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,38 @@ void writeFooterJavadocTagStart(Token token) {
123123
continuingFooterTag = true;
124124
}
125125

126+
void writeSnippetBegin(Token token) {
127+
requestBlankLine();
128+
writeToken(token);
129+
/*
130+
* We don't request a newline here because we should have at least a colon following on this
131+
* line, and we may have attributes after that.
132+
*
133+
* (If we find it convenient, we could instead consume the entire rest of the line as part of
134+
* the same token as `{@snippet` itself. But we already would never split the rest of the line
135+
* across lines (because we preserve whitespace), so that might not accomplish anything. Plus,
136+
* we'd probably want to be careful not to swallow an expectedly early closing `}`.)
137+
*/
138+
}
139+
140+
void writeSnippetEnd(Token token) {
141+
/*
142+
* We don't request a newline here because we have preserved all newlines that existed in the
143+
* input. TODO: b/323389829 - Improve upon that. Specifically:
144+
*
145+
* - If there is not yet a newline, we should add one.
146+
*
147+
* - If there are multiple newlines, we should probably collapse them.
148+
*
149+
* - If the closing brace isn't indented as we'd want (as in the link below, in which the whole
150+
* @apiNote isn't indented), we should indent it.
151+
*
152+
* https://github.com/openjdk/jdk/blob/1ebf2cf639300728ffc024784f5dc1704317b0b3/src/java.base/share/classes/java/util/Collections.java#L5993-L6006
153+
*/
154+
writeToken(token);
155+
requestBlankLine();
156+
}
157+
126158
void writeListOpen(Token token) {
127159
requestBlankLine();
128160

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

+4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ enum Type {
4141
END_JAVADOC,
4242
/** The {@code @foo} that begins a block Javadoc tag like {@code @throws}. */
4343
FOOTER_JAVADOC_TAG_START,
44+
/** The opening {@code {@snippet} of a code snippet. */
45+
SNIPPET_BEGIN,
46+
/** The closing {@code }} of a code snippet. */
47+
SNIPPET_END,
4448
LIST_OPEN_TAG,
4549
LIST_CLOSE_TAG,
4650
LIST_ITEM_OPEN_TAG,

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

+42
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,48 @@ public void unicodeCharacterCountArguableBug() {
906906
doFormatTest(input, expected);
907907
}
908908

909+
@Test
910+
public void blankLinesAroundSnippetAndNoMangling() {
911+
String[] input = {
912+
"/**", //
913+
" * hello world",
914+
" * {@snippet :",
915+
" * public class Foo {",
916+
" * private String s;",
917+
" * }",
918+
" * }",
919+
" * hello again",
920+
" */",
921+
"class Test {}",
922+
};
923+
String[] expected = {
924+
"/**", //
925+
" * hello world",
926+
" *",
927+
" * {@snippet :",
928+
" * public class Foo {",
929+
" * private String s;",
930+
" * }",
931+
" * }",
932+
" *",
933+
" * hello again",
934+
" */",
935+
"class Test {}",
936+
};
937+
doFormatTest(input, expected);
938+
}
939+
940+
@Test
941+
public void notASnippetUnlessOuterTag() {
942+
String[] input = {
943+
"/** I would like to tell you about the {@code {@snippet ...}} tag. */", "class Test {}",
944+
};
945+
String[] expected = {
946+
"/** I would like to tell you about the {@code {@snippet ...}} tag. */", "class Test {}",
947+
};
948+
doFormatTest(input, expected);
949+
}
950+
909951
@Test
910952
public void blankLineBeforeParams() {
911953
String[] input = {

0 commit comments

Comments
 (0)
Please sign in to comment.