/* * Copyright 2012-2014 Sergey Ignatov * * 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.erlang.formatter; import com.intellij.formatting.Indent; import com.intellij.lang.ASTNode; import com.intellij.psi.TokenType; import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import org.intellij.erlang.ErlangParserDefinition; import org.intellij.erlang.formatter.settings.ErlangCodeStyleSettings; import org.intellij.erlang.psi.ErlangArgumentDefinition; import org.intellij.erlang.psi.ErlangExpression; import org.intellij.erlang.psi.ErlangListOpExpression; import org.intellij.erlang.psi.ErlangParenthesizedExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Set; import static org.intellij.erlang.ErlangTypes.*; public class ErlangIndentProcessor { private static final Set<IElementType> BIN_OPERATORS = ContainerUtil.set( ERL_OP_PLUS, ERL_OP_MINUS, ERL_OP_AR_MUL, ERL_OP_AR_DIV, ERL_REM, ERL_OR, ERL_XOR, ERL_BOR, ERL_BXOR, ERL_BSL, ERL_BSR, ERL_AND, ERL_BAND, ERL_OP_EQ_EQ, ERL_OP_DIV_EQ, ERL_OP_EQ_COL_EQ, ERL_OP_EQ_DIV_EQ, ERL_OP_LT, ERL_OP_EQ_LT, ERL_OP_GT, ERL_OP_GT_EQ, ERL_OP_LT_EQ, ERL_OP_EQ, ERL_OP_EXL, ERL_OP_LT_MINUS, ERL_ANDALSO, ERL_ORELSE ); private final ErlangCodeStyleSettings myErlangSettings; public ErlangIndentProcessor(@NotNull ErlangCodeStyleSettings erlangSettings) { myErlangSettings = erlangSettings; } public Indent getChildIndent(ASTNode node, int binaryExpressionIndex) { if (binaryExpressionIndex > 0) return Indent.getNormalIndent(); IElementType elementType = node.getElementType(); ASTNode parent = node.getTreeParent(); IElementType parentType = parent != null ? parent.getElementType() : null; ASTNode grandfather = parent != null ? parent.getTreeParent() : null; IElementType grandfatherType = grandfather != null ? grandfather.getElementType() : null; ASTNode prevSibling = FormatterUtil.getPreviousNonWhitespaceSibling(node); IElementType prevSiblingElementType = prevSibling != null ? prevSibling.getElementType() : null; if (parent == null || parent.getTreeParent() == null) { return Indent.getNoneIndent(); } boolean containerNormal = ErlangFormattingBlock.isContainerNormal(parentType); boolean containerContinuation = ErlangFormattingBlock.isContainerContinuation(parentType, containerNormal); if (containerNormal || containerContinuation) { boolean initial = elementType == ERL_RADIX; boolean left = elementType == ERL_PAR_LEFT || elementType == ERL_CURLY_LEFT || elementType == ERL_BRACKET_LEFT || elementType == ERL_BIN_START || elementType == ERL_RADIX; boolean right = elementType == ERL_PAR_RIGHT || elementType == ERL_CURLY_RIGHT || elementType == ERL_BRACKET_RIGHT || elementType == ERL_BIN_END; if (initial || left || right && !FormatterUtil.isPrecededBy(node, ERL_COMMA, TokenType.ERROR_ELEMENT) && !FormatterUtil.isPrecededBy(node, ERL_OR_OR, TokenType.ERROR_ELEMENT)) { return Indent.getNoneIndent(); } return containerContinuation ? Indent.getContinuationIndent() : Indent.getNormalIndent(); } if ((parentType == ERL_GUARD || parentType == ERL_CLAUSE_GUARD && elementType == ERL_WHEN) && grandfatherType != ERL_IF_CLAUSE) { return Indent.getNormalIndent(); } if (parentType == ERL_RECORD_TUPLE) { // todo: not a smart solution //noinspection unchecked boolean insideCall = PsiTreeUtil.getParentOfType(node.getPsi(), ErlangArgumentDefinition.class, ErlangParenthesizedExpression.class) != null; return insideCall ? Indent.getNormalIndent() : Indent.getNoneIndent(); } if (parentType == ERL_BEGIN_END_BODY || parentType == ERL_TRY_EXPRESSIONS_CLAUSE) { return Indent.getNoneIndent(); } if (parentType == ERL_TRY_CLAUSES || parentType == ERL_FUN_CLAUSES) { return Indent.getNormalIndent(); } if (parentType == ERL_CASE_EXPRESSION || parentType == ERL_RECEIVE_EXPRESSION || parentType == ERL_TRY_EXPRESSION || parentType == ERL_BEGIN_END_EXPRESSION || parentType == ERL_IF_EXPRESSION || parentType == ERL_FUN_EXPRESSION || parentType == ERL_CATCH_EXPRESSION) { if (elementType == ERL_CR_CLAUSE || elementType == ERL_IF_CLAUSE || elementType == ERL_BEGIN_END_BODY || elementType == ERL_TRY_EXPRESSIONS_CLAUSE || elementType == ERL_AFTER_CLAUSE_BODY) { return Indent.getNormalIndent(myErlangSettings.INDENT_RELATIVE); } if (elementType == ERL_OF || elementType == ERL_CATCH || elementType == ERL_AFTER || elementType == ERL_END || elementType == ERL_TRY_CLAUSES || elementType == ERL_FUN_CLAUSES) { return myErlangSettings.INDENT_RELATIVE ? Indent.getSpaceIndent(0, true) : Indent.getNoneIndent(); } if (parentType == ERL_CASE_EXPRESSION && elementType != ERL_CASE) { return Indent.getNormalIndent(myErlangSettings.INDENT_RELATIVE); } } if (ErlangParserDefinition.COMMENTS.contains(elementType) && parentType == ERL_TRY_EXPRESSION) { return Indent.getNormalIndent(); } if (needIndent(parentType)) { return Indent.getNormalIndent(); } if (node.getPsi() instanceof ErlangListOpExpression) { return Indent.getNoneIndent(); } if (parent.getPsi() instanceof ErlangListOpExpression && (grandfather == null || grandfather.getPsi() instanceof ErlangExpression)) { return Indent.getNormalIndent(); } if (parentType == ERL_PREFIX_EXPRESSION) { return Indent.getNoneIndent(); } if (parent.getPsi() instanceof ErlangExpression && (BIN_OPERATORS.contains(elementType) || BIN_OPERATORS.contains(prevSiblingElementType))) { return Indent.getNormalIndent(); } return Indent.getNoneIndent(); } private static boolean needIndent(@Nullable IElementType type) { return type != null && ErlangFormattingBlock.BLOCKS_TOKEN_SET.contains(type); } }