diff --git a/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingOptionsProvider.java b/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingOptionsProvider.java index e33b90bab9aa..a0316253221b 100644 --- a/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingOptionsProvider.java +++ b/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingOptionsProvider.java @@ -36,5 +36,6 @@ public class JavaCodeFoldingOptionsProvider extends BeanConfigurable elements, @NotNull Collection result) { - final Set statements = new HashSet(); - - for (PsiElement element : elements) { - if (element instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)element; - final PsiStatement statement = PsiTreeUtil.getParentOfType(methodCall, PsiStatement.class, true, PsiMethod.class); - if (!statements.contains(statement) && isRecursiveMethodCall(methodCall)) { - statements.add(statement); - result.add(new RecursiveMethodCallMarkerInfo(methodCall)); - } - } - } - } - - private static boolean isRecursiveMethodCall(@NotNull PsiMethodCallExpression methodCall) { - final PsiMethod referencedMethod = methodCall.resolveMethod(); - - if (referencedMethod == null || !referencedMethod.isValid() || !methodCall.isValid()) { - return false; - } - - final PsiFile methodCallFile = methodCall.getContainingFile(); - final PsiFile methodFile = referencedMethod.getContainingFile(); - - if (methodCallFile == null || methodFile == null || !methodCallFile.equals(methodFile)) { - return false; - } - - final TextRange rmRange = referencedMethod.getTextRange(); - final int mcOffset = methodCall.getTextRange().getStartOffset(); - return rmRange != null && rmRange.contains(mcOffset); - } - - private static class RecursiveMethodCallMarkerInfo extends LineMarkerInfo { - private RecursiveMethodCallMarkerInfo(@NotNull PsiMethodCallExpression methodCall) { - super(methodCall, - methodCall.getTextRange(), - RECURSIVE_METHOD_ICON, - Pass.UPDATE_OVERRIDEN_MARKERS, - FunctionUtil.constant("Recursive method call"), - null, - GutterIconRenderer.Alignment.RIGHT - ); - } - } -} diff --git a/java/java-impl/src/com/intellij/codeInsight/folding/impl/JavaCodeFoldingSettingsImpl.java b/java/java-impl/src/com/intellij/codeInsight/folding/impl/JavaCodeFoldingSettingsImpl.java index 918d1f365626..6801385d880f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/folding/impl/JavaCodeFoldingSettingsImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/folding/impl/JavaCodeFoldingSettingsImpl.java @@ -165,6 +165,16 @@ public class JavaCodeFoldingSettingsImpl extends JavaCodeFoldingSettings impleme COLLAPSE_END_OF_LINE_COMMENTS = value; } + @Override + public boolean isCollapseRecursiveMethodCalls() { + return COLLAPSE_RECURSIVE_METHOD_CALLS; + } + + @Override + public void setCollapseRecursiveMethodCalls(boolean value) { + COLLAPSE_RECURSIVE_METHOD_CALLS = value; + } + @SuppressWarnings({"WeakerAccess"}) public boolean COLLAPSE_ACCESSORS = false; @SuppressWarnings({"WeakerAccess"}) public boolean COLLAPSE_INNER_CLASSES = false; @SuppressWarnings({"WeakerAccess"}) public boolean COLLAPSE_ANONYMOUS_CLASSES = false; @@ -174,6 +184,7 @@ public class JavaCodeFoldingSettingsImpl extends JavaCodeFoldingSettings impleme @SuppressWarnings({"WeakerAccess"}) public boolean COLLAPSE_I18N_MESSAGES = true; @SuppressWarnings({"WeakerAccess"}) public boolean COLLAPSE_SUPPRESS_WARNINGS = true; @SuppressWarnings({"WeakerAccess"}) public boolean COLLAPSE_END_OF_LINE_COMMENTS = false; + @SuppressWarnings({"WeakerAccess"}) public boolean COLLAPSE_RECURSIVE_METHOD_CALLS = true; @Override @NotNull diff --git a/java/java-impl/src/com/intellij/codeInsight/folding/impl/RecursiveMethodCallFoldingBuilder.java b/java/java-impl/src/com/intellij/codeInsight/folding/impl/RecursiveMethodCallFoldingBuilder.java new file mode 100644 index 000000000000..b846f2ee6ecd --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/folding/impl/RecursiveMethodCallFoldingBuilder.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2012 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 com.intellij.codeInsight.folding.impl; + +import com.intellij.codeInsight.folding.JavaCodeFoldingSettings; +import com.intellij.lang.ASTNode; +import com.intellij.lang.folding.FoldingBuilderEx; +import com.intellij.lang.folding.FoldingDescriptor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.SourceTreeToPsiMap; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @author Danila Ponomarenko + */ +public class RecursiveMethodCallFoldingBuilder extends FoldingBuilderEx { + + @NotNull + @Override + public FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { + if (!(root instanceof PsiJavaFile) || quick || !JavaCodeFoldingSettings.getInstance().isCollapseRecursiveMethodCalls()) { + return FoldingDescriptor.EMPTY; + } + final List result = new ArrayList(); + root.accept(new JavaRecursiveElementWalkingVisitor() { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + if (isRecursiveMethodCall(expression)) { + result.add(new FoldingDescriptor(expression, expression.getTextRange())); + } + else { + super.visitMethodCallExpression(expression); + } + } + }); + + Collections.sort(result, new Comparator() { + @Override + public int compare(FoldingDescriptor o1, FoldingDescriptor o2) { + return o2.getRange().getStartOffset() - o1.getRange().getStartOffset(); + } + }); + + return result.toArray(new FoldingDescriptor[result.size()]); + } + + @Override + public boolean isCollapsedByDefault(@NotNull ASTNode node) { + final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(node); + JavaCodeFoldingSettings settings = JavaCodeFoldingSettings.getInstance(); + + if (element instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)element; + return isRecursiveMethodCall(methodCall) && settings.isCollapseRecursiveMethodCalls(); + } + return false; + } + + private static final char ANTICLOCKWISE_GAPPED_CIRCLE_ARROW = '\u27F2'; //⟲ + + @Override + public String getPlaceholderText(@NotNull ASTNode node) { + return getPlaceholderText(node.getPsi()); + } + + @NotNull + private static String getPlaceholderText(@NotNull PsiElement element) { + if (element instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)element; + return getPlaceholderText(methodCall); + } + + return element.getText(); + } + + @NotNull + private static String getPlaceholderText(@NotNull PsiMethodCallExpression methodCall) { + return getQualifierText(methodCall) + ANTICLOCKWISE_GAPPED_CIRCLE_ARROW + methodCall.getArgumentList().getText(); + } + + @NotNull + private static String getQualifierText(@NotNull PsiMethodCallExpression methodCall) { + final PsiElement qualifier = methodCall.getMethodExpression().getQualifier(); + return qualifier != null ? qualifier.getText() + "." : ""; + } + + private static boolean isRecursiveMethodCall(@NotNull PsiMethodCallExpression methodCall) { + final PsiMethod referencedMethod = methodCall.resolveMethod(); + + if (referencedMethod == null || !referencedMethod.isValid() || !methodCall.isValid()) { + return false; + } + + final PsiFile methodCallFile = methodCall.getContainingFile(); + final PsiFile methodFile = referencedMethod.getContainingFile(); + + if (methodCallFile == null || methodFile == null || !methodCallFile.equals(methodFile)) { + return false; + } + + final TextRange rmRange = referencedMethod.getTextRange(); + final int mcOffset = methodCall.getTextRange().getStartOffset(); + return rmRange != null && rmRange.contains(mcOffset); + } +} diff --git a/java/openapi/src/com/intellij/codeInsight/folding/JavaCodeFoldingSettings.java b/java/openapi/src/com/intellij/codeInsight/folding/JavaCodeFoldingSettings.java index 585ce02a81f6..f0325b5506f4 100644 --- a/java/openapi/src/com/intellij/codeInsight/folding/JavaCodeFoldingSettings.java +++ b/java/openapi/src/com/intellij/codeInsight/folding/JavaCodeFoldingSettings.java @@ -62,4 +62,7 @@ public abstract class JavaCodeFoldingSettings { public abstract boolean isCollapseEndOfLineComments(); public abstract void setCollapseEndOfLineComments(boolean value); + + public abstract boolean isCollapseRecursiveMethodCalls(); + public abstract void setCollapseRecursiveMethodCalls(boolean value); } diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index c8f43e12f7c8..145f5ab1b536 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -348,6 +348,7 @@ checkbox.collapse.method.bodies=Method bodies checkbox.collapse.javadoc.comments=Documentation comments checkbox.collapse.title.imports=Imports checkbox.collapse.file.header=File header +checkbox.collapse.recursive.method.calls=Recursive method calls label.collapse.by.default=Collapse by default: checkbox.show.code.folding.outline=Show code folding outline group.tab.appearance=Tab Appearance diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index a2400794960f..6d54f6c7e979 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -749,6 +749,7 @@ + @@ -761,7 +762,6 @@ -