diff --git a/json/src/com/intellij/json/formatter/JsonBlock.java b/json/src/com/intellij/json/formatter/JsonBlock.java index 174e1607f010..0557f7804f37 100644 --- a/json/src/com/intellij/json/formatter/JsonBlock.java +++ b/json/src/com/intellij/json/formatter/JsonBlock.java @@ -21,7 +21,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import static com.intellij.json.JsonElementTypes.*; -import static com.intellij.json.JsonParserDefinition.*; +import static com.intellij.json.JsonParserDefinition.JSON_CONTAINERS; import static com.intellij.json.formatter.JsonCodeStyleSettings.ALIGN_PROPERTY_ON_COLON; import static com.intellij.json.formatter.JsonCodeStyleSettings.ALIGN_PROPERTY_ON_VALUE; import static com.intellij.json.psi.JsonPsiUtil.hasElementType; @@ -165,24 +165,6 @@ public class JsonBlock implements ASTBlock { @Nullable @Override public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) { - final CommonCodeStyleSettings commonSettings = getCommonSettings(); - final ASTNode leftChild = child1 instanceof JsonBlock ? ((JsonBlock)child1).myNode : null; - final ASTNode rightChild = child2 instanceof JsonBlock ? ((JsonBlock)child2).myNode : null; - // This causes braces/brackets to be on their own lines if whole object/array spans several lines. - if (leftChild != null && rightChild != null) { - if (hasElementType(leftChild, JSON_BRACES) ^ hasElementType(rightChild, JSON_BRACES)) { - final int numSpaces = commonSettings.SPACE_WITHIN_BRACES ? 1 : 0; - return Spacing.createDependentLFSpacing(numSpaces, numSpaces, myNode.getTextRange(), - commonSettings.KEEP_LINE_BREAKS, - commonSettings.KEEP_BLANK_LINES_IN_CODE); - } - else if (hasElementType(leftChild, JSON_BRACKETS) ^ hasElementType(rightChild, JSON_BRACKETS)) { - final int numSpaces = commonSettings.SPACE_WITHIN_BRACKETS ? 1 : 0; - return Spacing.createDependentLFSpacing(numSpaces, numSpaces, myNode.getTextRange(), - commonSettings.KEEP_LINE_BREAKS, - commonSettings.KEEP_BLANK_LINES_IN_CODE); - } - } return mySpacingBuilder.getSpacing(this, child1, child2); } diff --git a/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java b/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java index 45faae8bec56..5f96cb822195 100644 --- a/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java +++ b/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java @@ -41,8 +41,8 @@ public class JsonFormattingBuilderModel implements FormattingModelBuilder { return new SpacingBuilder(settings, JsonLanguage.INSTANCE) .before(COLON).spacing(spacesBeforeColon, spacesBeforeColon, 0, false, 0) .after(COLON).spacing(spacesAfterColon, spacesAfterColon, 0, false, 0) - .withinPair(L_BRACKET, R_BRACKET).spaceIf(commonSettings.SPACE_WITHIN_BRACKETS) - .withinPair(L_CURLY, R_CURLY).spaceIf(commonSettings.SPACE_WITHIN_BRACES) + .withinPair(L_BRACKET, R_BRACKET).spaceIf(commonSettings.SPACE_WITHIN_BRACKETS, true) + .withinPair(L_CURLY, R_CURLY).spaceIf(commonSettings.SPACE_WITHIN_BRACES, true) .before(COMMA).spacing(spacesBeforeComma, spacesBeforeComma, 0, false, 0) .after(COMMA).spaceIf(commonSettings.SPACE_AFTER_COMMA); } diff --git a/platform/lang-api/src/com/intellij/formatting/SpacingBuilder.java b/platform/lang-api/src/com/intellij/formatting/SpacingBuilder.java index d773109b6663..182b9727c381 100644 --- a/platform/lang-api/src/com/intellij/formatting/SpacingBuilder.java +++ b/platform/lang-api/src/com/intellij/formatting/SpacingBuilder.java @@ -1,6 +1,7 @@ package com.intellij.formatting; import com.intellij.lang.Language; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.tree.IElementType; @@ -16,19 +17,15 @@ import java.util.List; */ public class SpacingBuilder { private static class SpacingRule { - private final TokenSet myParentType; - private final TokenSet myChild1Type; - private final TokenSet myChild2Type; - private final int myMinSpaces; - private final int myMaxSpaces; - private final int myMinLF; - private final boolean myKeepLineBreaks; - private final int myKeepBlankLines; + protected final RuleCondition myRuleCondition; + protected final int myMinSpaces; + protected final int myMaxSpaces; + protected final int myMinLF; + protected final boolean myKeepLineBreaks; + protected final int myKeepBlankLines; - private SpacingRule(RuleCondition condition, int minSpaces, int maxSpaces, int minLF, boolean keepLineBreaks, int keepBlankLines) { - myParentType = condition.myParentType; - myChild1Type = condition.myChild1Type; - myChild2Type = condition.myChild2Type; + private SpacingRule(@NotNull RuleCondition condition, int minSpaces, int maxSpaces, int minLF, boolean keepLineBreaks, int keepBlankLines) { + myRuleCondition = condition; myMinSpaces = minSpaces; myMaxSpaces = maxSpaces; myMinLF = minLF; @@ -36,17 +33,33 @@ public class SpacingBuilder { myKeepBlankLines = keepBlankLines; } - public boolean matches(IElementType parentType, IElementType child1Type, IElementType child2Type) { - return ((myParentType == null || myParentType.contains(parentType)) && - (myChild1Type == null || myChild1Type.contains(child1Type)) && - (myChild2Type == null || myChild2Type.contains(child2Type))); + public boolean matches(@NotNull ASTBlock parentBlock, @NotNull ASTBlock childBlock1, @NotNull ASTBlock childBlock2) { + return myRuleCondition.matches(parentBlock.getNode().getElementType(), + childBlock1.getNode().getElementType(), + childBlock2.getNode().getElementType()); } - public Spacing createSpacing() { + public Spacing createSpacing(@NotNull ASTBlock parentBlock, @NotNull ASTBlock childBlock1, @NotNull ASTBlock childBlock2) { return Spacing.createSpacing(myMinSpaces, myMaxSpaces, myMinLF, myKeepLineBreaks, myKeepBlankLines); } } + private static class DependentLFSpacingRule extends SpacingRule { + public DependentLFSpacingRule(@NotNull RuleCondition condition, + int minSpaces, + int maxSpaces, + boolean keepLineBreaks, + int keepBlankLines) { + super(condition, minSpaces, maxSpaces, 1, keepLineBreaks, keepBlankLines); + } + + @Override + public Spacing createSpacing(@NotNull ASTBlock parentBlock, @NotNull ASTBlock childBlock1, @NotNull ASTBlock childBlock2) { + final TextRange range = parentBlock.getNode().getTextRange(); + return Spacing.createDependentLFSpacing(myMinSpaces, myMaxSpaces, range, myKeepLineBreaks, myKeepBlankLines); + } + } + private static class RuleCondition { private final TokenSet myParentType; private final TokenSet myChild1Type; @@ -57,6 +70,12 @@ public class SpacingBuilder { myChild1Type = child1Type; myChild2Type = child2Type; } + + private boolean matches(@NotNull IElementType parentType, @NotNull IElementType firstChildType, @NotNull IElementType secondChildType) { + return ((myParentType == null || myParentType.contains(parentType)) && + (myChild1Type == null || myChild1Type.contains(firstChildType)) && + (myChild2Type == null || myChild2Type.contains(secondChildType))); + } } public class RuleBuilder { @@ -71,15 +90,36 @@ public class SpacingBuilder { } public SpacingBuilder spaceIf(boolean option) { - return spaces(option ? 1 : 0); + return spaceIf(option, false); } - public SpacingBuilder spaces(final int count) { - for (RuleCondition condition : myConditions) { - myRules.add(new SpacingRule(condition, count, count, 0, - myCodeStyleSettings.KEEP_LINE_BREAKS, myCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE)); + /** + * If {@code useParentDependentLFSpacing} is true and parent block spans multiple lines, insert single line break. + * Otherwise insert whitespace block with exactly one or no spaces depending on value of {@code option} parameter. + * + * @see Spacing#createDependentLFSpacing + */ + public SpacingBuilder spaceIf(boolean option, boolean useParentDependentLFSpacing) { + return spaces(option ? 1 : 0, useParentDependentLFSpacing); + } + + public SpacingBuilder spaces(int count) { + return spaces(count, false); + } + + /** + * If {@code useParentDependentLFSpacing} is true and parent block spans multiple lines, insert single line break. + * Otherwise insert whitespace block that contains as many spaces as specified via {@code count} parameter. + * + * @see Spacing#createDependentLFSpacing + */ + public SpacingBuilder spaces(int count, boolean useParentDependentLFSpacing) { + if (useParentDependentLFSpacing) { + return parentDependentLFSpacing(count, count, myCodeStyleSettings.KEEP_LINE_BREAKS, myCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE); + } + else { + return spacing(count, count, 0, myCodeStyleSettings.KEEP_LINE_BREAKS, myCodeStyleSettings.KEEP_BLANK_LINES_IN_CODE); } - return SpacingBuilder.this; } public SpacingBuilder blankLines(int count) { @@ -118,6 +158,18 @@ public class SpacingBuilder { } return SpacingBuilder.this; } + + /** + * Similar to {@link #spacing} but replaced by single line break, if parent block spans multiple lines. + * + * @see Spacing#createDependentLFSpacing + */ + public SpacingBuilder parentDependentLFSpacing(int minSpaces, int maxSpaces, boolean keepLineBreaks, int keepBlankLines) { + for (RuleCondition condition : myConditions) { + myRules.add(new DependentLFSpacingRule(condition, minSpaces, maxSpaces, keepLineBreaks, keepBlankLines)); + } + return SpacingBuilder.this; + } } private final CommonCodeStyleSettings myCodeStyleSettings; @@ -271,12 +323,9 @@ public class SpacingBuilder { if (!(parent instanceof ASTBlock) || !(child1 instanceof ASTBlock) || !(child2 instanceof ASTBlock)) { return null; } - IElementType parentType = ((ASTBlock) parent).getNode().getElementType(); - IElementType child1Type = ((ASTBlock) child1).getNode().getElementType(); - IElementType child2Type = ((ASTBlock) child2).getNode().getElementType(); for (SpacingRule rule : myRules) { - if (rule.matches(parentType, child1Type, child2Type)) { - return rule.createSpacing(); + if (rule.matches((ASTBlock)parent, (ASTBlock)child1, (ASTBlock) child2)) { + return rule.createSpacing((ASTBlock)parent, (ASTBlock)child1, (ASTBlock) child2); } } return null; diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 3941f0a83f80..0aef3d332cb7 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -684,12 +684,10 @@ public class PyBlock implements ASTBlock { public Spacing getSpacing(Block child1, @NotNull Block child2) { if (child1 instanceof ASTBlock && child2 instanceof ASTBlock) { final ASTNode node1 = ((ASTBlock)child1).getNode(); - final ASTNode node2 = ((ASTBlock)child2).getNode(); final PsiElement psi1 = node1.getPsi(); final PsiElement psi2 = ((ASTBlock)child2).getNode().getPsi(); final IElementType childType1 = node1.getElementType(); - final IElementType childType2 = node2.getElementType(); if (psi1 instanceof PyImportStatementBase && psi2 instanceof PyImportStatementBase && psi2.getCopyableUserData(IMPORT_GROUP_BEGIN) != null) { return Spacing.createSpacing(0, 0, 2, true, 1); @@ -718,14 +716,6 @@ public class PyBlock implements ASTBlock { } } - if (myNode.getElementType() == PyElementTypes.DICT_LITERAL_EXPRESSION) { - if (shouldInsertNewLineForBrace(childType1) || shouldInsertNewLineForBrace(childType2)) { - final int spaces = settings.SPACE_WITHIN_BRACES ? 1 : 0; - return Spacing.createDependentLFSpacing(spaces, spaces, myNode.getTextRange(), - settings.KEEP_LINE_BREAKS, settings.KEEP_BLANK_LINES_IN_CODE); - } - } - if (psi2 instanceof PsiComment && !hasLineBreaksBefore(psi2.getNode(), 1) && myContext.getPySettings().SPACE_BEFORE_NUMBER_SIGN) { return Spacing.createSpacing(2, 0, 0, false, 0); } @@ -733,11 +723,6 @@ public class PyBlock implements ASTBlock { return myContext.getSpacingBuilder().getSpacing(this, child1, child2); } - private boolean shouldInsertNewLineForBrace(IElementType type) { - return (type == PyTokenTypes.LBRACE && myContext.getPySettings().DICT_NEW_LINE_AFTER_LEFT_BRACE) || - (type == PyTokenTypes.RBRACE && myContext.getPySettings().DICT_NEW_LINE_BEFORE_RIGHT_BRACE); - } - private Spacing getBlankLinesForOption(final int option) { final int blankLines = option + 1; return Spacing diff --git a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java index 0521bf4b3510..0f94cc79edea 100644 --- a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java +++ b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java @@ -119,6 +119,8 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, C .withinPairInside(LPAR, RPAR, PARENTHESIZED_EXPRESSION).spaces(0) .before(LBRACKET).spaceIf(pySettings.SPACE_BEFORE_LBRACKET) + .afterInside(LBRACE, DICT_LITERAL_EXPRESSION).spaceIf(pySettings.SPACE_WITHIN_BRACES, pySettings.DICT_NEW_LINE_AFTER_LEFT_BRACE) + .beforeInside(RBRACE, DICT_LITERAL_EXPRESSION).spaceIf(pySettings.SPACE_WITHIN_BRACES, pySettings.DICT_NEW_LINE_BEFORE_RIGHT_BRACE) .withinPair(LBRACE, RBRACE).spaceIf(pySettings.SPACE_WITHIN_BRACES) .withinPair(LBRACKET, RBRACKET).spaceIf(commonSettings.SPACE_WITHIN_BRACKETS)