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 c0e402bdb815..585eee3ab9c2 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 @@ -1266,7 +1266,8 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo */ protected boolean shouldUseVarDeclarationAlignment(ASTNode node) { return mySettings.ALIGN_GROUP_FIELD_DECLARATIONS && ALIGN_IN_COLUMNS_ELEMENT_TYPES.contains(node.getElementType()) - && !myAlignmentInColumnsHelper.useDifferentVarDeclarationAlignment(node, ALIGNMENT_IN_COLUMNS_CONFIG); + && !myAlignmentInColumnsHelper.useDifferentVarDeclarationAlignment(node, ALIGNMENT_IN_COLUMNS_CONFIG, + mySettings.KEEP_BLANK_LINES_IN_DECLARATIONS); } private SyntheticCodeBlock createCodeBlockBlock(final ArrayList localResult, final Indent indent, final int childrenIndent) { diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java index ba8e1c9cf243..95dfa3046e58 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java @@ -19,7 +19,6 @@ import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.codeStyle.CustomCodeStyleSettings; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.treeStructure.treetable.ListTreeTableModel; @@ -104,6 +103,9 @@ public abstract class OptionTableWithPreviewPanel extends MultilanguageCodeStyle for (Option each : myOptions) { each.setEnabled(true); } + for (Option each : myCustomOptions) { + each.setEnabled(false); + } } @Override @@ -117,6 +119,9 @@ public abstract class OptionTableWithPreviewPanel extends MultilanguageCodeStyle } } } + for (Option each : myCustomOptions) { + each.setEnabled(false); + } } @Override @@ -138,7 +143,6 @@ public abstract class OptionTableWithPreviewPanel extends MultilanguageCodeStyle } else { for (Option each : myCustomOptions) { - each.setEnabled(false); if (each.clazz == settingsClass && each.field.getName().equals(fieldName)) { each.setEnabled(true); } diff --git a/platform/lang-impl/src/com/intellij/formatting/alignment/AlignmentInColumnsHelper.java b/platform/lang-impl/src/com/intellij/formatting/alignment/AlignmentInColumnsHelper.java index 58909a0dbf19..808906ba89c3 100644 --- a/platform/lang-impl/src/com/intellij/formatting/alignment/AlignmentInColumnsHelper.java +++ b/platform/lang-impl/src/com/intellij/formatting/alignment/AlignmentInColumnsHelper.java @@ -16,11 +16,16 @@ package com.intellij.formatting.alignment; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.impl.source.tree.TreeUtil; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; +import com.intellij.util.SmartList; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * This class provides helper methods to use for 'align in columns' processing. *

@@ -51,13 +56,14 @@ public class AlignmentInColumnsHelper { * Allows to answer if given node should be aligned to the previous node of the same type according to the given alignment config * assuming that given node is a variable declaration. * - * @param node target node which alignment strategy is to be defined - * @param config alignment config to use for processing + * @param node target node which alignment strategy is to be defined + * @param config alignment config to use for processing + * @param blankLinesToBeKeptOnReformat corresponding KEEP_LINE_IN_* formatting setting * @return true if given node should be aligned to the previous one; false otherwise */ @SuppressWarnings({"MethodMayBeStatic"}) - public boolean useDifferentVarDeclarationAlignment(ASTNode node, AlignmentInColumnsConfig config) { - ASTNode prev = getPreviousAdjacentNodeOfTargetType(node, config); + public boolean useDifferentVarDeclarationAlignment(ASTNode node, AlignmentInColumnsConfig config, int blankLinesToBeKeptOnReformat) { + ASTNode prev = getPreviousAdjacentNodeOfTargetType(node, config, blankLinesToBeKeptOnReformat); if (prev == null) { return true; } @@ -121,24 +127,27 @@ public class AlignmentInColumnsHelper { } } - boolean prevContainsDefinition = containsSubNodeOfType(prev, config.getDistinguishableTypes()); - boolean currentContainsDefinition = containsSubNodeOfType(currentFieldToUse, config.getDistinguishableTypes()); + List prevTypes = findSubNodeTypes(prev, config.getDistinguishableTypes()); + List currTypes = findSubNodeTypes(currentFieldToUse, config.getDistinguishableTypes()); - return prevContainsDefinition ^ currentContainsDefinition; + return !prevTypes.equals(currTypes); } /** * Tries to find previous node adjacent to the given node that has the same * {@link AlignmentInColumnsConfig#getTargetDeclarationTypes() target type}. * - * @param baseNode base node to use - * @param config current processing config + * @param baseNode base node to use + * @param config current processing config + * @param blankLinesToBeKeptOnReformat * @return previous node to the given base node that has that same type and is adjacent to it if possible; * null otherwise */ @SuppressWarnings({"StatementWithEmptyBody"}) @Nullable - private static ASTNode getPreviousAdjacentNodeOfTargetType(ASTNode baseNode, AlignmentInColumnsConfig config) { + private static ASTNode getPreviousAdjacentNodeOfTargetType(ASTNode baseNode, + AlignmentInColumnsConfig config, + final double blankLinesToBeKeptOnReformat) { ASTNode nodeOfTargetType = deriveNodeOfTargetType(baseNode, config.getTargetDeclarationTypes()); if (nodeOfTargetType == null) { return null; @@ -154,11 +163,20 @@ public class AlignmentInColumnsHelper { @Override public boolean whitespaceFound(ASTNode node) { - return StringUtil.countChars(node.getText(), '\n') > 1; + return blankLinesToBeKeptOnReformat > 0 && StringUtil.countChars(node.getText(), '\n') > 1; } }); if (prev[0] == null) return null; + // ensure there are no non-whitespace, non-comment elements on the top level between baseNode and the found one + Pair siblingParents = TreeUtil.findTopmostSiblingParents(prev[0], baseNode); + if (siblingParents.first != null && siblingParents.second != null) { + for (ASTNode each = siblingParents.second.getTreePrev(); each != null && each != siblingParents.first; each = each.getTreePrev()) { + IElementType eachType = each.getElementType(); + if (!config.getCommentTokenTypes().contains(eachType) && !config.getWhiteSpaceTokenTypes().contains(eachType)) return null; + } + } + return deriveNodeOfTargetType(prev[0], TokenSet.create(nodeOfTargetType.getElementType())); } @@ -217,26 +235,6 @@ public class AlignmentInColumnsHelper { * * @return direct or indirect previous node of the given one having target type if possible; null otherwise */ - //@Nullable - //private static ASTNode getPreviousNode(ASTNode startNode, IElementType targetType) { - // // Try to find node that is direct previous node of the target node or is parent of the node that is indirect previous node - // // of the target node. - // ASTNode prev = startNode.getTreePrev(); - // for (ASTNode node = startNode; prev == null && node != null; node = node.getTreeParent()) { - // prev = node.getTreePrev(); - // } - // - // ASTNode result = prev; - // - // // Find the rightest child of the target previous parent node if necessary. - // if (prev != null) { - // for (; prev != null && prev.getElementType() != targetType; prev = prev.getLastChildNode()) { - // result = prev; - // } - // } - // - // return result; - //} private static boolean findPreviousNode(AlignmentInColumnsConfig config, ASTNode from, IElementType targetType, @@ -246,8 +244,6 @@ public class AlignmentInColumnsHelper { if (from == null) return false; for (ASTNode prev = processFrom ? from : from.getTreePrev(); prev != null; prev = prev.getTreePrev()) { - // todo stop processing on exiting from the scope - IElementType prevType = prev.getElementType(); if (prevType == targetType) { if (processor.targetTypeFound(prev)) return true; @@ -259,7 +255,12 @@ public class AlignmentInColumnsHelper { if (findPreviousNode(config, prev.getLastChildNode(), targetType, true, false, processor)) return true; } - return processParent ? findPreviousNode(config, from.getTreeParent(), targetType, false, processParent, processor) : false; + if (processParent) { + for (ASTNode parent = from.getTreeParent(); parent != null; parent = parent.getTreeParent()) { + if (findPreviousNode(config, parent, targetType, false, false, processor)) return true; + } + } + return false; } private static abstract class NodeProcessor { @@ -326,19 +327,14 @@ public class AlignmentInColumnsHelper { return null; } - /** - * Allows to check if given node contains direct child (level one depth) of the given type. - * - * @param node parent node to check - * @param type target child node type - * @return true if given parent node contains direct child of the given type; false otherwise - */ - private static boolean containsSubNodeOfType(ASTNode node, TokenSet types) { + private static List findSubNodeTypes(ASTNode node, TokenSet types) { + List foundTypes = new SmartList(); for (ASTNode child = node.getFirstChildNode(); child != null && child.getTreeParent() == node; child = child.getTreeNext()) { - if (types.contains(child.getElementType())) { - return true; + IElementType type = child.getElementType(); + if (types.contains(type)) { + foundTypes.add(type); } } - return false; + return foundTypes; } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/TreeUtil.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/TreeUtil.java index 864edb0baca9..66a5cb00c713 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/TreeUtil.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/TreeUtil.java @@ -20,6 +20,7 @@ import com.intellij.lang.ASTNode; import com.intellij.lexer.Lexer; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; import com.intellij.psi.impl.DebugUtil; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IStrongWhitespaceHolderElementType; @@ -28,6 +29,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashSet; +import java.util.LinkedList; import java.util.Set; public class TreeUtil { @@ -53,10 +55,10 @@ public class TreeUtil { @Nullable public static ASTNode findChildBackward(ASTNode parent, IElementType type) { - if (DebugUtil.CHECK_INSIDE_ATOMIC_ACTION_ENABLED){ + if (DebugUtil.CHECK_INSIDE_ATOMIC_ACTION_ENABLED) { ApplicationManager.getApplication().assertReadAccessAllowed(); } - for(ASTNode element = parent.getLastChildNode(); element != null; element = element.getTreePrev()){ + for (ASTNode element = parent.getLastChildNode(); element != null; element = element.getTreePrev()) { if (element.getElementType() == type) return element; } return null; @@ -64,7 +66,7 @@ public class TreeUtil { @Nullable public static ASTNode skipElements(ASTNode element, TokenSet types) { - while(true){ + while (true) { if (element == null) return null; if (!types.contains(element.getElementType())) break; element = element.getTreeNext(); @@ -86,7 +88,7 @@ public class TreeUtil { if (prev == null) return null; ASTNode firstChildNode = parent.getFirstChildNode(); ASTNode lastRelevant = null; - while(firstChildNode != prev){ + while (firstChildNode != prev) { if (!types.contains(firstChildNode.getElementType())) lastRelevant = firstChildNode; firstChildNode = firstChildNode.getTreeNext(); } @@ -95,7 +97,7 @@ public class TreeUtil { @Nullable public static ASTNode findParent(ASTNode element, IElementType type) { - for(ASTNode parent = element.getTreeParent(); parent != null; parent = parent.getTreeParent()){ + for (ASTNode parent = element.getTreeParent(); parent != null; parent = parent.getTreeParent()) { if (parent.getElementType() == type) return parent; } return null; @@ -103,11 +105,11 @@ public class TreeUtil { @Nullable public static LeafElement findFirstLeaf(ASTNode element) { - if (element instanceof LeafElement){ + if (element instanceof LeafElement) { return (LeafElement)element; } - else{ - for(ASTNode child = element.getFirstChildNode(); child != null; child = child.getTreeNext()){ + else { + for (ASTNode child = element.getFirstChildNode(); child != null; child = child.getTreeNext()) { LeafElement leaf = findFirstLeaf(child); if (leaf != null) return leaf; } @@ -126,7 +128,7 @@ public class TreeUtil { return element; } else { - for(TreeElement child = element.getFirstChildNode(); child != null; child = child.getTreeNext()){ + for (TreeElement child = element.getFirstChildNode(); child != null; child = child.getTreeNext()) { TreeElement leaf = findFirstLeafOrChameleon(child); if (leaf != null) return leaf; } @@ -136,10 +138,10 @@ public class TreeUtil { @Nullable public static LeafElement findLastLeaf(ASTNode element) { - if (element instanceof LeafElement){ + if (element instanceof LeafElement) { return (LeafElement)element; } - for(ASTNode child = element.getLastChildNode(); child != null; child = child.getTreePrev()){ + for (ASTNode child = element.getLastChildNode(); child != null; child = child.getTreePrev()) { LeafElement leaf = findLastLeaf(child); if (leaf != null) return leaf; } @@ -157,25 +159,48 @@ public class TreeUtil { } @Nullable - public static ASTNode findCommonParent(ASTNode one, ASTNode two){ + public static ASTNode findCommonParent(ASTNode one, ASTNode two) { // optimization - if(one == two) return one; + if (one == two) return one; final Set parents = new HashSet(20); while (one != null) { parents.add(one); one = one.getTreeParent(); } - while(two != null){ - if(parents.contains(two)) return two; + while (two != null) { + if (parents.contains(two)) return two; two = two.getTreeParent(); } return null; } + public static Pair findTopmostSiblingParents(ASTNode one, ASTNode two) { + if (one == two) return (Pair)Pair.create(null, null); + + LinkedList oneParents = new LinkedList(); + LinkedList twoParents = new LinkedList(); + while (one != null) { + oneParents.add(one); + one = one.getTreeParent(); + } + while (two != null) { + twoParents.add(two); + two = two.getTreeParent(); + } + + do { + one = oneParents.pollLast(); + two = twoParents.pollLast(); + } + while (one == two && one != null); + + return new Pair(one, two); + } + public static void clearCaches(TreeElement tree) { tree.clearCaches(); TreeElement child = tree.getFirstChildNode(); - while(child != null){ + while (child != null) { clearCaches(child); child = child.getTreeNext(); } @@ -187,7 +212,7 @@ public class TreeUtil { } public static FileElement getFileElement(TreeElement parent) { - while(parent != null && !(parent instanceof FileElement)) { + while (parent != null && !(parent instanceof FileElement)) { parent = parent.getTreeParent(); } return (FileElement)parent; @@ -212,7 +237,10 @@ public class TreeUtil { } @Nullable - public static TreeElement nextLeaf(@NotNull TreeElement start, CommonParentState commonParent, IElementType searchedType,boolean expandChameleons) { + public static TreeElement nextLeaf(@NotNull TreeElement start, + CommonParentState commonParent, + IElementType searchedType, + boolean expandChameleons) { TreeElement element = start; while (element != null) { if (commonParent != null) { @@ -245,7 +273,10 @@ public class TreeUtil { } @Nullable - private static TreeElement findFirstLeafOrType(@NotNull TreeElement element, final IElementType searchedType, final CommonParentState commonParent,final boolean expandChameleons) { + private static TreeElement findFirstLeafOrType(@NotNull TreeElement element, + final IElementType searchedType, + final CommonParentState commonParent, + final boolean expandChameleons) { final TreeElement[] result = {null}; element.acceptTree(new RecursiveTreeElementWalkingVisitor(expandChameleons) { @Override