diff --git a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java index eca55d069e4e..86210038b719 100644 --- a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java +++ b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java @@ -201,6 +201,11 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett String groupName = ApplicationBundle.message("wrapping.fields.annotation"); consumer.showCustomOption(JavaCodeStyleSettings.class, "DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION", "Do not wrap after single annotation", groupName); + + consumer.showCustomOption(JavaCodeStyleSettings.class, + "PLACE_DOT_ON_NEXT_LINE", + ApplicationBundle.message("checkbox.place.dot.on.next.line"), + CodeStyleSettingsCustomizable.WRAPPING_CALL_CHAIN); } else if (settingsType == SettingsType.BLANK_LINES_SETTINGS) { consumer.showAllStandardOptions(); diff --git a/java/java-impl/src/com/intellij/psi/codeStyle/JavaCodeStyleSettings.java b/java/java-impl/src/com/intellij/psi/codeStyle/JavaCodeStyleSettings.java index 8a4dfaf512a4..5195d7098094 100644 --- a/java/java-impl/src/com/intellij/psi/codeStyle/JavaCodeStyleSettings.java +++ b/java/java-impl/src/com/intellij/psi/codeStyle/JavaCodeStyleSettings.java @@ -100,6 +100,12 @@ public class JavaCodeStyleSettings extends CustomCodeStyleSettings implements Im public boolean SPACES_WITHIN_ANGLE_BRACKETS; + + + public boolean PLACE_DOT_ON_NEXT_LINE = true; + + + //Type arguments public boolean SPACE_AFTER_CLOSING_ANGLE_BRACKET_IN_TYPE_ARGUMENT; diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/CallChunkBlockBuilder.java b/java/java-impl/src/com/intellij/psi/formatter/java/CallChunkBlockBuilder.java index 45581eb85639..aea151296d87 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/CallChunkBlockBuilder.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/CallChunkBlockBuilder.java @@ -21,6 +21,9 @@ import com.intellij.lang.ASTNode; import com.intellij.psi.JavaTokenType; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.codeStyle.JavaCodeStyleSettings; +import com.intellij.util.CollectionsKt; +import com.siyeh.ig.psiutils.CollectionUtils; +import org.apache.commons.collections.ListUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -58,7 +61,24 @@ public class CallChunkBlockBuilder { } return new SyntheticCodeBlock(subBlocks, alignment, mySettings, myJavaSettings, Indent.getContinuationIndent(myIndentSettings.USE_RELATIVE_INDENTS), wrap); } - return new SyntheticCodeBlock(createJavaBlocks(subNodes), alignment, mySettings, myJavaSettings, Indent.getContinuationWithoutFirstIndent(myIndentSettings.USE_RELATIVE_INDENTS), null); + // Support for groovy style dot placement + final ASTNode lastNode = subNodes.get(subNodes.size() - 1); + if (lastNode.getElementType() == JavaTokenType.DOT) { + AlignmentStrategy strategy = AlignmentStrategy.getNullStrategy(); + subNodes.remove(subNodes.size() - 1); + if (!subNodes.isEmpty()) { + subBlocks.add(create(subNodes, wrap, null)); + } + Block block = newJavaBlock(lastNode, mySettings, myJavaSettings, Indent.getNoneIndent(), Wrap.createWrap(WrapType.ALWAYS, true), strategy, myFormattingMode); + subBlocks.add(block); + SyntheticCodeBlock syntheticCodeBlock = new SyntheticCodeBlock(subBlocks, alignment, mySettings, myJavaSettings, + Indent.getContinuationIndent(myIndentSettings.USE_RELATIVE_INDENTS), Wrap.createWrap(WrapType.NONE, false)); + return syntheticCodeBlock; + } + List blocks = createJavaBlocks(subNodes); + return new SyntheticCodeBlock(blocks, alignment, mySettings, myJavaSettings, + Indent.getContinuationWithoutFirstIndent(myIndentSettings.USE_RELATIVE_INDENTS), + null); } @NotNull diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/ChainMethodCallsBlockBuilder.java b/java/java-impl/src/com/intellij/psi/formatter/java/ChainMethodCallsBlockBuilder.java index 58557629661e..3331bdf3f356 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/ChainMethodCallsBlockBuilder.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/ChainMethodCallsBlockBuilder.java @@ -82,7 +82,11 @@ class ChainMethodCallsBlockBuilder { } } else { - wrap = null; + if (!myJavaSettings.PLACE_DOT_ON_NEXT_LINE) { + wrap = Wrap.createWrap(WrapType.ALWAYS, true); + } else { + wrap = null; + } chainedCallsAlignment = null; } @@ -123,23 +127,41 @@ class ChainMethodCallsBlockBuilder { } @NotNull - private static List splitMethodCallOnChunksByDots(@NotNull List nodes) { + private List splitMethodCallOnChunksByDots(@NotNull List nodes) { List result = new ArrayList<>(); - List current = new ArrayList<>(); - for (ASTNode node : nodes) { - if (node.getElementType() == JavaTokenType.DOT || node.getPsi() instanceof PsiComment) { - if (!current.isEmpty()) { - result.add(new ChainedCallChunk(current)); + if (myJavaSettings.PLACE_DOT_ON_NEXT_LINE) { + List current = new ArrayList<>(); + for (ASTNode node : nodes) { + if (node.getElementType() == JavaTokenType.DOT || node.getPsi() instanceof PsiComment) { + if (!current.isEmpty()) { + result.add(new ChainedCallChunk(current)); + } + current = new ArrayList<>(); } - current = new ArrayList<>(); + current.add(node); + } + + if (!current.isEmpty()) { + result.add(new ChainedCallChunk(current)); + } + } else { + List current = new ArrayList<>(); + for (ASTNode node : nodes) { + current.add(node); + if (node.getElementType() == JavaTokenType.DOT || node.getPsi() instanceof PsiComment) { + if (!current.isEmpty()) { + result.add(new ChainedCallChunk(current)); + } + current = new ArrayList<>(); + } + } + + if (!current.isEmpty()) { + result.add(new ChainedCallChunk(current)); } - current.add(node); - } - - if (!current.isEmpty()) { - result.add(new ChainedCallChunk(current)); } + return result; } diff --git a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavaFormatterWrapTest.java b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavaFormatterWrapTest.java index b2a2931f49fa..f0990d052d06 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavaFormatterWrapTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JavaFormatterWrapTest.java @@ -935,4 +935,44 @@ public class JavaFormatterWrapTest extends AbstractJavaFormatterTest { "}" ); } + + public void testDotPlacement() { + //getSettings().WRAP_LONG_LINES = true; + //getSettings().WRAP_COMMENTS = true; + //getSettings().RIGHT_MARGIN = 50; + getJavaSettings().PLACE_DOT_ON_NEXT_LINE = false; + + + doTextTest( + "public class Chains {\n" + + " static Chains get() {return null;}\n" + + " Chains foo() {\n" + + " return null;\n" + + " }\n" + + " Chains bar() {\n" + + " return null;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " get()\n" + + " .bar()\n" + + " .foo()\n" + + " .bar()\n" + + " .bar()\n" + + " .foo();\n" + + " }\n" + + "}\n", + + "public class Main {\n" + + "\n" + + " /**\n" + + " * {@link #authenticationCompleted(android.app.Activity,\n" + + " * int, int, android.content.Intent)}\n" + + " *\n" + + " * @param args\n" + + " */\n" + + " public static void main(String[] args) {\n" + + " }\n" + + "}" + ); + } } diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 86ebf8014182..aa0d1b7c8342 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -511,6 +511,7 @@ group.javadoc.alignment=Alignment checkbox.enable.javadoc.formatting=Enable JavaDoc formatting checkbox.align.parameter.descriptions=Align parameter descriptions checkbox.align.thrown.exception.descriptions=Align thrown exception descriptions +checkbox.place.dot.on.next.line=Place dot on next line in chained calls checkbox.after.description=After description checkbox.after.parameter.descriptions=After parameter descriptions checkbox.after.return.tag=After return tag