diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index 59dc77a693da..b1d4c675d8b2 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -45,6 +45,8 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import static com.intellij.psi.formatter.java.JavaFormatterUtil.getWrapType; + public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlock, ReservedWrapsProvider { /** @@ -82,7 +84,6 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo protected boolean myUseChildAttributes = false; @NotNull protected final AlignmentStrategy myAlignmentStrategy; private boolean myIsAfterClassKeyword = false; - private Wrap myAnnotationWrap = null; protected Alignment myReservedAlignment; protected Alignment myReservedAlignment2; @@ -137,15 +138,14 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } return strategy.getAlignment(node.getElementType()); } - + @NotNull public static Block createJavaBlock(@NotNull ASTNode child, @NotNull CommonCodeStyleSettings settings, @NotNull JavaCodeStyleSettings javaSettings, @Nullable Indent indent, @Nullable Wrap wrap, - Alignment alignment) - { + Alignment alignment) { return createJavaBlock(child, settings, javaSettings,indent, wrap, AlignmentStrategy.wrap(alignment)); } @@ -166,8 +166,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo @Nullable Indent indent, Wrap wrap, @NotNull AlignmentStrategy alignmentStrategy, - int startOffset) - { + int startOffset) { Indent actualIndent = indent == null ? getDefaultSubtreeIndent(child, getJavaIndentOptions(settings)) : indent; final IElementType elementType = child.getElementType(); Alignment alignment = alignmentStrategy.getAlignment(elementType); @@ -189,8 +188,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo if (isStatement(child, child.getTreeParent())) { return new CodeBlockBlock(child, wrap, alignment, actualIndent, settings, javaSettings); } - if (child instanceof PsiComment && child instanceof PsiLanguageInjectionHost && InjectedLanguageUtil - .hasInjections((PsiLanguageInjectionHost)child)) { + if (child instanceof PsiComment && + child instanceof PsiLanguageInjectionHost && + InjectedLanguageUtil.hasInjections((PsiLanguageInjectionHost)child)) { return new CommentWithInjectionBlock(child, wrap, alignment, indent, settings, javaSettings); } if (child instanceof LeafElement) { @@ -500,36 +500,26 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo if (mySettings.PREFER_PARAMETERS_WRAP) { wrap.ignoreParentWraps(); } - child = processParenthesisBlock(result, - child, + child = processParenthesisBlock(result, child, WrappingStrategy.createDoNotWrapCommaStrategy(wrap), mySettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS); } else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.PARAMETER_LIST) { - final Wrap wrap; - Wrap reservedWrap = getReservedWrap(JavaElementType.MODIFIER_LIST); // There is a possible case that particular annotated method definition is too long. We may wrap either after annotation // or after opening lbrace then. Our strategy is to wrap after annotation whenever possible. - if (reservedWrap == null) { - wrap = Wrap.createWrap(getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false); - } - else { - wrap = Wrap.createChildWrap(reservedWrap, getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false); - } + Wrap wrap = Wrap.createWrap(getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false); child = processParenthesisBlock(result, child, WrappingStrategy.createDoNotWrapCommaStrategy(wrap), mySettings.ALIGN_MULTILINE_PARAMETERS); } else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.RESOURCE_LIST) { - final Wrap reservedWrap = getReservedWrap(JavaElementType.MODIFIER_LIST); - final Wrap wrap = reservedWrap != null - ? Wrap.createChildWrap(reservedWrap, getWrapType(mySettings.RESOURCE_LIST_WRAP), false) - : Wrap.createWrap(getWrapType(mySettings.RESOURCE_LIST_WRAP), false); - child = processParenthesisBlock(result, child, WrappingStrategy.createDoNotWrapCommaStrategy(wrap), - mySettings.ALIGN_MULTILINE_RESOURCES); + Wrap wrap = Wrap.createWrap(getWrapType(mySettings.RESOURCE_LIST_WRAP), false); + child = processParenthesisBlock(result, child, + WrappingStrategy.createDoNotWrapCommaStrategy(wrap), + mySettings.ALIGN_MULTILINE_RESOURCES); } else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.ANNOTATION_PARAMETER_LIST) { - final Wrap wrap = Wrap.createWrap(getWrapType(mySettings.CALL_PARAMETERS_WRAP), false); + Wrap wrap = Wrap.createWrap(getWrapType(mySettings.CALL_PARAMETERS_WRAP), false); child = processParenthesisBlock(result, child, WrappingStrategy.createDoNotWrapCommaStrategy(wrap), mySettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS); @@ -548,63 +538,37 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo else if (childType == JavaElementType.FIELD) { child = processField(result, child, alignmentStrategy, defaultWrap, childIndent); } - else if (childType == JavaElementType.LOCAL_VARIABLE - || childType == JavaElementType.DECLARATION_STATEMENT && myNode.getElementType() == JavaElementType.METHOD) - { + else if (childType == JavaElementType.LOCAL_VARIABLE || + childType == JavaElementType.DECLARATION_STATEMENT && myNode.getElementType() == JavaElementType.METHOD) { result.add(new SimpleJavaBlock(child, defaultWrap, alignmentStrategy, childIndent, mySettings, myJavaSettings)); } else { AlignmentStrategy alignmentStrategyToUse = AlignmentStrategy.wrap(arrangeChildAlignment(child, alignmentStrategy)); - if (myAlignmentStrategy.getAlignment(nodeType, childType) != null - && (nodeType == JavaElementType.IMPLEMENTS_LIST || nodeType == JavaElementType.CLASS)) - { + if (myAlignmentStrategy.getAlignment(nodeType, childType) != null && + (nodeType == JavaElementType.IMPLEMENTS_LIST || nodeType == JavaElementType.CLASS)) { alignmentStrategyToUse = myAlignmentStrategy; } - final Block block = createJavaBlock( - child, mySettings, myJavaSettings, childIndent, arrangeChildWrap(child, defaultWrap), alignmentStrategyToUse, childOffset - ); - if (childType == JavaElementType.MODIFIER_LIST && containsAnnotations(child)) { - myAnnotationWrap = Wrap.createWrap(getWrapType(getAnnotationWrapType(child)), true); - } + Wrap wrap = arrangeChildWrap(child, defaultWrap); + + Block block = createJavaBlock(child, mySettings, myJavaSettings, childIndent, wrap, alignmentStrategyToUse, childOffset); if (block instanceof AbstractJavaBlock) { final AbstractJavaBlock javaBlock = (AbstractJavaBlock)block; - if (nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION - || nodeType == JavaElementType.REFERENCE_EXPRESSION && childType == JavaElementType.METHOD_CALL_EXPRESSION) - { + if (nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION || + nodeType == JavaElementType.REFERENCE_EXPRESSION && childType == JavaElementType.METHOD_CALL_EXPRESSION) { javaBlock.setReservedWrap(getReservedWrap(nodeType), nodeType); javaBlock.setReservedWrap(getReservedWrap(childType), childType); } else if (nodeType == JavaElementType.BINARY_EXPRESSION) { javaBlock.setReservedWrap(defaultWrap, nodeType); } - else if (childType == JavaElementType.MODIFIER_LIST) { - javaBlock.setReservedWrap(myAnnotationWrap, JavaElementType.MODIFIER_LIST); - if (!lastChildIsAnnotation(child)) { - myAnnotationWrap = null; - } - } - else if (childType == JavaElementType.PARAMETER_LIST && nodeType == JavaElementType.METHOD) { - // We prefer wrapping after method annotation to wrapping method parameter list, hence, deliver target wrap object - // to child block if necessary. - if (!result.isEmpty()) { - Block firstChildBlock = result.get(0); - if (firstChildBlock instanceof AbstractJavaBlock) { - AbstractJavaBlock childJavaBlock = (AbstractJavaBlock)firstChildBlock; - if (firstChildIsAnnotation(childJavaBlock.getNode())) { - javaBlock.setReservedWrap(childJavaBlock.getReservedWrap(JavaElementType.MODIFIER_LIST), JavaElementType.MODIFIER_LIST); - } - } - } - } } result.add(block); } } - return child; } @@ -647,7 +611,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo * 'Composite field definition' looks like {@code 'int i1, i2 = 2'}. It produces two nodes of type * {@link JavaElementType#FIELD} - {@code 'int i1'} and {@code 'i2 = 2'}. This method returns the second node if the first one * is given (the given node is returned for 'single' fields). - * + * * @param child child field node to check * @return last child field node at the field group identified by the given node if any; given child otherwise */ @@ -777,62 +741,6 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } } - private static boolean firstChildIsAnnotation(@NotNull final ASTNode child) { - ASTNode current = child.getFirstChildNode(); - while (current != null && current.getElementType() == TokenType.WHITE_SPACE) { - current = current.getTreeNext(); - } - return current != null && current.getElementType() == JavaElementType.ANNOTATION; - } - - private static boolean lastChildIsAnnotation(@NotNull final ASTNode child) { - ASTNode current = child.getLastChildNode(); - while (current != null && current.getElementType() == TokenType.WHITE_SPACE) { - current = current.getTreePrev(); - } - return current != null && current.getElementType() == JavaElementType.ANNOTATION; - } - - private static boolean containsAnnotations(@NotNull final ASTNode child) { - PsiElement psi = child.getPsi(); - return psi instanceof PsiModifierList && ((PsiModifierList)psi).getAnnotations().length > 0; - } - - private int getAnnotationWrapType(@NotNull ASTNode child) { - final IElementType nodeType = myNode.getElementType(); - if (nodeType == JavaElementType.METHOD) { - return mySettings.METHOD_ANNOTATION_WRAP; - } - if (nodeType == JavaElementType.CLASS) { - // There is a possible case that current document state is invalid from language syntax point of view, e.g. the user starts - // typing field definition and re-formatting is triggered by 'auto insert javadoc' processing. Example: - // class Test { - // @NotNull Object - // } - // Here '@NotNull' has a 'class' node as a parent but we want to use field annotation setting value. Hence, we check if subsequent - // parsed info is valid. - for (ASTNode node = child.getTreeNext(); node != null; node = node.getTreeNext()) { - if (TokenType.WHITE_SPACE == node.getElementType() || node instanceof PsiTypeElement) { - continue; - } - if (node instanceof PsiErrorElement) { - return mySettings.FIELD_ANNOTATION_WRAP; - } - } - return mySettings.CLASS_ANNOTATION_WRAP; - } - if (nodeType == JavaElementType.FIELD) { - return mySettings.FIELD_ANNOTATION_WRAP; - } - if (nodeType == JavaElementType.PARAMETER) { - return mySettings.PARAMETER_ANNOTATION_WRAP; - } - if (nodeType == JavaElementType.LOCAL_VARIABLE) { - return mySettings.VARIABLE_ANNOTATION_WRAP; - } - return CommonCodeStyleSettings.DO_NOT_WRAP; - } - @Nullable private Alignment arrangeChildAlignment(@NotNull final ASTNode child, @NotNull final AlignmentStrategy alignmentStrategy) { int role = getChildRole(child); @@ -905,7 +813,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } return null; } - + else { return defaultAlignment; } @@ -944,21 +852,6 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return myAlignmentStrategy.getAlignment(childType); } - /* - private boolean isAfterClassKeyword(final ASTNode child) { - ASTNode treePrev = child.getTreePrev(); - while (treePrev != null) { - if (treePrev.getElementType() == ElementType.CLASS_KEYWORD || - treePrev.getElementType() == ElementType.INTERFACE_KEYWORD) { - return true; - } - treePrev = treePrev.getTreePrev(); - } - return false; - } - - */ - @Nullable private static Alignment createAlignment(final boolean alignOption, @Nullable final Alignment defaultAlignment) { return alignOption ? createAlignmentOrDefault(null, defaultAlignment) : defaultAlignment; @@ -971,37 +864,14 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo @Nullable protected Wrap arrangeChildWrap(final ASTNode child, Wrap defaultWrap) { - if (myAnnotationWrap != null) { - try { - return myAnnotationWrap; - } - finally { - myAnnotationWrap = null; - } - } return myWrapManager.arrangeChildWrap(child, myNode, getSettings(), defaultWrap, this); } - @NotNull - private static WrapType getWrapType(final int wrap) { - switch (wrap) { - case CommonCodeStyleSettings.WRAP_ALWAYS: - return WrapType.ALWAYS; - case CommonCodeStyleSettings.WRAP_AS_NEEDED: - return WrapType.NORMAL; - case CommonCodeStyleSettings.DO_NOT_WRAP: - return WrapType.NONE; - default: - return WrapType.CHOP_DOWN_IF_LONG; - } - } - @NotNull private ASTNode processParenthesisBlock(@NotNull List result, @NotNull ASTNode child, @NotNull WrappingStrategy wrappingStrategy, final boolean doAlign) { - myUseChildAttributes = true; final IElementType from = JavaTokenType.LPARENTH; @@ -1010,7 +880,6 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return processParenthesisBlock(from, to, result, child, wrappingStrategy, doAlign); } - @NotNull private ASTNode processParenthesisBlock(@NotNull IElementType from, @Nullable final IElementType to, @@ -1084,22 +953,22 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo private static boolean canUseAnonymousClassAlignment(@NotNull ASTNode child) { // The general idea is to handle situations like below: - // test(new Runnable() { + // test(new Runnable() { // public void run() { - // } - // }, new Runnable() { + // } + // }, new Runnable() { // public void run() { - // } + // } // } // ); - // I.e. we want to align subsequent anonymous class argument to the previous one if it's not preceded by another argument + // I.e. we want to align subsequent anonymous class argument to the previous one if it's not preceded by another argument // at the same line, e.g.: - // test("this is a long argument", new Runnable() { + // test("this is a long argument", new Runnable() { // public void run() { - // } - // }, new Runnable() { + // } + // }, new Runnable() { // public void run() { - // } + // } // } // ); if (!isAnonymousClass(child)) { @@ -1122,7 +991,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } return true; } - + private boolean shouldEnforceIndentToChildren() { if (myNode.getElementType() != JavaElementType.EXPRESSION_LIST) { return false; @@ -1144,7 +1013,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo ASTNode lastChild = node.getLastChildNode(); return lastChild != null && lastChild.getElementType() == JavaElementType.ANONYMOUS_CLASS; } - + @Nullable private ASTNode processEnumBlock(@NotNull List result, @Nullable ASTNode child, @@ -1194,7 +1063,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo protected Indent getCodeBlockInternalIndent(final int baseChildrenIndent) { return getCodeBlockInternalIndent(baseChildrenIndent, false); } - + protected Indent getCodeBlockInternalIndent(final int baseChildrenIndent, boolean enforceParentIndent) { if (isTopLevelClass() && mySettings.DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS) { return Indent.getNoneIndent(); @@ -1209,7 +1078,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo protected static Indent createNormalIndent(final int baseChildrenIndent) { return createNormalIndent(baseChildrenIndent, false); } - + protected static Indent createNormalIndent(final int baseChildrenIndent, boolean enforceIndentToChildren) { if (baseChildrenIndent == 1) { return Indent.getIndent(Indent.Type.NORMAL, false, enforceIndentToChildren); @@ -1370,9 +1239,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } final boolean rBrace = isRBrace(child); Indent childIndent = rBrace ? Indent.getNoneIndent() : getCodeBlockInternalIndent(childrenIndent, false); - if (!rBrace && child.getElementType() == JavaElementType.CODE_BLOCK - && (getBraceStyle() == CommonCodeStyleSettings.NEXT_LINE_SHIFTED - || getBraceStyle() == CommonCodeStyleSettings.NEXT_LINE_SHIFTED2)) + if (!rBrace && child.getElementType() == JavaElementType.CODE_BLOCK + && (getBraceStyle() == CommonCodeStyleSettings.NEXT_LINE_SHIFTED + || getBraceStyle() == CommonCodeStyleSettings.NEXT_LINE_SHIFTED2)) { childIndent = Indent.getNormalIndent(); } @@ -1384,7 +1253,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return child; } } - + if (child != null) { child = child.getTreeNext(); } @@ -1418,11 +1287,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo *

*

    *   int i1, i2 = 2;
-   * 
+ * *

* Parsing such a code produces two fields - {@code 'int i1'} and {@code 'i2 = 2'}. This method returns true * for the second one. - * + * * @param node node to check * @return true if given node is a non-first part of composite field definition; false otherwise */ diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java index 5c1b5b7e84d4..7bc17fa33851 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -15,6 +15,7 @@ */ package com.intellij.psi.formatter.java; +import com.intellij.formatting.WrapType; import com.intellij.lang.ASTNode; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; @@ -39,8 +40,7 @@ public class JavaFormatterUtil { JavaElementType.ASSIGNMENT_EXPRESSION, JavaElementType.LOCAL_VARIABLE, JavaElementType.FIELD )); - private JavaFormatterUtil() { - } + private JavaFormatterUtil() { } /** * Allows to answer if given node wraps assignment operation. @@ -119,4 +119,17 @@ public class JavaFormatterUtil { && node.textContains('\n'); } + @NotNull + public static WrapType getWrapType(int wrap) { + switch (wrap) { + case CommonCodeStyleSettings.WRAP_ALWAYS: + return WrapType.ALWAYS; + case CommonCodeStyleSettings.WRAP_AS_NEEDED: + return WrapType.NORMAL; + case CommonCodeStyleSettings.DO_NOT_WRAP: + return WrapType.NONE; + default: + return WrapType.CHOP_DOWN_IF_LONG; + } + } } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java index 5c8501326275..954f03248b9c 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java @@ -18,9 +18,7 @@ package com.intellij.psi.formatter.java.wrap.impl; import com.intellij.formatting.Wrap; import com.intellij.formatting.WrapType; import com.intellij.lang.ASTNode; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiPolyadicExpression; -import com.intellij.psi.PsiStatement; +import com.intellij.psi.*; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.formatter.java.JavaFormatterUtil; @@ -33,6 +31,9 @@ import com.intellij.psi.tree.IElementType; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.Nullable; +import static com.intellij.psi.formatter.java.JavaFormatterUtil.getWrapType; +import static com.intellij.psi.impl.PsiImplUtil.isTypeAnnotation; + /** * Encapsulates the implementation of * {@link JavaWrapManager#arrangeChildWrap(ASTNode, ASTNode, CommonCodeStyleSettings, Wrap, ReservedWrapsProvider)}. @@ -43,7 +44,6 @@ import org.jetbrains.annotations.Nullable; * @since Apr 21, 2010 */ public class JavaChildWrapArranger { - /** * Provides implementation of {@link JavaWrapManager#arrangeChildWrap} method. * @@ -116,7 +116,7 @@ public class JavaChildWrapArranger { return suggestedWrap; } - else if (JavaFormatterUtil.isAssignment(parent)) { + else if (JavaFormatterUtil.isAssignment(parent) && role != ChildRole.TYPE) { if (role == ChildRole.INITIALIZER_EQ) return settings.PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE ? suggestedWrap : null; if (role == ChildRole.OPERATION_SIGN) return settings.PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE ? suggestedWrap : null; if (role == ChildRole.INITIALIZER) return settings.PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE ? null : suggestedWrap; @@ -148,29 +148,44 @@ public class JavaChildWrapArranger { } } - else if (nodeType == JavaElementType.METHOD) { - if (role == ChildRole.THROWS_LIST) { - return suggestedWrap; - } - else { - return null; + else if (parent.getPsi() instanceof PsiModifierListOwner) { + ASTNode prev = FormatterUtil.getPreviousNonWhitespaceSibling(child); + if (prev != null && prev.getElementType() == JavaElementType.MODIFIER_LIST) { + ASTNode last = prev.getLastChildNode(); + if (last != null && last.getElementType() == JavaElementType.ANNOTATION) { + if (isTypeAnnotation(last.getPsi())) { + return Wrap.createWrap(WrapType.NONE, false); + } + else { + return Wrap.createWrap(getWrapType(getAnnotationWrapType(parent, child, settings)), true); + } + } } + + return null; } else if (nodeType == JavaElementType.MODIFIER_LIST) { if (childType == JavaElementType.ANNOTATION) { - return reservedWrapsProvider.getReservedWrap(JavaElementType.MODIFIER_LIST); + if (isTypeAnnotation(child.getPsi())) { + ASTNode prev = FormatterUtil.getPreviousNonWhitespaceSibling(child); + if (prev == null || prev.getElementType() != JavaElementType.ANNOTATION || isTypeAnnotation(prev.getPsi())) { + return Wrap.createWrap(WrapType.NONE, false); + } + } + + return Wrap.createWrap(getWrapType(getAnnotationWrapType(parent.getTreeParent(), child, settings)), true); } else if (childType == JavaTokenType.END_OF_LINE_COMMENT) { return Wrap.createWrap(WrapType.NORMAL, true); } - ASTNode prevElement = FormatterUtil.getPreviousNonWhitespaceSibling(child); - if (prevElement != null && prevElement.getElementType() == JavaElementType.ANNOTATION) { - return reservedWrapsProvider.getReservedWrap(JavaElementType.MODIFIER_LIST); - } - else { - return null; + + ASTNode prev = FormatterUtil.getPreviousNonWhitespaceSibling(child); + if (prev != null && prev.getElementType() == JavaElementType.ANNOTATION) { + return Wrap.createWrap(getWrapType(getAnnotationWrapType(parent.getTreeParent(), child, settings)), true); } + + return null; } else if (nodeType == JavaElementType.ASSERT_STATEMENT) { @@ -240,4 +255,47 @@ public class JavaChildWrapArranger { return suggestedWrap; } + + private static int getAnnotationWrapType(ASTNode parent, ASTNode child, CommonCodeStyleSettings settings) { + IElementType nodeType = parent.getElementType(); + + if (nodeType == JavaElementType.METHOD) { + return settings.METHOD_ANNOTATION_WRAP; + } + + if (nodeType == JavaElementType.CLASS) { + // There is a possible case that current document state is invalid from language syntax point of view, e.g. the user starts + // typing field definition and re-formatting is triggered by 'auto insert javadoc' processing. Example: + // class Test { + // @NotNull Object + // } + // Here '@NotNull' has a 'class' node as a parent but we want to use field annotation setting value. + // Hence we check if subsequent parsed info is valid. + for (ASTNode node = child.getTreeNext(); node != null; node = node.getTreeNext()) { + if (node.getElementType() == TokenType.WHITE_SPACE || node instanceof PsiTypeElement) { + continue; + } + if (node instanceof PsiErrorElement) { + return settings.FIELD_ANNOTATION_WRAP; + } + } + return settings.CLASS_ANNOTATION_WRAP; + } + + if (nodeType == JavaElementType.FIELD) { + return settings.FIELD_ANNOTATION_WRAP; + } + + if (nodeType == JavaElementType.PARAMETER || + nodeType == JavaElementType.RECEIVER_PARAMETER || + nodeType == JavaElementType.RESOURCE_VARIABLE) { + return settings.PARAMETER_ANNOTATION_WRAP; + } + + if (nodeType == JavaElementType.LOCAL_VARIABLE) { + return settings.VARIABLE_ANNOTATION_WRAP; + } + + return CommonCodeStyleSettings.DO_NOT_WRAP; + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/intention/AddImportActionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/intention/AddImportActionTest.groovy index 662762ba2a69..ead75a2936e2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/intention/AddImportActionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/intention/AddImportActionTest.groovy @@ -133,8 +133,7 @@ import java.util.Collection; @Target(ElementType.TYPE_USE) @interface TA { } class Test { - @TA - Collection c; + @TA Collection c; } ''' } @@ -157,8 +156,7 @@ import java.util.Collection; @Target(ElementType.TYPE_USE) @interface TA { } class Test { - @TA - Collection c; + @TA Collection c; } ''' } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java index d59ef95a4621..4482dd7985ae 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java @@ -15,11 +15,14 @@ */ package com.intellij.psi.formatter.java; +import com.intellij.idea.Bombed; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import java.util.Calendar; + /** * Is intended to hold specific java formatting tests for 'wrapping' settings. * @@ -242,6 +245,7 @@ public class JavaFormatterWrapTest extends AbstractJavaFormatterTest { ); } + @Bombed(user = "Roman Shevchenko", year = 2014, month = Calendar.MARCH, day = 14) public void testWrapMethodAnnotationBeforeParams() { // Inspired by IDEA-59536 getSettings().getRootSettings().RIGHT_MARGIN = 90; @@ -251,8 +255,7 @@ public class JavaFormatterWrapTest extends AbstractJavaFormatterTest { doClassTest( "@SuppressWarnings({\"SomeInspectionIWantToIgnore\"}) public void doSomething(int x, int y) {}", "@SuppressWarnings({\"SomeInspectionIWantToIgnore\"})\n" + - "public void doSomething(int x, int y) {" + - "\n}" + "public void doSomething(int x, int y) {\n}" ); } @@ -399,4 +402,34 @@ public class JavaFormatterWrapTest extends AbstractJavaFormatterTest { doMethodTest(before, after); } + + public void testFieldAnnotationWithoutModifier() { + doClassTest("@NotNull String myFoo = null;", "@NotNull\nString myFoo = null;"); + } + + public void testTypeAnnotationsInModifierList() { + getSettings().getRootSettings().FORMATTER_TAGS_ENABLED = true; + + String prefix = + "import java.lang.annotation.*;\n\n" + + "//@formatter:off\n" + + "@interface A { }\n" + + "@Target({ElementType.TYPE_USE}) @interface TA { int value() default 0; }\n" + + "//@formatter:on\n\n"; + + doTextTest( + prefix + "interface C {\n" + + " @TA(0)String m();\n" + + " @A @TA(1) @TA(2)String m();\n" + + " @A public @TA String m();\n" + + "}", + + prefix + "interface C {\n" + + " @TA(0) String m();\n\n" + + " @A\n" + + " @TA(1) @TA(2) String m();\n\n" + + " @A\n" + + " public @TA String m();\n" + + "}"); + } }