Skip to content

Commit 58947f8

Browse files
authored
Merge pull request #75 from openjavaformat/javadoc-snippet
Keep a javadoc {@snippet} as it is written
2 parents 037a7fd + eb20cda commit 58947f8

5 files changed

Lines changed: 114 additions & 2 deletions

File tree

‎open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocFormatter.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ private String render(List<Token> input, int blockIndent) {
6969
case FOOTER_JAVADOC_TAG_START:
7070
output.writeFooterJavadocTagStart(token);
7171
break;
72+
case SNIPPET_BEGIN:
73+
output.writeSnippetBegin(token);
74+
break;
75+
case SNIPPET_END:
76+
output.writeSnippetEnd(token);
77+
break;
7278
case LIST_OPEN_TAG:
7379
output.writeListOpen(token);
7480
break;

‎open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import static com.palantir.javaformat.java.javadoc.Token.Type.PARAGRAPH_OPEN_TAG;
4545
import static com.palantir.javaformat.java.javadoc.Token.Type.PRE_CLOSE_TAG;
4646
import static com.palantir.javaformat.java.javadoc.Token.Type.PRE_OPEN_TAG;
47+
import static com.palantir.javaformat.java.javadoc.Token.Type.SNIPPET_BEGIN;
48+
import static com.palantir.javaformat.java.javadoc.Token.Type.SNIPPET_END;
4749
import static com.palantir.javaformat.java.javadoc.Token.Type.TABLE_CLOSE_TAG;
4850
import static com.palantir.javaformat.java.javadoc.Token.Type.TABLE_OPEN_TAG;
4951
import static com.palantir.javaformat.java.javadoc.Token.Type.WHITESPACE;
@@ -98,6 +100,7 @@ private static String stripJavadocBeginAndEnd(String input) {
98100
private final NestingCounter preDepth = new NestingCounter();
99101
private final NestingCounter codeDepth = new NestingCounter();
100102
private final NestingCounter tableDepth = new NestingCounter();
103+
private boolean outerInlineTagIsSnippet;
101104
private boolean somethingSinceNewline;
102105

103106
private JavadocLexer(CharStream input) {
@@ -159,13 +162,26 @@ private Token.Type consumeToken() throws LexException {
159162
}
160163
somethingSinceNewline = true;
161164

162-
if (input.tryConsumeRegex(INLINE_TAG_OPEN_PATTERN)) {
165+
if (input.tryConsumeRegex(SNIPPET_TAG_OPEN_PATTERN)) {
166+
if (braceDepth.value() == 0) {
167+
braceDepth.increment();
168+
outerInlineTagIsSnippet = true;
169+
return SNIPPET_BEGIN;
170+
}
171+
braceDepth.increment();
172+
return INLINE_TAG_OPEN;
173+
} else if (input.tryConsumeRegex(INLINE_TAG_OPEN_PATTERN)) {
163174
braceDepth.increment();
164175
return INLINE_TAG_OPEN;
165176
} else if (input.tryConsume("{")) {
166177
braceDepth.incrementIfPositive();
167178
return LITERAL;
168179
} else if (input.tryConsume("}")) {
180+
if (outerInlineTagIsSnippet && braceDepth.value() == 1) {
181+
braceDepth.decrementIfPositive();
182+
outerInlineTagIsSnippet = false;
183+
return SNIPPET_END;
184+
}
169185
braceDepth.decrementIfPositive();
170186
return braceDepth.isPositive() ? LITERAL : INLINE_TAG_CLOSE;
171187
}
@@ -240,7 +256,7 @@ private Token.Type consumeToken() throws LexException {
240256
}
241257

242258
private boolean preserveExistingFormatting() {
243-
return preDepth.isPositive() || tableDepth.isPositive() || codeDepth.isPositive();
259+
return preDepth.isPositive() || tableDepth.isPositive() || codeDepth.isPositive() || outerInlineTagIsSnippet;
244260
}
245261

246262
private void checkMatchingTags() throws LexException {
@@ -541,6 +557,7 @@ private static boolean hasMultipleNewlines(String s) {
541557
private static final Pattern BLOCKQUOTE_OPEN_PATTERN = openTagPattern("blockquote");
542558
private static final Pattern BLOCKQUOTE_CLOSE_PATTERN = closeTagPattern("blockquote");
543559
private static final Pattern BR_PATTERN = openTagPattern("br");
560+
private static final Pattern SNIPPET_TAG_OPEN_PATTERN = compile("^[{]@snippet\\b");
544561
private static final Pattern INLINE_TAG_OPEN_PATTERN = compile("^[{]@\\w*");
545562
/*
546563
* We exclude < so that we don't swallow following HTML tags. This lets us fix up "foo<p>" (~400

‎open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocWriter.java‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,30 @@ void writeFooterJavadocTagStart(Token token) {
126126
continuingFooterTag = true;
127127
}
128128

129+
void writeSnippetBegin(Token token) {
130+
requestBlankLine();
131+
writeToken(token);
132+
/*
133+
* We don't request a newline here because we should have at least a colon following on this
134+
* line, and we may have attributes after that.
135+
*
136+
* (If we find it convenient, we could instead consume the entire rest of the line as part of
137+
* the same token as `{@snippet` itself. But we already would never split the rest of the line
138+
* across lines (because we preserve whitespace), so that might not accomplish anything. Plus,
139+
* we'd probably want to be careful not to swallow an expectedly early closing `}`.)
140+
*/
141+
}
142+
143+
void writeSnippetEnd(Token token) {
144+
/*
145+
* We don't request a newline here because we have preserved all newlines that existed in the
146+
* input. Specifically, if there is not yet a newline, one could be added; several could be
147+
* collapsed; and a closing brace that is not indented as we'd want could be indented.
148+
*/
149+
writeToken(token);
150+
requestBlankLine();
151+
}
152+
129153
void writeListOpen(Token token) {
130154
requestBlankLine();
131155

‎open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/Token.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ enum Type {
3939
END_JAVADOC,
4040
/** The {@code @foo} that begins a block Javadoc tag like {@code @throws}. */
4141
FOOTER_JAVADOC_TAG_START,
42+
/** The opening {@code {@snippet} of a code snippet. */
43+
SNIPPET_BEGIN,
44+
/** The closing {@code }} of a code snippet. */
45+
SNIPPET_END,
4246
LIST_OPEN_TAG,
4347
LIST_CLOSE_TAG,
4448
LIST_ITEM_OPEN_TAG,

‎open-java-format/src/test/java/com/palantir/javaformat/java/JavadocFormattingTest.java‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,4 +1418,65 @@ public void u2028LineSeparator() {
14181418
};
14191419
doFormatTest(input, expected);
14201420
}
1421+
1422+
@Test
1423+
public void blankLinesAroundSnippetAndNoMangling() {
1424+
String[] input = {
1425+
"/**", //
1426+
" * hello world",
1427+
" * {@snippet :",
1428+
" * public class Foo {",
1429+
" * private String s;",
1430+
" * }",
1431+
" * }",
1432+
" * hello again",
1433+
" */",
1434+
"class Test {}",
1435+
};
1436+
String[] expected = {
1437+
"/**", //
1438+
" * hello world",
1439+
" *",
1440+
" * {@snippet :",
1441+
" * public class Foo {",
1442+
" * private String s;",
1443+
" * }",
1444+
" * }",
1445+
" *",
1446+
" * hello again",
1447+
" */",
1448+
"class Test {}",
1449+
};
1450+
doFormatTest(input, expected);
1451+
}
1452+
1453+
@Test
1454+
public void notASnippetUnlessOuterTag() {
1455+
String[] input = {
1456+
"/** I would like to tell you about the {@code {@snippet ...}} tag. */", "class Test {}",
1457+
};
1458+
String[] expected = {
1459+
"/** I would like to tell you about the {@code {@snippet ...}} tag. */", "class Test {}",
1460+
};
1461+
doFormatTest(input, expected);
1462+
}
1463+
1464+
@Test
1465+
public void snippetKeepsCommentsAndIndentation() {
1466+
String[] input = {
1467+
"/**", //
1468+
" * Example usage:",
1469+
" *",
1470+
" * {@snippet :",
1471+
" * int x = 1;",
1472+
" * foo(x,",
1473+
" * y); // @highlight substring=\"foo\"",
1474+
" * }",
1475+
" *",
1476+
" * Done.",
1477+
" */",
1478+
"class Test {}",
1479+
};
1480+
doFormatTest(input, input);
1481+
}
14211482
}

0 commit comments

Comments
 (0)