blob: f7dbe79fe6ac8527b592e6ca0f158861677e6f0a [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.jetbrains.plugins.groovy.formatter.processors;
import com.intellij.formatting.Wrap;
import com.intellij.formatting.WrapType;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.formatter.FormattingContext;
import org.jetbrains.plugins.groovy.formatter.blocks.GroovyBlock;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
/**
* @author Max Medvedev
*/
public class GroovyWrappingProcessor {
private final ASTNode myNode;
private final CommonCodeStyleSettings mySettings;
private final IElementType myParentType;
private final Wrap myCommonWrap;
private final FormattingContext myContext;
private boolean myUsedDefaultWrap = false;
public GroovyWrappingProcessor(GroovyBlock block) {
myContext = block.getContext();
mySettings = myContext.getSettings();
myNode = block.getNode();
myParentType = myNode.getElementType();
myCommonWrap = createCommonWrap();
}
private static final TokenSet SKIP = TokenSet.create(
GroovyTokenTypes.mCOMMA, GroovyTokenTypes.mQUESTION, GroovyTokenTypes.mSEMI,
GroovyTokenTypes.mASSIGN, GroovyTokenTypes.mBAND_ASSIGN, GroovyTokenTypes.mBOR_ASSIGN, GroovyTokenTypes.mBSR_ASSIGN,
GroovyTokenTypes.mBXOR_ASSIGN, GroovyTokenTypes.mDIV_ASSIGN,
GroovyTokenTypes.mMINUS_ASSIGN, GroovyTokenTypes.mMOD_ASSIGN, GroovyTokenTypes.mPLUS_ASSIGN, GroovyTokenTypes.mSL_ASSIGN,
GroovyTokenTypes.mSR_ASSIGN,
GroovyTokenTypes.mSTAR_ASSIGN, GroovyTokenTypes.mSTAR_STAR_ASSIGN,
GroovyTokenTypes.mASSIGN, GroovyTokenTypes.mBAND_ASSIGN, GroovyTokenTypes.mBOR_ASSIGN, GroovyTokenTypes.mBSR_ASSIGN,
GroovyTokenTypes.mBXOR_ASSIGN, GroovyTokenTypes.mDIV_ASSIGN,
GroovyTokenTypes.mMINUS_ASSIGN, GroovyTokenTypes.mMOD_ASSIGN, GroovyTokenTypes.mPLUS_ASSIGN, GroovyTokenTypes.mSL_ASSIGN,
GroovyTokenTypes.mSR_ASSIGN,
GroovyTokenTypes.mSTAR_ASSIGN, GroovyTokenTypes.mSTAR_STAR_ASSIGN,
GroovyTokenTypes.mBAND, GroovyTokenTypes.mBOR, GroovyTokenTypes.mBXOR, GroovyTokenTypes.mDIV, GroovyTokenTypes.mEQUAL,
GroovyTokenTypes.mGE, GroovyTokenTypes.mGT, GroovyTokenTypes.mLOR, GroovyTokenTypes.mLT, GroovyTokenTypes.mLE, GroovyTokenTypes.mMINUS,
GroovyTokenTypes.kAS, GroovyTokenTypes.kIN,
GroovyTokenTypes.mMOD, GroovyTokenTypes.mPLUS, GroovyTokenTypes.mSTAR, GroovyTokenTypes.mSTAR_STAR, GroovyTokenTypes.mNOT_EQUAL,
GroovyTokenTypes.mCOMPARE_TO, GroovyTokenTypes.mLAND, GroovyTokenTypes.kINSTANCEOF,
GroovyElementTypes.COMPOSITE_LSHIFT_SIGN, GroovyElementTypes.COMPOSITE_RSHIFT_SIGN, GroovyElementTypes.COMPOSITE_TRIPLE_SHIFT_SIGN,
GroovyTokenTypes.mREGEX_FIND, GroovyTokenTypes.mREGEX_MATCH, GroovyTokenTypes.mRANGE_INCLUSIVE, GroovyTokenTypes.mRANGE_EXCLUSIVE,
GroovyTokenTypes.mBNOT, GroovyTokenTypes.mLNOT, GroovyTokenTypes.mMINUS, GroovyTokenTypes.mDEC, GroovyTokenTypes.mPLUS,
GroovyTokenTypes.mINC,
GroovyTokenTypes.mSPREAD_DOT, GroovyTokenTypes.mOPTIONAL_DOT, GroovyTokenTypes.mMEMBER_POINTER, GroovyTokenTypes.mDOT,
GroovyElementTypes.COMPOSITE_LSHIFT_SIGN, GroovyElementTypes.COMPOSITE_RSHIFT_SIGN, GroovyElementTypes.COMPOSITE_TRIPLE_SHIFT_SIGN,
GroovyTokenTypes.mLT, GroovyTokenTypes.mGT, GroovyTokenTypes.mLE, GroovyTokenTypes.mGE, GroovyTokenTypes.kIN,
GroovyTokenTypes.kIN, GroovyTokenTypes.mCOLON,
GroovyTokenTypes.mGSTRING_CONTENT, GroovyTokenTypes.mGSTRING_END, GroovyElementTypes.GSTRING_INJECTION, GroovyTokenTypes.mREGEX_CONTENT,
GroovyTokenTypes.mREGEX_END, GroovyTokenTypes.mDOLLAR_SLASH_REGEX_CONTENT, GroovyTokenTypes.mDOLLAR_SLASH_REGEX_END
);
public Wrap getChildWrap(ASTNode childNode) {
if (myContext.isInsidePlainGString()) return createNoneWrap();
final IElementType childType = childNode.getElementType();
if (SKIP.contains(childType)) {
return createNoneWrap();
}
if (myParentType == GroovyElementTypes.EXTENDS_CLAUSE || myParentType == GroovyElementTypes.IMPLEMENTS_CLAUSE) {
if (childType == GroovyTokenTypes.kEXTENDS || childType == GroovyTokenTypes.kIMPLEMENTS) {
return Wrap.createWrap(mySettings.EXTENDS_KEYWORD_WRAP, true);
}
}
if (myParentType == GroovyElementTypes.ARGUMENTS) {
if (childType == GroovyTokenTypes.mLPAREN || childType == GroovyTokenTypes.mRPAREN) {
return createNoneWrap();
}
}
if (myParentType == GroovyElementTypes.THROW_CLAUSE && childType == GroovyTokenTypes.kTHROWS) {
return Wrap.createWrap(mySettings.THROWS_KEYWORD_WRAP, true);
}
if (myParentType == GroovyElementTypes.MODIFIERS) {
if (getLeftSiblingType(childNode) == GroovyElementTypes.ANNOTATION) {
return getCommonWrap();
}
else {
return createNormalWrap();
}
}
if (ANNOTATION_CONTAINERS.contains(myParentType)) {
final ASTNode leftSibling = getLeftSibling(childNode);
if (leftSibling != null && leftSibling.getElementType() == GroovyElementTypes.MODIFIERS && endsWithAnnotation(leftSibling)) {
final int wrapType = getAnnotationsWrapType(childNode);
if (wrapType != -1) {
return Wrap.createWrap(wrapType, true);
}
}
}
return getCommonWrap();
}
@Nullable
private static IElementType getLeftSiblingType(ASTNode node) {
ASTNode prev = getLeftSibling(node);
return prev != null ? prev.getElementType() : null;
}
private static ASTNode getLeftSibling(ASTNode node) {
ASTNode prev = node.getTreePrev();
while (prev != null && StringUtil.isEmptyOrSpaces(prev.getText())) {
prev = prev.getTreePrev();
}
return prev;
}
private static boolean endsWithAnnotation(ASTNode modifierListNode) {
final PsiElement psi = modifierListNode.getPsi();
return psi instanceof GrModifierList && psi.getLastChild() instanceof GrAnnotation;
}
private Wrap getCommonWrap() {
if (myCommonWrap == null) {
return createNoneWrap();
//return null;
}
if (myUsedDefaultWrap) {
return myCommonWrap;
}
else {
myUsedDefaultWrap = true;
return createNoneWrap();
//return null;
}
}
private static Wrap createNormalWrap() {
return Wrap.createWrap(WrapType.NORMAL, true);
}
private static Wrap createNoneWrap() {
return Wrap.createWrap(WrapType.NONE, false);
}
@Nullable
private Wrap createCommonWrap() {
if (myParentType == GroovyElementTypes.EXTENDS_CLAUSE || myParentType == GroovyElementTypes.IMPLEMENTS_CLAUSE) {
myUsedDefaultWrap = true;
return Wrap.createWrap(mySettings.EXTENDS_LIST_WRAP, true);
}
if (myParentType == GroovyElementTypes.THROW_CLAUSE) {
myUsedDefaultWrap = true;
return Wrap.createWrap(mySettings.THROWS_LIST_WRAP, true);
}
if (myParentType == GroovyElementTypes.PARAMETERS_LIST) {
myUsedDefaultWrap = true;
return Wrap.createWrap(mySettings.METHOD_PARAMETERS_WRAP, true);
}
if (myParentType == GroovyElementTypes.ARGUMENTS || myParentType == GroovyElementTypes.COMMAND_ARGUMENTS) {
myUsedDefaultWrap = myParentType == GroovyElementTypes.ARGUMENTS;
return Wrap.createWrap(mySettings.CALL_PARAMETERS_WRAP, myUsedDefaultWrap);
}
if (myParentType == GroovyElementTypes.FOR_TRADITIONAL_CLAUSE || myParentType == GroovyElementTypes.FOR_IN_CLAUSE) {
myUsedDefaultWrap = true;
return Wrap.createWrap(mySettings.FOR_STATEMENT_WRAP, true);
}
if (TokenSets.BINARY_EXPRESSIONS.contains(myParentType)) {
return Wrap.createWrap(mySettings.BINARY_OPERATION_WRAP, false);
}
if (myParentType == GroovyElementTypes.ASSIGNMENT_EXPRESSION) {
return Wrap.createWrap(mySettings.ASSIGNMENT_WRAP, false);
}
if (myParentType == GroovyElementTypes.CONDITIONAL_EXPRESSION || myParentType == GroovyElementTypes.ELVIS_EXPRESSION) {
return Wrap.createWrap(mySettings.TERNARY_OPERATION_WRAP, false);
}
if (myParentType == GroovyElementTypes.ASSERT_STATEMENT) {
return Wrap.createWrap(mySettings.ASSERT_STATEMENT_WRAP, false);
}
if (TokenSets.BLOCK_SET.contains(myParentType)) {
return createNormalWrap();
}
if (myParentType == GroovyElementTypes.MODIFIERS) {
final int wrapType = getAnnotationsWrapType(myNode);
if (wrapType != -1) {
myUsedDefaultWrap = true;
return Wrap.createWrap(wrapType, true);
}
}
return null;
}
public Wrap getChainedMethodCallWrap() {
return myContext.isInsidePlainGString() ? Wrap.createWrap(WrapType.NONE, false)
: Wrap.createWrap(mySettings.METHOD_CALL_CHAIN_WRAP, false);
}
private final TokenSet ANNOTATION_CONTAINERS = TokenSet.create(
GroovyElementTypes.CLASS_DEFINITION, GroovyElementTypes.INTERFACE_DEFINITION, GroovyElementTypes.ENUM_DEFINITION, GroovyElementTypes.TRAIT_DEFINITION,
GroovyElementTypes.ANNOTATION_DEFINITION,
GroovyElementTypes.METHOD_DEFINITION, GroovyElementTypes.CONSTRUCTOR_DEFINITION,
GroovyElementTypes.VARIABLE_DEFINITION,
GroovyElementTypes.PARAMETER,
GroovyElementTypes.ENUM_CONSTANT,
GroovyElementTypes.IMPORT_STATEMENT
);
private int getAnnotationsWrapType(ASTNode modifierList) {
final IElementType containerType = modifierList.getTreeParent().getElementType();
if (TokenSets.TYPE_DEFINITIONS.contains(containerType)) {
return mySettings.CLASS_ANNOTATION_WRAP;
}
if (TokenSets.METHOD_DEFS.contains(containerType)) {
return mySettings.METHOD_ANNOTATION_WRAP;
}
if (GroovyElementTypes.VARIABLE_DEFINITION == containerType) {
final IElementType pparentType = modifierList.getTreeParent().getTreeParent().getElementType();
if (pparentType == GroovyElementTypes.CLASS_BODY || pparentType == GroovyElementTypes.ENUM_BODY) {
return mySettings.FIELD_ANNOTATION_WRAP;
}
else {
return mySettings.VARIABLE_ANNOTATION_WRAP;
}
}
if (GroovyElementTypes.PARAMETER == containerType) {
return mySettings.PARAMETER_ANNOTATION_WRAP;
}
if (GroovyElementTypes.ENUM_CONSTANT == containerType) {
return mySettings.ENUM_CONSTANTS_WRAP;
}
if (GroovyElementTypes.IMPORT_STATEMENT == containerType) {
return myContext.getGroovySettings().IMPORT_ANNOTATION_WRAP;
}
return -1;
}
}