diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index 8ec93c2f3fcc..0da40199fa65 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -706,7 +706,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo private Block createMethodCallExpressionBlock(@NotNull ASTNode node, Wrap blockWrap, Alignment alignment, Indent indent) { final ArrayList nodes = new ArrayList(); collectNodes(nodes, node); - return new ChainMethodCallsBlockBuilder(alignment, blockWrap, indent).build(nodes); + return new ChainMethodCallsBlockBuilder(alignment, blockWrap, indent, mySettings, myJavaSettings).build(nodes); } private static void collectNodes(@NotNull List nodes, @NotNull ASTNode node) { @@ -838,7 +838,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } @Nullable - private static Alignment createAlignment(final boolean alignOption, @Nullable final Alignment defaultAlignment) { + public static Alignment createAlignment(final boolean alignOption, @Nullable final Alignment defaultAlignment) { return alignOption ? createAlignmentOrDefault(null, defaultAlignment) : defaultAlignment; } @@ -1313,107 +1313,5 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return result; } - private class ChainMethodCallsBlockBuilder { - private Wrap blockWrap; - private Alignment blockAlignment; - private Indent blockIndent; - private Wrap myWrap; - private Alignment myChainedCallsAlignment; - - public ChainMethodCallsBlockBuilder(Alignment alignment, Wrap wrap, Indent indent) { - blockWrap = wrap; - blockAlignment = alignment; - blockIndent = indent; - } - - public Block build(List nodes) { - myWrap = getNewWrap(); - myChainedCallsAlignment = getNewAlignment(); - - List blocks = buildBlocksFrom(nodes); - - Indent indent = blockIndent != null ? blockIndent : Indent.getContinuationWithoutFirstIndent(myIndentSettings.USE_RELATIVE_INDENTS); - return new SyntheticCodeBlock(blocks, blockAlignment, mySettings, myJavaSettings, indent, blockWrap); - } - - private List buildBlocksFrom(List nodes) { - List methodCall = splitMethodCallOnChunksByDots(nodes); - Wrap wrapToUse = null; - Alignment alignmentToUse = null; - - List blocks = new ArrayList(); - - for (ChainedCallChunk currentCallChunk : methodCall) { - if (isMethodCall(currentCallChunk)) { - wrapToUse = myWrap; - alignmentToUse = shouldAlignMethod(currentCallChunk, methodCall) ? myChainedCallsAlignment : null; - } - else if (wrapToUse != null) { - wrapToUse = null; - alignmentToUse = null; - - myChainedCallsAlignment = getNewAlignment(); - myWrap = getNewWrap(); - } - - SyntheticBlockBuilder builder = new SyntheticBlockBuilder(mySettings, myJavaSettings); - blocks.add(builder.create(currentCallChunk.nodes, wrapToUse, alignmentToUse)); - } - - return blocks; - } - - private boolean shouldAlignMethod(ChainedCallChunk currentMethodChunk, List methodCall) { - return mySettings.ALIGN_MULTILINE_CHAINED_METHODS - && !currentMethodChunk.isEmpty() - && !chunkIsFirstInChainMethodCall(currentMethodChunk, methodCall); - } - - private boolean chunkIsFirstInChainMethodCall(@NotNull ChainedCallChunk callChunk, @NotNull List methodCall) { - return !methodCall.isEmpty() && callChunk == methodCall.get(0); - } - - @NotNull - private List splitMethodCallOnChunksByDots(@NotNull List nodes) { - List result = new ArrayList(); - - List current = new ArrayList(); - for (ASTNode node : nodes) { - if (node.getElementType() == JavaTokenType.DOT) { - result.add(new ChainedCallChunk(current)); - current = new ArrayList(); - } - current.add(node); - } - - result.add(new ChainedCallChunk(current)); - return result; - } - - private Alignment getNewAlignment() { - return createAlignment(mySettings.ALIGN_MULTILINE_CHAINED_METHODS, null); - } - - private Wrap getNewWrap() { - return Wrap.createWrap(getWrapType(mySettings.METHOD_CALL_CHAIN_WRAP), false); - } - - private boolean isMethodCall(@NotNull ChainedCallChunk callChunk) { - List nodes = callChunk.nodes; - return !nodes.isEmpty() && nodes.get(nodes.size() - 1).getElementType() == JavaElementType.EXPRESSION_LIST; - } - } - - private static class ChainedCallChunk { - @NotNull final List nodes; - - ChainedCallChunk(@NotNull List nodes) { - this.nodes = nodes; - } - - boolean isEmpty() { - return nodes.isEmpty(); - } - } } 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 new file mode 100644 index 000000000000..520fa0b03534 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/formatter/java/ChainMethodCallsBlockBuilder.java @@ -0,0 +1,148 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * 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.intellij.psi.formatter.java; + +import com.intellij.formatting.Alignment; +import com.intellij.formatting.Block; +import com.intellij.formatting.Indent; +import com.intellij.formatting.Wrap; +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.psi.impl.source.tree.JavaElementType; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +import static com.intellij.psi.formatter.java.JavaFormatterUtil.getWrapType; + +class ChainMethodCallsBlockBuilder { + private CommonCodeStyleSettings mySettings; + private CommonCodeStyleSettings.IndentOptions myIndentSettings; + private JavaCodeStyleSettings myJavaSettings; + + private Wrap myBlockWrap; + private Alignment myBlockAlignment; + private Indent myBlockIndent; + + private Wrap myWrap; + private Alignment myChainedCallsAlignment; + + public ChainMethodCallsBlockBuilder(Alignment alignment, + Wrap wrap, + Indent indent, + CommonCodeStyleSettings settings, + JavaCodeStyleSettings javaSettings) + { + myBlockWrap = wrap; + myBlockAlignment = alignment; + myBlockIndent = indent; + mySettings = settings; + myIndentSettings = settings.getIndentOptions(); + myJavaSettings = javaSettings; + } + + public Block build(List nodes) { + myWrap = getNewWrap(); + myChainedCallsAlignment = getNewAlignment(); + + List blocks = buildBlocksFrom(nodes); + + Indent indent = myBlockIndent != null ? myBlockIndent : Indent.getContinuationWithoutFirstIndent(myIndentSettings.USE_RELATIVE_INDENTS); + return new SyntheticCodeBlock(blocks, myBlockAlignment, mySettings, myJavaSettings, indent, myBlockWrap); + } + + private List buildBlocksFrom(List nodes) { + List methodCall = splitMethodCallOnChunksByDots(nodes); + Wrap wrapToUse = null; + Alignment alignmentToUse = null; + + List blocks = new ArrayList(); + + for (ChainedCallChunk currentCallChunk : methodCall) { + if (isMethodCall(currentCallChunk)) { + wrapToUse = myWrap; + alignmentToUse = shouldAlignMethod(currentCallChunk, methodCall) ? myChainedCallsAlignment : null; + } + else if (wrapToUse != null) { + wrapToUse = null; + alignmentToUse = null; + + myChainedCallsAlignment = getNewAlignment(); + myWrap = getNewWrap(); + } + + SyntheticBlockBuilder builder = new SyntheticBlockBuilder(mySettings, myJavaSettings); + blocks.add(builder.create(currentCallChunk.nodes, wrapToUse, alignmentToUse)); + } + + return blocks; + } + + private boolean shouldAlignMethod(ChainedCallChunk currentMethodChunk, List methodCall) { + return mySettings.ALIGN_MULTILINE_CHAINED_METHODS + && !currentMethodChunk.isEmpty() + && !chunkIsFirstInChainMethodCall(currentMethodChunk, methodCall); + } + + private boolean chunkIsFirstInChainMethodCall(@NotNull ChainedCallChunk callChunk, @NotNull List methodCall) { + return !methodCall.isEmpty() && callChunk == methodCall.get(0); + } + + @NotNull + private List splitMethodCallOnChunksByDots(@NotNull List nodes) { + List result = new ArrayList(); + + List current = new ArrayList(); + for (ASTNode node : nodes) { + if (node.getElementType() == JavaTokenType.DOT) { + result.add(new ChainedCallChunk(current)); + current = new ArrayList(); + } + current.add(node); + } + + result.add(new ChainedCallChunk(current)); + return result; + } + + private Alignment getNewAlignment() { + return AbstractJavaBlock.createAlignment(mySettings.ALIGN_MULTILINE_CHAINED_METHODS, null); + } + + private Wrap getNewWrap() { + return Wrap.createWrap(getWrapType(mySettings.METHOD_CALL_CHAIN_WRAP), false); + } + + private boolean isMethodCall(@NotNull ChainedCallChunk callChunk) { + List nodes = callChunk.nodes; + return !nodes.isEmpty() && nodes.get(nodes.size() - 1).getElementType() == JavaElementType.EXPRESSION_LIST; + } +} + +class ChainedCallChunk { + @NotNull final List nodes; + + ChainedCallChunk(@NotNull List nodes) { + this.nodes = nodes; + } + + boolean isEmpty() { + return nodes.isEmpty(); + } +}