Skip to content

Commit

Permalink
Handle <code> tags
Browse files Browse the repository at this point in the history
These tags currently receive no special handling, which causes line
breaks inside multi-line <code> blocks to be lost. The blocks need to be
wrapped in <pre>s to render correctly, but the formatter should still
avoid mangling whitespace.

Also, only recognize 'footer' javadoc tags that start with a lowercase
letter.  This includes all supported javadoc tags, and excludes
unescaped annotations inside code blocks (which are currently formatted
as if they were tags).

MOE_MIGRATED_REVID=127962506
  • Loading branch information
cushon committed Jul 21, 2016
1 parent ba6bd8c commit db5aec2
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 9 deletions.
Expand Up @@ -23,7 +23,6 @@
import com.google.common.collect.ImmutableList;
import com.google.googlejavaformat.java.JavaFormatterOptions;
import com.google.googlejavaformat.java.javadoc.JavadocLexer.LexException;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -93,6 +92,12 @@ private static String render(List<Token> input, int blockIndent, JavaFormatterOp
case PRE_CLOSE_TAG:
output.writePreClose(token);
break;
case CODE_OPEN_TAG:
output.writeCodeOpen(token);
break;
case CODE_CLOSE_TAG:
output.writeCodeClose(token);
break;
case TABLE_OPEN_TAG:
output.writeTableOpen(token);
break;
Expand Down
Expand Up @@ -22,6 +22,8 @@
import static com.google.googlejavaformat.java.javadoc.Token.Type.BLOCKQUOTE_CLOSE_TAG;
import static com.google.googlejavaformat.java.javadoc.Token.Type.BLOCKQUOTE_OPEN_TAG;
import static com.google.googlejavaformat.java.javadoc.Token.Type.BR_TAG;
import static com.google.googlejavaformat.java.javadoc.Token.Type.CODE_CLOSE_TAG;
import static com.google.googlejavaformat.java.javadoc.Token.Type.CODE_OPEN_TAG;
import static com.google.googlejavaformat.java.javadoc.Token.Type.END_JAVADOC;
import static com.google.googlejavaformat.java.javadoc.Token.Type.FOOTER_JAVADOC_TAG_START;
import static com.google.googlejavaformat.java.javadoc.Token.Type.FORCED_NEWLINE;
Expand Down Expand Up @@ -93,6 +95,7 @@ private static String stripJavadocBeginAndEnd(String input) {
private final CharStream input;
private final NestingCounter braceDepth = new NestingCounter();
private final NestingCounter preDepth = new NestingCounter();
private final NestingCounter codeDepth = new NestingCounter();
private final NestingCounter tableDepth = new NestingCounter();
private boolean somethingSinceNewline;

Expand All @@ -111,9 +114,7 @@ private ImmutableList<Token> generateTokens() throws LexException {
tokens.add(token);
}

if (braceDepth.isPositive() || preDepth.isPositive() || tableDepth.isPositive()) {
throw new LexException();
}
checkMatchingTags();

token = new Token(END_JAVADOC, "*/");
tokens.add(token);
Expand All @@ -133,7 +134,8 @@ private Token readToken() throws LexException {
}

private Type consumeToken() throws LexException {
boolean preserveExistingFormatting = preDepth.isPositive() || tableDepth.isPositive();
boolean preserveExistingFormatting =
preDepth.isPositive() || tableDepth.isPositive() || codeDepth.isPositive();

if (input.tryConsumeRegex(NEWLINE_PATTERN)) {
somethingSinceNewline = false;
Expand All @@ -151,9 +153,7 @@ private Type consumeToken() throws LexException {
* https://github.com/google/google-java-format/issues/7#issuecomment-197383926
*/
if (!somethingSinceNewline && input.tryConsumeRegex(FOOTER_TAG_PATTERN)) {
if (braceDepth.isPositive() || preDepth.isPositive() || tableDepth.isPositive()) {
throw new LexException();
}
checkMatchingTags();
somethingSinceNewline = true;
return FOOTER_JAVADOC_TAG_START;
}
Expand Down Expand Up @@ -182,6 +182,12 @@ private Type consumeToken() throws LexException {
} else if (input.tryConsumeRegex(PRE_CLOSE_PATTERN)) {
preDepth.decrementIfPositive();
return PRE_CLOSE_TAG;
} else if (input.tryConsumeRegex(CODE_OPEN_PATTERN)) {
codeDepth.increment();
return CODE_OPEN_TAG;
} else if (input.tryConsumeRegex(CODE_CLOSE_PATTERN)) {
codeDepth.decrementIfPositive();
return CODE_CLOSE_TAG;
} else if (input.tryConsumeRegex(TABLE_OPEN_PATTERN)) {
tableDepth.increment();
return TABLE_OPEN_TAG;
Expand Down Expand Up @@ -229,6 +235,15 @@ private Type consumeToken() throws LexException {
throw new AssertionError();
}

private void checkMatchingTags() throws LexException {
if (braceDepth.isPositive()
|| preDepth.isPositive()
|| tableDepth.isPositive()
|| codeDepth.isPositive()) {
throw new LexException();
}
}

/**
* Join together adjacent literal tokens, and join together adjacent whitespace tokens.
*
Expand Down Expand Up @@ -478,15 +493,20 @@ private static boolean hasMultipleNewlines(String s) {
* stripping it now: It otherwise might confuse our line-length count, which we use for wrapping.
*/
private static final Pattern NEWLINE_PATTERN = compile("^[ \t]*\n[ \t]*[*]?[ \t]?");

// We ensure elsewhere that we match this only at the beginning of a line.
private static final Pattern FOOTER_TAG_PATTERN = compile("^@\\w*");
// Only match tags that start with a lowercase letter, to avoid false matches on unescaped
// annotations inside code blocks.
private static final Pattern FOOTER_TAG_PATTERN = compile("^@[a-z]\\w*");
private static final Pattern MOE_BEGIN_STRIP_COMMENT_PATTERN =
compile("^<!--\\s*MOE:begin_intracomment_strip\\s*-->");
private static final Pattern MOE_END_STRIP_COMMENT_PATTERN =
compile("^<!--\\s*MOE:end_intracomment_strip\\s*-->");
private static final Pattern HTML_COMMENT_PATTERN = fullCommentPattern();
private static final Pattern PRE_OPEN_PATTERN = openTagPattern("pre");
private static final Pattern PRE_CLOSE_PATTERN = closeTagPattern("pre");
private static final Pattern CODE_OPEN_PATTERN = openTagPattern("code");
private static final Pattern CODE_CLOSE_PATTERN = closeTagPattern("code");
private static final Pattern TABLE_OPEN_PATTERN = openTagPattern("table");
private static final Pattern TABLE_CLOSE_PATTERN = closeTagPattern("table");
private static final Pattern LIST_OPEN_PATTERN = openTagPattern("ul|ol|dl");
Expand Down
Expand Up @@ -196,6 +196,14 @@ void writePreClose(Token token) {
requestBlankLine();
}

void writeCodeOpen(Token token) {
writeToken(token);
}

void writeCodeClose(Token token) {
writeToken(token);
}

void writeTableOpen(Token token) {
requestBlankLine();

Expand Down
Expand Up @@ -54,6 +54,8 @@ enum Type {
BLOCKQUOTE_CLOSE_TAG,
PRE_OPEN_TAG,
PRE_CLOSE_TAG,
CODE_OPEN_TAG,
CODE_CLOSE_TAG,
TABLE_OPEN_TAG,
TABLE_CLOSE_TAG,
/** {@code <!-- MOE:begin_intracomment_strip -->} */
Expand Down
Expand Up @@ -3,6 +3,7 @@ class B29618429 {
* Hello
*
* <p>World
*
* <pre>
* @LooksLikeATag(
* foo = "bar"
Expand Down
@@ -0,0 +1,45 @@
/**
* Base class for {@link Foo}.
*
* <p>The subclass should implement {@link #bar()}, with the implementation returning a
* new instance of the foo relevant to that baz.
*
* Example:
* <code>
* @Override
* protected Foo bar() {
* return new Foo();
* }
* </code>
*
* The subclass should call {@link #get()} to get the foo object,
* and should not cache it in the subclass.
* Example:
* <code>
* @Annotation("something")
* public void thing() {
* }
* </code>
*
* @param <T> concrete subclass
*/
class Test {
/**
* Field
*
* Example:
* <pre><code>
* @Annotation("something")
* public void thing() {
* }
* </code></pre>
*
* @param asd
*/
int x() {}

/**
* Inline <code>foo</code>.
*/
int y() {}
}
@@ -0,0 +1,39 @@
/**
* Base class for {@link Foo}.
*
* <p>The subclass should implement {@link #bar()}, with the implementation returning a new instance
* of the foo relevant to that baz.
*
* <p>Example: <code>
* @Override
* protected Foo bar() {
* return new Foo();
* }
* </code> The subclass should call {@link #get()} to get the foo object, and should not cache it in
* the subclass. Example: <code>
* @Annotation("something")
* public void thing() {
* }
* </code>
*
* @param <T> concrete subclass
*/
class Test {
/**
* Field
*
* <p>Example:
*
* <pre><code>
* @Annotation("something")
* public void thing() {
* }
* </code></pre>
*
* @param asd
*/
int x() {}

/** Inline <code>foo</code>. */
int y() {}
}

0 comments on commit db5aec2

Please sign in to comment.