/* * Copyright 2000-2017 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 || myParentType == GroovyElementTypes.TUPLE_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; } }