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 d6881ee3e733..e3736b1e0575 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 @@ -106,7 +106,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo final CodeStyleSettings settings, final JavaWrapManager wrapManager, @NotNull final AlignmentStrategy alignmentStrategy, AlignmentInColumnsHelper alignmentInColumnsHelper) { - super(node, wrap, alignmentStrategy.getAlignment(node.getElementType())); + super(node, wrap, createBlockAlignment(alignmentStrategy, node)); mySettings = settings; myIndentSettings = settings.getIndentOptions(StdFileTypes.JAVA); myIndent = indent; @@ -115,6 +115,16 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo myAlignmentInColumnsHelper = alignmentInColumnsHelper; } + @Nullable + private static Alignment createBlockAlignment(@NotNull AlignmentStrategy strategy, @NotNull ASTNode node) { + // There is a possible case that 'implements' section is incomplete (e.g. ends with comma). We may want to align lbrace + // to the first implemented interface reference then. + if (node.getElementType() == JavaElementType.IMPLEMENTS_LIST) { + return null; + } + return strategy.getAlignment(node.getElementType()); + } + public static Block createJavaBlock(final ASTNode child, final CodeStyleSettings settings, final Indent indent, @@ -168,7 +178,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return block; } else if (isLikeExtendsList(elementType)) { - return new ExtendsListBlock(child, wrap, alignment, settings); + return new ExtendsListBlock(child, wrap, alignmentStrategy, settings); } else if (elementType == JavaElementType.CODE_BLOCK) { return new CodeBlockBlock(child, wrap, alignment, actualIndent, settings); @@ -330,10 +340,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo else if (nodeType == JavaElementType.FOR_STATEMENT) { return createAlignment(mySettings.ALIGN_MULTILINE_FOR, null); } - else if (nodeType == JavaElementType.EXTENDS_LIST) { - return createAlignment(mySettings.ALIGN_MULTILINE_EXTENDS_LIST, null); - } - else if (nodeType == JavaElementType.IMPLEMENTS_LIST) { + else if (nodeType == JavaElementType.EXTENDS_LIST || nodeType == JavaElementType.IMPLEMENTS_LIST) { return createAlignment(mySettings.ALIGN_MULTILINE_EXTENDS_LIST, null); } else if (nodeType == JavaElementType.THROWS_LIST) { @@ -349,13 +356,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } return createAlignment(mySettings.ALIGN_MULTILINE_BINARY_OPERATION, defaultAlignment); } - else if (nodeType == JavaElementType.CLASS) { + else if (nodeType == JavaElementType.CLASS || nodeType == JavaElementType.METHOD) { return Alignment.createAlignment(); } - else if (nodeType == JavaElementType.METHOD) { - return Alignment.createAlignment(); - } - else if (nodeType == JavaElementType.MODIFIER_LIST) { return myAlignment; } @@ -514,9 +517,15 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo result.add(new SimpleJavaBlock(child, defaultWrap, alignmentStrategy, childIndent, mySettings)); } else { - final Block block = - createJavaBlock(child, mySettings, childIndent, arrangeChildWrap(child, defaultWrap), - AlignmentStrategy.wrap(arrangeChildAlignment(child, alignmentStrategy)), childOffset); + AlignmentStrategy alignmentStrategyToUse = AlignmentStrategy.wrap(arrangeChildAlignment(child, alignmentStrategy)); + if (myAlignmentStrategy != null && myAlignmentStrategy.getAlignment(nodeType, childType) != null + && (nodeType == JavaElementType.IMPLEMENTS_LIST || nodeType == JavaElementType.CLASS)) + { + alignmentStrategyToUse = myAlignmentStrategy; + } + final Block block = createJavaBlock( + child, mySettings, childIndent, arrangeChildWrap(child, defaultWrap), alignmentStrategyToUse, childOffset + ); if (childType == JavaElementType.MODIFIER_LIST && containsAnnotations(child)) { myAnnotationWrap = Wrap.createWrap(getWrapType(getAnnotationWrapType(child)), true); @@ -524,12 +533,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo if (block instanceof AbstractJavaBlock) { final AbstractJavaBlock javaBlock = (AbstractJavaBlock)block; - if (nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION) { - javaBlock.setReservedWrap(getReservedWrap(nodeType), nodeType); - javaBlock.setReservedWrap(getReservedWrap(childType), childType); - } - else if (nodeType == JavaElementType.REFERENCE_EXPRESSION && - childType == JavaElementType.METHOD_CALL_EXPRESSION) { + if ((nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION) + || (nodeType == JavaElementType.REFERENCE_EXPRESSION && childType == JavaElementType.METHOD_CALL_EXPRESSION)) + { javaBlock.setReservedWrap(getReservedWrap(nodeType), nodeType); javaBlock.setReservedWrap(getReservedWrap(childType), childType); } @@ -596,7 +602,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } @NotNull private static ASTNode findLastFieldInGroup(final ASTNode child) { - final PsiTypeElement typeElement = ((PsiVariable)child.getPsi()).getTypeElement(); + PsiElement psi = child.getPsi(); + if (psi == null) { + return child; + } + final PsiTypeElement typeElement = ((PsiVariable)psi).getTypeElement(); if (typeElement == null) return child; ASTNode lastChildNode = child.getLastChildNode(); @@ -613,7 +623,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo || StdTokenSets.COMMENT_BIT_SET.contains(currentNode.getElementType())) { } else if (currentNode.getElementType() == JavaElementType.FIELD) { - if (((PsiVariable)currentNode.getPsi()).getTypeElement() != typeElement) { + if (((PsiVariable)psi).getTypeElement() != typeElement) { return currentResult; } else { @@ -786,7 +796,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } private static boolean containsAnnotations(final ASTNode child) { - return ((PsiModifierList)child.getPsi()).getAnnotations().length > 0; + PsiElement psi = child.getPsi(); + if (!(psi instanceof PsiModifierList)) { + return false; + } + return ((PsiModifierList)psi).getAnnotations().length > 0; } private int getAnnotationWrapType(@NotNull ASTNode child) { @@ -1094,18 +1108,15 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo if (psiNode instanceof PsiClass) { return mySettings.CLASS_BRACE_STYLE; } - else if (psiNode instanceof PsiMethod) { - return mySettings.METHOD_BRACE_STYLE; - } - - else if (psiNode instanceof PsiCodeBlock && psiNode.getParent() != null && psiNode.getParent() instanceof PsiMethod) { + else if (psiNode instanceof PsiMethod + || (psiNode instanceof PsiCodeBlock && psiNode.getParent() != null && psiNode.getParent() instanceof PsiMethod)) + { return mySettings.METHOD_BRACE_STYLE; } else { return mySettings.BRACE_STYLE; } - } protected Indent getCodeBlockInternalIndent(final int baseChildrenIndent) { @@ -1269,14 +1280,16 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo child = child.getTreeNext(); AlignmentStrategy varDeclarationAlignmentStrategy - = AlignmentStrategy.createAlignmentPerTypeStrategy(VAR_DECLARATION_ELEMENT_TYPES_TO_ALIGN, true); + = AlignmentStrategy.createAlignmentPerTypeStrategy(VAR_DECLARATION_ELEMENT_TYPES_TO_ALIGN, JavaElementType.FIELD, true); while (child != null) { // We consider that subsequent fields shouldn't be aligned if they are separated by blank line(s). if (!FormatterUtil.containsWhiteSpacesOnly(child)) { if (!ElementType.JAVA_COMMENT_BIT_SET.contains(child.getElementType()) && !shouldUseVarDeclarationAlignment(child)) { // Reset var declaration alignment. - varDeclarationAlignmentStrategy = AlignmentStrategy.createAlignmentPerTypeStrategy(VAR_DECLARATION_ELEMENT_TYPES_TO_ALIGN, true); + varDeclarationAlignmentStrategy = AlignmentStrategy.createAlignmentPerTypeStrategy( + VAR_DECLARATION_ELEMENT_TYPES_TO_ALIGN, JavaElementType.FIELD, true + ); } final boolean rBrace = isRBrace(child); Indent childIndent = rBrace ? Indent.getNoneIndent() : getCodeBlockInternalIndent(childrenIndent); diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java index 2ccafc0a234c..e171ccaab0cd 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/CodeBlockBlock.java @@ -17,6 +17,7 @@ package com.intellij.psi.formatter.java; import com.intellij.formatting.*; import com.intellij.lang.ASTNode; +import com.intellij.psi.JavaTokenType; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.formatter.common.AbstractBlock; @@ -45,7 +46,7 @@ public class CodeBlockBlock extends AbstractJavaBlock { final Alignment alignment, final Indent indent, final CodeStyleSettings settings) { - super(node, wrap, alignment, indent, settings); + super(node, wrap, getAlignmentStrategy(alignment, node, settings), indent, settings); if (isSwitchCodeBlock() && !settings.INDENT_CASE_FROM_SWITCH) { myChildrenIndent = 0; } @@ -54,6 +55,37 @@ public class CodeBlockBlock extends AbstractJavaBlock { } } + /** + * There is a possible case that 'implements' section is incomplete (e.g. ends with comma). We may want to align lbrace + * to the comma then. + * + * @param alignment block alignment + * @param baseNode base AST node + * @return alignment strategy to use for the given node + */ + private static AlignmentStrategy getAlignmentStrategy(Alignment alignment, ASTNode baseNode, @NotNull CodeStyleSettings settings) { + if (baseNode.getElementType() != JavaElementType.CLASS || !settings.ALIGN_MULTILINE_EXTENDS_LIST) { + return AlignmentStrategy.wrap(alignment); + } + for (ASTNode node = baseNode.getLastChildNode(); node != null; node = FormattingAstUtil.getPrevNonWhiteSpaceNode(node)) { + if (node.getElementType() != JavaElementType.IMPLEMENTS_LIST) { + continue; + } + ASTNode lastChildNode = node.getLastChildNode(); + if (lastChildNode != null && lastChildNode.getElementType() == JavaTokenType.ERROR_ELEMENT) { + Alignment alignmentToUse = alignment; + if (alignment == null) { + alignmentToUse = Alignment.createAlignment(); + } + return AlignmentStrategy.wrap( + alignmentToUse, false, JavaTokenType.LBRACE, JavaElementType.JAVA_CODE_REFERENCE, node.getElementType() + ); + } + break; + } + return AlignmentStrategy.wrap(alignment); + } + private boolean isSwitchCodeBlock() { return myNode.getTreeParent().getElementType() == ElementType.SWITCH_STATEMENT; } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/ExtendsListBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/ExtendsListBlock.java index c1042d823a17..696d5e146d8c 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/ExtendsListBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/ExtendsListBlock.java @@ -19,6 +19,7 @@ import com.intellij.formatting.Alignment; import com.intellij.formatting.Block; import com.intellij.formatting.Indent; import com.intellij.formatting.Wrap; +import com.intellij.formatting.alignment.AlignmentStrategy; import com.intellij.lang.ASTNode; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.FormatterUtil; @@ -31,6 +32,10 @@ public class ExtendsListBlock extends AbstractJavaBlock{ public ExtendsListBlock(final ASTNode node, final Wrap wrap, final Alignment alignment, CodeStyleSettings settings) { super(node, wrap, alignment, Indent.getNoneIndent(), settings); } + + public ExtendsListBlock(final ASTNode node, final Wrap wrap, final AlignmentStrategy alignmentStrategy, CodeStyleSettings settings) { + super(node, wrap, alignmentStrategy, Indent.getNoneIndent(), settings); + } protected List buildChildren() { final ArrayList result = new ArrayList(); @@ -52,8 +57,13 @@ public class ExtendsListBlock extends AbstractJavaBlock{ } result.add(createJavaBlock(child, mySettings, myChildIndent, arrangeChildWrap(child, childWrap), alignment)); } else { + if (myAlignmentStrategy != null) { + Alignment candidate = myAlignmentStrategy.getAlignment(child.getElementType()); + if (candidate != null) { + alignment = myChildAlignment = candidate; + } + } processChild(elementsExceptKeyword, child, myChildAlignment, childWrap, myChildIndent); - } } child = child.getTreeNext(); @@ -67,9 +77,7 @@ public class ExtendsListBlock extends AbstractJavaBlock{ } private boolean alignList() { - if (myNode.getElementType() == ElementType.EXTENDS_LIST) { - return mySettings.ALIGN_MULTILINE_EXTENDS_LIST; - } else if (myNode.getElementType() == ElementType.IMPLEMENTS_LIST) { + if (myNode.getElementType() == ElementType.EXTENDS_LIST || myNode.getElementType() == ElementType.IMPLEMENTS_LIST) { return mySettings.ALIGN_MULTILINE_EXTENDS_LIST; } else if (myNode.getElementType() == ElementType.THROWS_LIST) { return mySettings.ALIGN_MULTILINE_THROWS_LIST; diff --git a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java index 48ddfa3c3e24..04e01043456a 100644 --- a/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java +++ b/platform/lang-impl/src/com/intellij/formatting/InitialInfoBuilder.java @@ -154,7 +154,7 @@ class InitialInfoBuilder { } myCurrentWhiteSpace.append(blockStartOffset, myModel, myOptions); - boolean isReadOnly = isReadOnly(textRange, rootBlockIsRightBlock); + boolean isReadOnly = isReadOnly(rootBlock, rootBlockIsRightBlock); ReadOnlyBlockInformationProvider previousProvider = myReadOnlyBlockInformationProvider; try { @@ -320,11 +320,49 @@ class InitialInfoBuilder { } } - private boolean isReadOnly(final TextRange textRange, boolean rootIsRightBlock) { - if (myAffectedRanges == null) return false; - return myAffectedRanges.isReadOnly(textRange, rootIsRightBlock); + private boolean isReadOnly(final Block block, boolean rootIsRightBlock) { + if (myAffectedRanges == null || !myAffectedRanges.isReadOnly(block.getTextRange(), rootIsRightBlock)) { + return false; + } + + if (!block.isIncomplete()) { + return true; + } + + // There is a possible case that particular sub-block has an alignment and we don't want to loose information about it by + // excluding that sub-block from the processing. + List blocks = block.getSubBlocks(); + for (Block subBlock : blocks) { + if (hasAlignmentInTree(subBlock)) { + return false; + } + } + return true; } + /** + * Allows to answer if given block or any of its sub-blocks has defined alignment to use. + * + * @param block root block to check + * @return true if given block or any of its sub-blocks has defined alignment to use; + * false otherwise + */ + private static boolean hasAlignmentInTree(@NotNull Block block) { + Stack blocks = new Stack(); + blocks.push(block); + + while (!blocks.isEmpty()) { + Block blockToProcess = blocks.pop(); + if (blockToProcess.getAlignment() != null) { + return true; + } + for (Block subBlock : blockToProcess.getSubBlocks()) { + blocks.push(subBlock); + } + } + return false; + } + public Map getBlockToInfoMap() { return myResult; } diff --git a/platform/lang-impl/src/com/intellij/formatting/alignment/AlignmentStrategy.java b/platform/lang-impl/src/com/intellij/formatting/alignment/AlignmentStrategy.java index 7d18afe45ff8..ef83a48de476 100644 --- a/platform/lang-impl/src/com/intellij/formatting/alignment/AlignmentStrategy.java +++ b/platform/lang-impl/src/com/intellij/formatting/alignment/AlignmentStrategy.java @@ -17,43 +17,68 @@ package com.intellij.formatting.alignment; import com.intellij.formatting.Alignment; import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; import static java.util.Arrays.asList; -/** - * GoF 'Strategy' for {@link Alignment} retrieval. - */ -public abstract class AlignmentStrategy { +/** GoF 'Strategy' for {@link Alignment} retrieval. */ +public abstract class AlignmentStrategy { - private static final AlignmentStrategy NULL_STRATEGY = new SharedAlignmentStrategy(null); + private static final AlignmentStrategy NULL_STRATEGY = wrap(null); - /** - * @return shared strategy instance that returns null all the time - */ + /** @return shared strategy instance that returns null all the time */ public static AlignmentStrategy getNullStrategy() { return NULL_STRATEGY; } /** - * Constructs strategy that returns given alignment for all elements except those which types are delivered as a trailing argument. + * Delegates the processing to {@link #wrap(Alignment, boolean, IElementType...)} with 'true' as the second argument * - * @param alignment target alignment to wrap - * @param typesToIgnore types of the elements for which null should be returned on subsequent calls - * to {@link #getAlignment(IElementType)} - * @return strategy that returns given alignment all the time for elements which types are not defined - * as 'types to ignore'; null is returned for them + * @param alignment + * @param filterTypes + * @return */ - public static AlignmentStrategy wrap(Alignment alignment, IElementType ... typesToIgnore) { - return new SharedAlignmentStrategy(alignment, typesToIgnore); + public static AlignmentStrategy wrap(Alignment alignment, IElementType... filterTypes) { + return new SharedAlignmentStrategy(alignment, true, filterTypes); } + /** + * Constructs strategy that returns given alignment for all elements which types pass through the target filter. + * + * @param alignment target alignment to wrap + * @param ignoreFilterTypes flag that defines if given alignment should be returned for all elements with given types or + * all elements except those with the given types + * @param filterTypes element types that should be used for filtering on subsequent calls + * to {@link #getAlignment(IElementType)} + * @return strategy that returns given alignment all the time for elements which types pass through the target + * filter; null otherwise + */ + public static AlignmentStrategy wrap(Alignment alignment, boolean ignoreFilterTypes, IElementType... filterTypes) { + return new SharedAlignmentStrategy(alignment, ignoreFilterTypes, filterTypes); + } + + /** + * Delegates to {@link #createAlignmentPerTypeStrategy(Collection, IElementType, boolean)} with no parent type + * check (null is delivered as a parent type). + * + * @param targetTypes target child types + * @param allowBackwardShift flag that defines if backward alignment shift is allowed + * @return alignment strategy for the given arguments + */ + public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy(@NotNull Collection targetTypes, + boolean allowBackwardShift) + { + return new AlignmentPerTypeStrategy(targetTypes, null, allowBackwardShift); + } + /** * Creates strategy that creates and caches one alignment per given type internally and returns it on subsequent calls - * to {@link #getAlignment(IElementType)} for elements which type is listed at the given collection. null - * is returned from {@link #getAlignment(IElementType)} for elements which types are not listed at the given collection. + * to {@link #getAlignment(IElementType, IElementType)} for elements which type is listed at the given collection and parent type + * (if defined) is the same as the given one; null is returned from {@link #getAlignment(IElementType, IElementType)} for all + * other elements. *

* This strategy is assumed to be used at following situations - suppose we want to align code blocks that doesn't belong * to the same parent but have similar structure, e.g. variable declaration assignments like the one below: @@ -64,19 +89,41 @@ public abstract class AlignmentStrategy { * We can provide parent blocks of that target blocks with the same instance of this alignment strategy and let them eventually * reuse the same alignment objects for target sub-blocks of the same type. * - * @param targetTypes target types for which cached alignment should be returned - * @param allowBackwardShift flag that specifies if former aligned element may be shifted to right in order to align - * to subsequent element (e.g. '=' block of 'int start = 1' statement - * below is shifted one symbol right in order to align to the '=' block - * of 'int finish = 1' statement) - * @return alignment retrieval strategy that follows the rules described above + * @param targetTypes target types for which cached alignment should be returned + * @param parentType target parent type + * @param allowBackwardShift flag that specifies if former aligned element may be shifted to right in order to align + * to subsequent element (e.g. '=' block of 'int start = 1' statement + * below is shifted one symbol right in order to align to the '=' block + * of 'int finish = 1' statement) + * @return alignment retrieval strategy that follows the rules described above */ - public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy(Collection targetTypes, boolean allowBackwardShift) { - return new AlignmentPerTypeStrategy(targetTypes, allowBackwardShift); + public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy( + @NotNull Collection targetTypes, @Nullable IElementType parentType, boolean allowBackwardShift) + { + return new AlignmentPerTypeStrategy(targetTypes, parentType, allowBackwardShift); } + /** + * Delegates the processing to {@link #getAlignment(IElementType, IElementType)} without parent element type + * filtering (null is used as parent element type). + * + * @param childType target child type + * @return alignment to use + */ @Nullable - public abstract Alignment getAlignment(IElementType elementType); + public Alignment getAlignment(@Nullable IElementType childType) { + return getAlignment(null, childType); + } + + /** + * Requests current strategy for alignment to use for the child of the given type assuming that parent node has the given type. + * + * @param parentType parent type to use for filtering (if not null) + * @param childType child type to use for filtering (if not null) + * @return alignment to use for the given arguments + */ + @Nullable + public abstract Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType); /** * Stands for {@link AlignmentStrategy} implementation that is configured to return single pre-configured {@link Alignment} object @@ -84,17 +131,20 @@ public abstract class AlignmentStrategy { */ private static class SharedAlignmentStrategy extends AlignmentStrategy { - private final Set myDisableElementTypes = new HashSet(); - private final Alignment myAlignment; + private final Set myFilterElementTypes = new HashSet(); - SharedAlignmentStrategy(Alignment alignment, IElementType ... disabledElementTypes) { + private final Alignment myAlignment; + private final boolean myIgnoreFilterTypes; + + private SharedAlignmentStrategy(Alignment alignment, boolean ignoreFilterTypes, IElementType... disabledElementTypes) { myAlignment = alignment; - myDisableElementTypes.addAll(asList(disabledElementTypes)); + myIgnoreFilterTypes = ignoreFilterTypes; + myFilterElementTypes.addAll(asList(disabledElementTypes)); } @Nullable - public Alignment getAlignment(IElementType elementType) { - return myDisableElementTypes.contains(elementType) ? null : myAlignment; + public Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType) { + return (myFilterElementTypes.contains(childType) ^ myIgnoreFilterTypes) ? myAlignment : null; } } @@ -105,9 +155,12 @@ public abstract class AlignmentStrategy { public static class AlignmentPerTypeStrategy extends AlignmentStrategy { private final Map myAlignments = new HashMap(); - private final boolean myAllowBackwardShift; + + private final IElementType myParentType; + private final boolean myAllowBackwardShift; - AlignmentPerTypeStrategy(Collection targetElementTypes, boolean allowBackwardShift) { + AlignmentPerTypeStrategy(Collection targetElementTypes, IElementType parentType, boolean allowBackwardShift) { + myParentType = parentType; myAllowBackwardShift = allowBackwardShift; for (IElementType elementType : targetElementTypes) { myAlignments.put(elementType, Alignment.createAlignment(myAllowBackwardShift)); @@ -115,8 +168,11 @@ public abstract class AlignmentStrategy { } @Override - public Alignment getAlignment(IElementType elementType) { - return myAlignments.get(elementType); + public Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType) { + if (myParentType != null && parentType != null && myParentType != parentType) { + return null; + } + return myAlignments.get(childType); } public void renewAlignment(IElementType elementType) { diff --git a/platform/lang-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java b/platform/lang-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java index 78bacea14ca2..7c73ac408f85 100644 --- a/platform/lang-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java +++ b/platform/lang-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java @@ -34,6 +34,7 @@ public abstract class AbstractBlock implements ASTBlock { protected final Alignment myAlignment; private List mySubBlocks; + private Boolean myIncomplete; protected AbstractBlock(@NotNull ASTNode node, @Nullable Wrap wrap, @Nullable Alignment alignment) { myNode = node; @@ -79,6 +80,7 @@ public abstract class AbstractBlock implements ASTBlock { return new ChildAttributes(getChildIndent(), getFirstChildAlignment()); } + @Nullable private Alignment getFirstChildAlignment() { List subBlocks = getSubBlocks(); for (final Block subBlock : subBlocks) { @@ -96,7 +98,10 @@ public abstract class AbstractBlock implements ASTBlock { } public boolean isIncomplete() { - return FormatterUtil.isIncompleted(getNode()); + if (myIncomplete == null) { + myIncomplete = FormatterUtil.isIncompleted(getNode()); + } + return myIncomplete; } @Override