mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Platform SpacingBuilder supports simple dependentLFSpacing dependent on parent block
This type of dependent line feed spacing is often used to implement code style settings like "New line after '['" and "Put ']' on new line" for collection literals and similar elements in various languages.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user