/* * Copyright 2013-2017 Grzegorz Ligas <ligasgr@gmail.com> and other contributors * (see the CONTRIBUTORS file). * * 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 org.intellij.xquery.formatter; import com.intellij.formatting.Alignment; import com.intellij.formatting.Block; import com.intellij.formatting.ChildAttributes; import com.intellij.formatting.Indent; import com.intellij.formatting.Spacing; import com.intellij.formatting.SpacingBuilder; import com.intellij.formatting.Wrap; import com.intellij.formatting.WrapType; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.TokenType; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.formatter.common.AbstractBlock; import com.intellij.psi.impl.source.tree.LeafPsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.util.containers.ContainerUtil; import org.intellij.xquery.psi.XQueryBasicTypes; import org.intellij.xquery.psi.XQueryExprSingle; import org.intellij.xquery.psi.XQueryFunctionName; import org.intellij.xquery.psi.XQueryNamespacePrefix; import org.intellij.xquery.psi.XQueryTokenType; import org.intellij.xquery.psi.XQueryVarName; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Set; import static org.intellij.xquery.psi.XQueryTypes.ADDITIVE_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.AND_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.ARGUMENT_LIST; import static org.intellij.xquery.psi.XQueryTypes.CASE_CLAUSE; import static org.intellij.xquery.psi.XQueryTypes.CASTABLE_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.CAST_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.CATCH_CLAUSE; import static org.intellij.xquery.psi.XQueryTypes.CATCH_CLAUSE_EXPRESSION; import static org.intellij.xquery.psi.XQueryTypes.ENCLOSED_CONTENT_EXPRESSION; import static org.intellij.xquery.psi.XQueryTypes.DIR_ELEM_CONTENT; import static org.intellij.xquery.psi.XQueryTypes.ENCLOSED_EXPRESSION; import static org.intellij.xquery.psi.XQueryTypes.EQ; import static org.intellij.xquery.psi.XQueryTypes.EQUAL; import static org.intellij.xquery.psi.XQueryTypes.EQUALITY_COMP; import static org.intellij.xquery.psi.XQueryTypes.EXCLAMATION_MARK; import static org.intellij.xquery.psi.XQueryTypes.EXPR; import static org.intellij.xquery.psi.XQueryTypes.FOR_BINDING; import static org.intellij.xquery.psi.XQueryTypes.FUNCTION_DECL; import static org.intellij.xquery.psi.XQueryTypes.GE; import static org.intellij.xquery.psi.XQueryTypes.GE_CHARS; import static org.intellij.xquery.psi.XQueryTypes.GT; import static org.intellij.xquery.psi.XQueryTypes.GT_CHAR; import static org.intellij.xquery.psi.XQueryTypes.IF_EXPR; import static org.intellij.xquery.psi.XQueryTypes.INSTANCE_OF_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.INTERSECT_EXCEPT_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.K_AND; import static org.intellij.xquery.psi.XQueryTypes.K_AS; import static org.intellij.xquery.psi.XQueryTypes.K_CAST; import static org.intellij.xquery.psi.XQueryTypes.K_CASTABLE; import static org.intellij.xquery.psi.XQueryTypes.K_DIV; import static org.intellij.xquery.psi.XQueryTypes.K_EXCEPT; import static org.intellij.xquery.psi.XQueryTypes.K_IDIV; import static org.intellij.xquery.psi.XQueryTypes.K_INSTANCE; import static org.intellij.xquery.psi.XQueryTypes.K_INTERSECT; import static org.intellij.xquery.psi.XQueryTypes.K_IS; import static org.intellij.xquery.psi.XQueryTypes.K_MOD; import static org.intellij.xquery.psi.XQueryTypes.K_OF; import static org.intellij.xquery.psi.XQueryTypes.K_OR; import static org.intellij.xquery.psi.XQueryTypes.K_TO; import static org.intellij.xquery.psi.XQueryTypes.K_TREAT; import static org.intellij.xquery.psi.XQueryTypes.K_UNION; import static org.intellij.xquery.psi.XQueryTypes.LE; import static org.intellij.xquery.psi.XQueryTypes.LET_BINDING; import static org.intellij.xquery.psi.XQueryTypes.LE_CHARS; import static org.intellij.xquery.psi.XQueryTypes.LT; import static org.intellij.xquery.psi.XQueryTypes.LT_CHAR; import static org.intellij.xquery.psi.XQueryTypes.L_C_BRACE; import static org.intellij.xquery.psi.XQueryTypes.L_PAR; import static org.intellij.xquery.psi.XQueryTypes.MULTIPLICATIVE_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.NE; import static org.intellij.xquery.psi.XQueryTypes.NODECOMP_GT; import static org.intellij.xquery.psi.XQueryTypes.NODECOMP_LT; import static org.intellij.xquery.psi.XQueryTypes.NODE_COMP; import static org.intellij.xquery.psi.XQueryTypes.NOT_EQUAL; import static org.intellij.xquery.psi.XQueryTypes.OP_ASSIGN; import static org.intellij.xquery.psi.XQueryTypes.OP_MINUS; import static org.intellij.xquery.psi.XQueryTypes.OP_PLUS; import static org.intellij.xquery.psi.XQueryTypes.ORDER_SPEC; import static org.intellij.xquery.psi.XQueryTypes.OR_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.PARAM_LIST; import static org.intellij.xquery.psi.XQueryTypes.PARENTHESIZED_EXPR; import static org.intellij.xquery.psi.XQueryTypes.PIPE; import static org.intellij.xquery.psi.XQueryTypes.PIPE_PIPE; import static org.intellij.xquery.psi.XQueryTypes.RELATIONAL_COMP; import static org.intellij.xquery.psi.XQueryTypes.RELATIVE_PATH_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.RETURN_CLAUSE; import static org.intellij.xquery.psi.XQueryTypes.R_PAR; import static org.intellij.xquery.psi.XQueryTypes.SLASH; import static org.intellij.xquery.psi.XQueryTypes.SLASH_SLASH; import static org.intellij.xquery.psi.XQueryTypes.STAR_SIGN; import static org.intellij.xquery.psi.XQueryTypes.STEP_EXPR; import static org.intellij.xquery.psi.XQueryTypes.SWITCH_CASE_CLAUSE; import static org.intellij.xquery.psi.XQueryTypes.SWITCH_DEFAULT_RETURN_CLAUSE; import static org.intellij.xquery.psi.XQueryTypes.SWITCH_RETURN_CLAUSE; import static org.intellij.xquery.psi.XQueryTypes.TO_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.TREAT_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.TRY_CLAUSE; import static org.intellij.xquery.psi.XQueryTypes.TYPESWITCH_DEFAULT_RETURN_CLAUSE; import static org.intellij.xquery.psi.XQueryTypes.UNION_OPERATOR; import static org.intellij.xquery.psi.XQueryTypes.VALUE_COMP; import static org.intellij.xquery.psi.XQueryTypes.VAR_VALUE; import static org.intellij.xquery.psi.XQueryTypes.WHERE_CLAUSE; public class XQueryFormattingBlock extends AbstractBlock { private static final Set<IElementType> BIN_OPERATORS = ContainerUtil.set( K_OR, K_AND, PIPE_PIPE, K_TO, OP_PLUS, OP_MINUS, STAR_SIGN, K_DIV, K_IDIV, K_MOD, K_UNION, PIPE, K_INTERSECT, K_EXCEPT, K_INSTANCE, K_OF, K_TREAT, K_AS, K_CASTABLE, K_CAST, EQ, NE, LT, LE, GT, GE, EQUAL, NOT_EQUAL, LT_CHAR, LE_CHARS, GT_CHAR, GE_CHARS, K_IS, NODECOMP_LT, NODECOMP_GT, EXCLAMATION_MARK, AND_OPERATOR, OR_OPERATOR, TO_OPERATOR, MULTIPLICATIVE_OPERATOR, ADDITIVE_OPERATOR, UNION_OPERATOR, INTERSECT_EXCEPT_OPERATOR, INSTANCE_OF_OPERATOR, TREAT_OPERATOR, CASTABLE_OPERATOR, CAST_OPERATOR, EQUALITY_COMP, RELATIONAL_COMP, VALUE_COMP, NODE_COMP ); private final SpacingBuilder spacingBuilder; private final CommonCodeStyleSettings settings; public XQueryFormattingBlock(@NotNull ASTNode node, @Nullable Wrap wrap, @Nullable Alignment alignment, @NotNull CommonCodeStyleSettings settings, @NotNull SpacingBuilder spacingBuilder) { super(node, wrap, alignment); this.spacingBuilder = spacingBuilder; this.settings = settings; } @Override protected List<Block> buildChildren() { List<Block> blocks = new ArrayList<Block>(); ASTNode child = myNode.getFirstChildNode(); while (child != null) { if (child.getElementType() != TokenType.WHITE_SPACE && child.getTextRange().getLength() != 0) { Block block = new XQueryFormattingBlock(child, Wrap.createWrap(WrapType.NONE, false), null, settings, spacingBuilder); blocks.add(block); } child = child.getTreeNext(); } return blocks; } @Nullable @Override public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) { return spacingBuilder.getSpacing(this, child1, child2); } @Override public boolean isLeaf() { return myNode.getFirstChildNode() == null; } @Override public Indent getIndent() { IElementType type = myNode.getElementType(); ASTNode parent = myNode.getTreeParent(); IElementType parentType = parent != null ? parent.getElementType() : null; IElementType prevType = getTypeOfPreviousElement(myNode); if (parent == null) return Indent.getNoneIndent(); if (isExpressionAfterBrace(type, prevType) || isExpressionAfterParenthesis(type, prevType) || isXmlChild(type) || isForOrLetBinding(parentType) || isExprInsideOfEnclosedExpr(type, parentType)) return Indent.getNormalIndent(); if (isASingleExpression()) { if (parentType == IF_EXPR) { return Indent.getNormalIndent(); } if (parentType == WHERE_CLAUSE || parentType == RETURN_CLAUSE || parentType == VAR_VALUE || parentType == ORDER_SPEC || parentType == SWITCH_RETURN_CLAUSE) { return Indent.getNormalIndent(); } } if (type == SWITCH_RETURN_CLAUSE || type == SWITCH_DEFAULT_RETURN_CLAUSE || type == TYPESWITCH_DEFAULT_RETURN_CLAUSE || type == CASE_CLAUSE || type == SWITCH_CASE_CLAUSE) { return Indent.getNormalIndent(); } if (isParamOrArgumentList(parentType) && (type != L_PAR && type != R_PAR)) { return Indent.getContinuationIndent(); } if (isChildOfSingleExpression()) { if (BIN_OPERATORS.contains(type) || BIN_OPERATORS.contains(prevType)) { return Indent.getContinuationIndent(); } if (((type == SLASH || type == SLASH_SLASH || type == RELATIVE_PATH_OPERATOR) && (prevType == STEP_EXPR)) || ((type == STEP_EXPR) && (prevType == SLASH || prevType == SLASH_SLASH || prevType == RELATIVE_PATH_OPERATOR))) { return Indent.getContinuationIndent(); } } return Indent.getNoneIndent(); } private boolean isExprInsideOfEnclosedExpr(IElementType type, IElementType parentType) { return type == EXPR && parentType == ENCLOSED_EXPRESSION; } private boolean isExpressionAfterBrace(IElementType type, IElementType typeOfPreviousElement) { return (type == EXPR || type == ENCLOSED_CONTENT_EXPRESSION) && typeOfPreviousElement == L_C_BRACE; } private boolean isExpressionAfterParenthesis(IElementType type, IElementType typeOfPreviousElement) { return type == EXPR && typeOfPreviousElement == L_PAR; } @NotNull @Override public ChildAttributes getChildAttributes(int newChildIndex) { IElementType type = myNode.getElementType(); Indent childIndent = calculateChildIndent(type, false); if (childIndent == null && newChildIndex > 0) { IElementType calculatedType = getIElementType(newChildIndex); childIndent = calculateChildIndent(calculatedType, true); } return new ChildAttributes(childIndent != null ? childIndent : Indent.getNoneIndent(), null); } private Indent calculateChildIndent(IElementType type, boolean fromCalculatedType) { if (type == ENCLOSED_EXPRESSION || type == FUNCTION_DECL || (! fromCalculatedType && type == PARENTHESIZED_EXPR) || type == LET_BINDING || type == OP_ASSIGN || type == RETURN_CLAUSE || (! fromCalculatedType && type == TRY_CLAUSE) || (! fromCalculatedType && type == CATCH_CLAUSE) || type == CATCH_CLAUSE_EXPRESSION) return Indent.getNormalIndent(); return null; } private boolean isASingleExpression() { return myNode.getPsi() instanceof XQueryExprSingle; } private IElementType getTypeOfPreviousElement(ASTNode myNode) { ASTNode prevSibling = FormatterUtil.getPreviousNonWhitespaceSibling(myNode); IElementType prevType = prevSibling != null ? prevSibling.getElementType() : null; return prevType; } private boolean isChildOfSingleExpression() { return myNode.getPsi().getParent() instanceof XQueryExprSingle; } private boolean isParamOrArgumentList(IElementType parentType) { return parentType == PARAM_LIST || parentType == ARGUMENT_LIST; } private boolean isForOrLetBinding(IElementType parentType) { return parentType == LET_BINDING || parentType == FOR_BINDING; } private boolean isXmlChild(IElementType type) { return type == DIR_ELEM_CONTENT; } @Nullable private IElementType getIElementType(int newChildIndex) { Block block = getSubBlocks().get(newChildIndex - 1); while (block instanceof XQueryFormattingBlock && ! block.getSubBlocks().isEmpty()) { List<Block> subBlocks = block.getSubBlocks(); Block childBlock = subBlocks.get(subBlocks.size() - 1); if (! (childBlock instanceof XQueryFormattingBlock)) break; else { ASTNode node = ((XQueryFormattingBlock) childBlock).getNode(); PsiElement psi = node.getPsi(); IElementType elementType = node.getElementType(); if (elementType instanceof XQueryTokenType) break; if (psi instanceof LeafPsiElement || psi instanceof XQueryFunctionName || psi instanceof XQueryVarName || psi instanceof XQueryNamespacePrefix) break; } block = childBlock; } return block instanceof XQueryFormattingBlock ? ((XQueryFormattingBlock) block).getNode().getElementType() : null; } }