| /* |
| * 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; |
| } |
| |
| } |