diff --git a/core/pom.xml b/core/pom.xml index 25504b58b..1e8f22357 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -22,7 +22,7 @@ com.google.googlejavaformat google-java-format-parent - 1.6-SNAPSHOT + 1.6 google-java-format diff --git a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java index 0077bb79a..60fdcc1fd 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java @@ -65,8 +65,8 @@ * is a final EOF token to hold final comments. * *

The formatter walks the AST to generate a Greg Nelson/Derek Oppen-style list of formatting - * {@link Op}s [1--2] that then generates a structured {@link Doc}. Each AST node type has a visitor - * to emit a sequence of {@link Op}s for the node. + * {@link Op}s [1--2] that then generates a structured {@link Doc} . Each AST node type has a + * visitor to emit a sequence of {@link Op}s for the node. * *

Some data-structure operations are easier in the list of {@link Op}s, while others become * easier in the {@link Doc}. The {@link Op}s are walked to attach the comments. As the {@link Op}s @@ -100,6 +100,11 @@ public Formatter(JavaFormatterOptions options) { this.options = options; } + /** @return The options used for this formatter. */ + public JavaFormatterOptions options() { + return options; + } + /** * Construct a {@code Formatter} given a Java compilation unit. Parses the code; builds a {@link * JavaInput} and the corresponding {@link JavaOutput}. diff --git a/core/src/main/java/com/google/googlejavaformat/java/SnippetFormatter.java b/core/src/main/java/com/google/googlejavaformat/java/SnippetFormatter.java index 3827ef958..1ac62261c 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/SnippetFormatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/SnippetFormatter.java @@ -14,14 +14,15 @@ package com.google.googlejavaformat.java; +import java.util.ArrayList; +import java.util.List; + import com.google.common.base.CharMatcher; import com.google.common.base.Preconditions; import com.google.common.collect.DiscreteDomain; import com.google.common.collect.Range; import com.google.common.collect.RangeSet; import com.google.common.collect.TreeRangeSet; -import java.util.ArrayList; -import java.util.List; /** Formats a subset of a compilation unit. */ public class SnippetFormatter { @@ -56,16 +57,25 @@ public void closeBraces(int initialIndent) { } } - private static final int INDENTATION_SIZE = 2; - private final Formatter formatter = new Formatter(); + private static final int SPACES_PER_INDENT_LEVEL = 2; + private final Formatter formatter; private static final CharMatcher NOT_WHITESPACE = CharMatcher.whitespace().negate(); + public SnippetFormatter() { + this(JavaFormatterOptions.defaultOptions()); + } + + public SnippetFormatter(JavaFormatterOptions options) { + Preconditions.checkNotNull(options); + this.formatter = new Formatter(options); + } + public String createIndentationString(int indentationLevel) { Preconditions.checkArgument( indentationLevel >= 0, "Indentation level cannot be less than zero. Given: %s", indentationLevel); - int spaces = indentationLevel * INDENTATION_SIZE; + int spaces = indentationLevel * indentationSize(); StringBuilder buf = new StringBuilder(spaces); for (int i = 0; i < spaces; i++) { buf.append(' '); @@ -134,9 +144,10 @@ private static List toReplacements(String source, String replacemen "source = \"" + source + "\", replacement = \"" + replacement + "\""); } /* - * In the past we seemed to have problems touching non-whitespace text in the formatter, even - * just replacing some code with itself. Retrospective attempts to reproduce this have failed, - * but this may be an issue for future changes. + * In the past we seemed to have problems touching non-whitespace text + * in the formatter, even just replacing some code with itself. + * Retrospective attempts to reproduce this have failed, but this may be + * an issue for future changes. */ List replacements = new ArrayList<>(); int i = NOT_WHITESPACE.indexIn(source); @@ -163,8 +174,9 @@ private static List toReplacements(String source, String replacemen private SnippetWrapper snippetWrapper(SnippetKind kind, String source, int initialIndent) { /* - * Synthesize a dummy class around the code snippet provided by Eclipse. The dummy class is - * correctly formatted -- the blocks use correct indentation, etc. + * Synthesize a dummy class around the code snippet provided by Eclipse. + * The dummy class is correctly formatted -- the blocks use correct + * indentation, etc. */ switch (kind) { case COMPILATION_UNIT: @@ -215,4 +227,8 @@ private SnippetWrapper snippetWrapper(SnippetKind kind, String source, int initi throw new IllegalArgumentException("Unknown snippet kind: " + kind); } } + + private int indentationSize() { + return SPACES_PER_INDENT_LEVEL * formatter.options().indentationMultiplier(); + } } diff --git a/eclipse_plugin/META-INF/MANIFEST.MF b/eclipse_plugin/META-INF/MANIFEST.MF index db87451a3..27613e9ce 100644 --- a/eclipse_plugin/META-INF/MANIFEST.MF +++ b/eclipse_plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: google-java-format Bundle-SymbolicName: google-java-format-eclipse-plugin;singleton:=true -Bundle-Version: 1.5.0 +Bundle-Version: 1.6.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.eclipse.jdt.core;bundle-version="3.10.0", org.eclipse.jface, @@ -11,5 +11,5 @@ Require-Bundle: org.eclipse.jdt.core;bundle-version="3.10.0", org.eclipse.equinox.common Bundle-ClassPath: ., lib/guava-22.0.jar, - lib/javac-shaded-9-dev-r4023-3.jar, - lib/google-java-format-1.5.jar + lib/javac-shaded-9+181-r4173-1.jar, + lib/google-java-format-1.6.jar diff --git a/eclipse_plugin/build.properties b/eclipse_plugin/build.properties index 04f9f31bd..dc5ae7c7e 100644 --- a/eclipse_plugin/build.properties +++ b/eclipse_plugin/build.properties @@ -3,6 +3,6 @@ output.. = target/classes bin.includes = META-INF/,\ .,\ plugin.xml,\ - lib/javac-shaded-9-dev-r4023-3.jar,\ + lib/javac-shaded-9+181-r4173-1.jar,\ lib/guava-22.0.jar,\ - lib/google-java-format-1.5.jar + lib/google-java-format-1.6.jar diff --git a/eclipse_plugin/plugin.xml b/eclipse_plugin/plugin.xml index 6ae2d0516..6d8eb6a98 100644 --- a/eclipse_plugin/plugin.xml +++ b/eclipse_plugin/plugin.xml @@ -5,7 +5,12 @@ + name="google-java-format 1.6"> + + diff --git a/eclipse_plugin/pom.xml b/eclipse_plugin/pom.xml index ff1c1341c..662fbc554 100644 --- a/eclipse_plugin/pom.xml +++ b/eclipse_plugin/pom.xml @@ -20,11 +20,11 @@ com.google.googlejavaformat google-java-format-parent - 1.5 + 1.6 google-java-format-eclipse-plugin - 1.5.0 + 1.6.0 eclipse-plugin google-java-format Plugin for Eclipse 4.5+ @@ -48,7 +48,7 @@ com.google.googlejavaformat google-java-format - 1.5 + ${project.parent.version} diff --git a/eclipse_plugin/src/com/google/googlejavaformat/java/AbstractJavaFormatter.java b/eclipse_plugin/src/com/google/googlejavaformat/java/AbstractJavaFormatter.java new file mode 100644 index 000000000..7a4dd6b7a --- /dev/null +++ b/eclipse_plugin/src/com/google/googlejavaformat/java/AbstractJavaFormatter.java @@ -0,0 +1,162 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.googlejavaformat.java; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.formatter.CodeFormatter; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Range; +import com.google.googlejavaformat.java.SnippetFormatter.SnippetKind; + +/** + * This class provides the bulk of the logic to run java formatter within Eclipse. It provides the + * ability to formatting options to be specified. + */ +public abstract class AbstractJavaFormatter extends CodeFormatter { + + @Override + public TextEdit format( + int kind, String source, int offset, int length, int indentationLevel, String lineSeparator) { + IRegion[] regions = new IRegion[] {new Region(offset, length)}; + return formatInternal(kind, source, regions, indentationLevel); + } + + @Override + public TextEdit format( + int kind, String source, IRegion[] regions, int indentationLevel, String lineSeparator) { + return formatInternal(kind, source, regions, indentationLevel); + } + + @Override + public String createIndentationString(int indentationLevel) { + Preconditions.checkArgument( + indentationLevel >= 0, + "Indentation level cannot be less than zero. Given: %s", + indentationLevel); + int spaces = indentationLevel * indentationSize(); + StringBuilder buf = new StringBuilder(spaces); + for (int i = 0; i < spaces; i++) { + buf.append(' '); + } + return buf.toString(); + } + + /** Runs the Google Java formatter on the given source, with only the given ranges specified. */ + private TextEdit formatInternal(int kind, String source, IRegion[] regions, int initialIndent) { + try { + boolean includeComments = + (kind & CodeFormatter.F_INCLUDE_COMMENTS) == CodeFormatter.F_INCLUDE_COMMENTS; + kind &= ~CodeFormatter.F_INCLUDE_COMMENTS; + SnippetKind snippetKind; + switch (kind) { + case ASTParser.K_EXPRESSION: + snippetKind = SnippetKind.EXPRESSION; + break; + case ASTParser.K_STATEMENTS: + snippetKind = SnippetKind.STATEMENTS; + break; + case ASTParser.K_CLASS_BODY_DECLARATIONS: + snippetKind = SnippetKind.CLASS_BODY_DECLARATIONS; + break; + case ASTParser.K_COMPILATION_UNIT: + snippetKind = SnippetKind.COMPILATION_UNIT; + break; + default: + throw new IllegalArgumentException(String.format("Unknown snippet kind: %d", kind)); + } + List replacements = + createSnippetFormatter() + .format( + snippetKind, source, rangesFromRegions(regions), initialIndent, includeComments); + if (idempotent(source, regions, replacements)) { + // Do not create edits if there's no diff. + return null; + } + // Convert replacements to text edits. + return editFromReplacements(replacements); + } catch (IllegalArgumentException | FormatterException exception) { + // Do not format on errors. + return null; + } + } + + private List> rangesFromRegions(IRegion[] regions) { + List> ranges = new ArrayList<>(); + for (IRegion region : regions) { + ranges.add(Range.closedOpen(region.getOffset(), region.getOffset() + region.getLength())); + } + return ranges; + } + + /** @return {@code true} if input and output texts are equal, else {@code false}. */ + private boolean idempotent(String source, IRegion[] regions, List replacements) { + // This implementation only checks for single replacement. + if (replacements.size() == 1) { + Replacement replacement = replacements.get(0); + String output = replacement.getReplacementString(); + // Entire source case: input = output, nothing changed. + if (output.equals(source)) { + return true; + } + // Single region and single replacement case: if they are equal, + // nothing changed. + if (regions.length == 1) { + Range range = replacement.getReplaceRange(); + String snippet = source.substring(range.lowerEndpoint(), range.upperEndpoint()); + if (output.equals(snippet)) { + return true; + } + } + } + return false; + } + + private TextEdit editFromReplacements(List replacements) { + // Split the replacements that cross line boundaries. + TextEdit edit = new MultiTextEdit(); + for (Replacement replacement : replacements) { + Range replaceRange = replacement.getReplaceRange(); + edit.addChild( + new ReplaceEdit( + replaceRange.lowerEndpoint(), + replaceRange.upperEndpoint() - replaceRange.lowerEndpoint(), + replacement.getReplacementString())); + } + return edit; + } + + /** + * Create an instance of the formatter that will be used. This method should be aligned with + * {@link #indentationSize()} such that the indentation size corresponds with the indent level of + * the format options used to create the snippet formatter. + */ + protected abstract SnippetFormatter createSnippetFormatter(); + + /** + * The number of spaces per indent level. This method should be aligned with {@link + * #createSnippetFormatter()} such that the indentation size corresponds with the indent level of + * the format options used to create the snippet formatter. + */ + protected abstract int indentationSize(); +} diff --git a/eclipse_plugin/src/com/google/googlejavaformat/java/AospJavaFormatter.java b/eclipse_plugin/src/com/google/googlejavaformat/java/AospJavaFormatter.java new file mode 100644 index 000000000..2207531a7 --- /dev/null +++ b/eclipse_plugin/src/com/google/googlejavaformat/java/AospJavaFormatter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.googlejavaformat.java; + +import com.google.googlejavaformat.java.JavaFormatterOptions.Style; + +/** Runs the AOSP Java formatter on the given code. */ +public class AospJavaFormatter extends AbstractJavaFormatter { + + @Override + protected SnippetFormatter createSnippetFormatter() { + return new SnippetFormatter(JavaFormatterOptions.builder().style(Style.AOSP).build()); + } + + @Override + protected int indentationSize() { + return 4; + } +} diff --git a/eclipse_plugin/src/com/google/googlejavaformat/java/GoogleJavaFormatter.java b/eclipse_plugin/src/com/google/googlejavaformat/java/GoogleJavaFormatter.java index c300514be..5a0ee155c 100644 --- a/eclipse_plugin/src/com/google/googlejavaformat/java/GoogleJavaFormatter.java +++ b/eclipse_plugin/src/com/google/googlejavaformat/java/GoogleJavaFormatter.java @@ -14,131 +14,16 @@ package com.google.googlejavaformat.java; -import com.google.common.base.Preconditions; -import com.google.common.collect.Range; -import com.google.googlejavaformat.java.SnippetFormatter.SnippetKind; -import java.util.ArrayList; -import java.util.List; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.formatter.CodeFormatter; -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.Region; -import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.text.edits.ReplaceEdit; -import org.eclipse.text.edits.TextEdit; - /** Runs the Google Java formatter on the given code. */ -public class GoogleJavaFormatter extends CodeFormatter { - - private static final int INDENTATION_SIZE = 2; +public class GoogleJavaFormatter extends AbstractJavaFormatter { @Override - public TextEdit format( - int kind, String source, int offset, int length, int indentationLevel, String lineSeparator) { - IRegion[] regions = new IRegion[] {new Region(offset, length)}; - return formatInternal(kind, source, regions, indentationLevel); + protected SnippetFormatter createSnippetFormatter() { + return new SnippetFormatter(); } @Override - public TextEdit format( - int kind, String source, IRegion[] regions, int indentationLevel, String lineSeparator) { - return formatInternal(kind, source, regions, indentationLevel); - } - - @Override - public String createIndentationString(int indentationLevel) { - Preconditions.checkArgument( - indentationLevel >= 0, - "Indentation level cannot be less than zero. Given: %s", - indentationLevel); - int spaces = indentationLevel * INDENTATION_SIZE; - StringBuilder buf = new StringBuilder(spaces); - for (int i = 0; i < spaces; i++) { - buf.append(' '); - } - return buf.toString(); - } - - /** Runs the Google Java formatter on the given source, with only the given ranges specified. */ - private TextEdit formatInternal(int kind, String source, IRegion[] regions, int initialIndent) { - try { - boolean includeComments = - (kind & CodeFormatter.F_INCLUDE_COMMENTS) == CodeFormatter.F_INCLUDE_COMMENTS; - kind &= ~CodeFormatter.F_INCLUDE_COMMENTS; - SnippetKind snippetKind; - switch (kind) { - case ASTParser.K_EXPRESSION: - snippetKind = SnippetKind.EXPRESSION; - break; - case ASTParser.K_STATEMENTS: - snippetKind = SnippetKind.STATEMENTS; - break; - case ASTParser.K_CLASS_BODY_DECLARATIONS: - snippetKind = SnippetKind.CLASS_BODY_DECLARATIONS; - break; - case ASTParser.K_COMPILATION_UNIT: - snippetKind = SnippetKind.COMPILATION_UNIT; - break; - default: - throw new IllegalArgumentException(String.format("Unknown snippet kind: %d", kind)); - } - List replacements = - new SnippetFormatter() - .format( - snippetKind, source, rangesFromRegions(regions), initialIndent, includeComments); - if (idempotent(source, regions, replacements)) { - // Do not create edits if there's no diff. - return null; - } - // Convert replacements to text edits. - return editFromReplacements(replacements); - } catch (IllegalArgumentException | FormatterException exception) { - // Do not format on errors. - return null; - } - } - - private List> rangesFromRegions(IRegion[] regions) { - List> ranges = new ArrayList<>(); - for (IRegion region : regions) { - ranges.add(Range.closedOpen(region.getOffset(), region.getOffset() + region.getLength())); - } - return ranges; - } - - /** @return {@code true} if input and output texts are equal, else {@code false}. */ - private boolean idempotent(String source, IRegion[] regions, List replacements) { - // This implementation only checks for single replacement. - if (replacements.size() == 1) { - Replacement replacement = replacements.get(0); - String output = replacement.getReplacementString(); - // Entire source case: input = output, nothing changed. - if (output.equals(source)) { - return true; - } - // Single region and single replacement case: if they are equal, nothing changed. - if (regions.length == 1) { - Range range = replacement.getReplaceRange(); - String snippet = source.substring(range.lowerEndpoint(), range.upperEndpoint()); - if (output.equals(snippet)) { - return true; - } - } - } - return false; - } - - private TextEdit editFromReplacements(List replacements) { - // Split the replacements that cross line boundaries. - TextEdit edit = new MultiTextEdit(); - for (Replacement replacement : replacements) { - Range replaceRange = replacement.getReplaceRange(); - edit.addChild( - new ReplaceEdit( - replaceRange.lowerEndpoint(), - replaceRange.upperEndpoint() - replaceRange.lowerEndpoint(), - replacement.getReplacementString())); - } - return edit; + protected int indentationSize() { + return 2; } } diff --git a/pom.xml b/pom.xml index 85da3d239..1b66215b3 100644 --- a/pom.xml +++ b/pom.xml @@ -28,14 +28,15 @@ com.google.googlejavaformat google-java-format-parent pom - 1.6-SNAPSHOT + 1.6 core + + eclipse_plugin Google Java Format Parent