From 3b2dfb2619bd62f65756ce02cf481cf00e277deb Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 3 Aug 2016 19:38:16 +0300 Subject: [PATCH] literal arguments folding: fold one char instead of the whole literal; folding in case of generic method fixed; Settings|Editor|General|Code Folding|Literal arguments|Minimum arguments to fold and Minimum parameter name length settings added --- .../JavaCodeFoldingOptionsProvider.java | 53 +++++++-- .../editor/JavaCodeFoldingSettingsPanel.form | 83 ++++++++++++++ .../editor/JavaCodeFoldingSettingsPanel.java | 64 +++++++++++ .../folding/JavaCodeFoldingSettings.java | 7 ++ .../impl/JavaCodeFoldingSettingsBase.java | 49 +++++++-- .../folding/impl/JavaFoldingBuilderBase.java | 5 +- .../impl/ParameterNameFoldingManager.java | 98 +++++++++-------- .../folding/JavaFoldingTest.groovy | 102 +++++++++++------- .../openapi/options/BeanConfigurable.java | 79 +++++++++++++- .../com/intellij/openapi/util/annotations.xml | 3 + .../src/com/intellij/openapi/util/Setter.java | 20 ++++ 11 files changed, 452 insertions(+), 111 deletions(-) create mode 100644 java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingSettingsPanel.form create mode 100644 java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingSettingsPanel.java create mode 100644 platform/util-rt/src/com/intellij/openapi/util/Setter.java 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 7a9aed06479c..0c78591db4ea 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 @@ -27,16 +27,47 @@ import com.intellij.openapi.options.BeanConfigurable; public class JavaCodeFoldingOptionsProvider extends BeanConfigurable implements CodeFoldingOptionsProvider { public JavaCodeFoldingOptionsProvider() { super(JavaCodeFoldingSettings.getInstance()); - checkBox("INLINE_PARAMETER_NAMES_FOR_LITERAL_CALL_ARGUMENTS", ApplicationBundle.message("checkbox.collapse.boolean.parameters")); - checkBox("COLLAPSE_ONE_LINE_METHODS", ApplicationBundle.message("checkbox.collapse.one.line.methods")); - checkBox("COLLAPSE_ACCESSORS", ApplicationBundle.message("checkbox.collapse.simple.property.accessors")); - checkBox("COLLAPSE_INNER_CLASSES", ApplicationBundle.message("checkbox.collapse.inner.classes")); - checkBox("COLLAPSE_ANONYMOUS_CLASSES", ApplicationBundle.message("checkbox.collapse.anonymous.classes")); - checkBox("COLLAPSE_ANNOTATIONS", ApplicationBundle.message("checkbox.collapse.annotations")); - checkBox("COLLAPSE_CLOSURES", ApplicationBundle.message("checkbox.collapse.closures")); - checkBox("COLLAPSE_CONSTRUCTOR_GENERIC_PARAMETERS", ApplicationBundle.message("checkbox.collapse.generic.constructor.parameters")); - checkBox("COLLAPSE_I18N_MESSAGES", ApplicationBundle.message("checkbox.collapse.i18n.messages")); - checkBox("COLLAPSE_SUPPRESS_WARNINGS", ApplicationBundle.message("checkbox.collapse.suppress.warnings")); - checkBox("COLLAPSE_END_OF_LINE_COMMENTS", ApplicationBundle.message("checkbox.collapse.end.of.line.comments")); + JavaCodeFoldingSettings settings = getInstance(); + checkBox(ApplicationBundle.message("checkbox.collapse.boolean.parameters"), settings::isInlineParameterNamesForLiteralCallArguments, settings::setInlineParameterNamesForLiteralCallArguments); + JavaCodeFoldingSettingsPanel panel = new JavaCodeFoldingSettingsPanel(); + + component(panel.getRow0(), + () -> null, + pair -> {}, + () -> null, + pair -> {} + ); + component(panel.getRow1(), + settings::getInlineLiteralParameterMinNameLength, + settings::setInlineLiteralParameterMinNameLength, + panel::getMinNameLengthThresholdText, + panel::setMinNameLengthThresholdText + ); + component(panel.getRow2(), + settings::getInlineLiteralParameterMinArgumentsToFold, + settings::setInlineLiteralParameterMinArgumentsToFold, + panel::getMinArgumentsToFoldText, + panel::setMinArgumentsToFoldText + ); + + checkBox(ApplicationBundle.message("checkbox.collapse.one.line.methods"), settings::isCollapseOneLineMethods, settings::setCollapseOneLineMethods); + + checkBox(ApplicationBundle.message("checkbox.collapse.simple.property.accessors"), settings::isCollapseAccessors, settings::setCollapseAccessors); + + checkBox(ApplicationBundle.message("checkbox.collapse.inner.classes"), settings::isCollapseInnerClasses, settings::setCollapseInnerClasses); + + checkBox(ApplicationBundle.message("checkbox.collapse.anonymous.classes"), settings::isCollapseAnonymousClasses, settings::setCollapseAnonymousClasses); + + checkBox(ApplicationBundle.message("checkbox.collapse.annotations"), settings::isCollapseAnnotations, settings::setCollapseAnnotations); + + checkBox(ApplicationBundle.message("checkbox.collapse.closures"), settings::isCollapseLambdas, settings::setCollapseLambdas); + + checkBox(ApplicationBundle.message("checkbox.collapse.generic.constructor.parameters"), settings::isCollapseConstructorGenericParameters, settings::setCollapseConstructorGenericParameters); + + checkBox(ApplicationBundle.message("checkbox.collapse.i18n.messages"), settings::isCollapseI18nMessages, settings::setCollapseI18nMessages); + + checkBox(ApplicationBundle.message("checkbox.collapse.suppress.warnings"), settings::isCollapseSuppressWarnings, settings::setCollapseSuppressWarnings); + + checkBox(ApplicationBundle.message("checkbox.collapse.end.of.line.comments"), settings::isCollapseEndOfLineComments, settings::setCollapseEndOfLineComments); } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingSettingsPanel.form b/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingSettingsPanel.form new file mode 100644 index 000000000000..1f6df57e3de0 --- /dev/null +++ b/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingSettingsPanel.form @@ -0,0 +1,83 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingSettingsPanel.java b/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingSettingsPanel.java new file mode 100644 index 000000000000..802a174dd014 --- /dev/null +++ b/java/java-impl/src/com/intellij/application/options/editor/JavaCodeFoldingSettingsPanel.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2016 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.application.options.editor; + +import javax.swing.*; + +public class JavaCodeFoldingSettingsPanel { + private JTextField minNameLengthThresholdText; + private JTextField minArgumentsToFoldText; + private JPanel row0; + private JPanel row1; + private JPanel row2; + + int getMinNameLengthThresholdText() { + try { + return Integer.parseInt(minNameLengthThresholdText.getText()); + } + catch (NumberFormatException e) { + return 3; + } + } + + void setMinNameLengthThresholdText(int value) { + minNameLengthThresholdText.setText(String.valueOf(value)); + } + + int getMinArgumentsToFoldText() { + try { + return Integer.parseInt(minArgumentsToFoldText.getText()); + } + catch (NumberFormatException e) { + return 2; + } + } + + void setMinArgumentsToFoldText(int value) { + minArgumentsToFoldText.setText(String.valueOf(value)); + } + + JComponent getRow0() { + return row0; + } + + JPanel getRow1() { + return row1; + } + + JPanel getRow2() { + return row2; + } +} diff --git a/java/java-psi-api/src/com/intellij/codeInsight/folding/JavaCodeFoldingSettings.java b/java/java-psi-api/src/com/intellij/codeInsight/folding/JavaCodeFoldingSettings.java index 5e45853f2f0f..25b33e690cf2 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/folding/JavaCodeFoldingSettings.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/folding/JavaCodeFoldingSettings.java @@ -40,6 +40,7 @@ public abstract class JavaCodeFoldingSettings { public abstract void setCollapseAccessors(boolean value); public abstract boolean isCollapseOneLineMethods(); + public abstract void setCollapseOneLineMethods(boolean value); public abstract boolean isCollapseInnerClasses(); public abstract void setCollapseInnerClasses(boolean value); @@ -67,4 +68,10 @@ public abstract class JavaCodeFoldingSettings { public abstract boolean isInlineParameterNamesForLiteralCallArguments(); public abstract void setInlineParameterNamesForLiteralCallArguments(boolean value); + + public abstract int getInlineLiteralParameterMinNameLength(); + public abstract void setInlineLiteralParameterMinNameLength(int value); + + public abstract int getInlineLiteralParameterMinArgumentsToFold(); + public abstract void setInlineLiteralParameterMinArgumentsToFold(int value); } diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaCodeFoldingSettingsBase.java b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaCodeFoldingSettingsBase.java index b20fdf5a5fa8..eeeb5b3cc93c 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaCodeFoldingSettingsBase.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaCodeFoldingSettingsBase.java @@ -19,17 +19,19 @@ import com.intellij.codeInsight.folding.CodeFoldingSettings; import com.intellij.codeInsight.folding.JavaCodeFoldingSettings; public class JavaCodeFoldingSettingsBase extends JavaCodeFoldingSettings { - @SuppressWarnings("WeakerAccess") public boolean COLLAPSE_ACCESSORS; - @SuppressWarnings("WeakerAccess") public boolean COLLAPSE_ONE_LINE_METHODS = true; - @SuppressWarnings("WeakerAccess") public boolean COLLAPSE_INNER_CLASSES; - @SuppressWarnings("WeakerAccess") public boolean COLLAPSE_ANONYMOUS_CLASSES; - @SuppressWarnings("WeakerAccess") public boolean COLLAPSE_ANNOTATIONS; - @SuppressWarnings("WeakerAccess") public boolean COLLAPSE_CLOSURES = true; - @SuppressWarnings("WeakerAccess") public boolean COLLAPSE_CONSTRUCTOR_GENERIC_PARAMETERS = true; - @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; - @SuppressWarnings("WeakerAccess") public boolean INLINE_PARAMETER_NAMES_FOR_LITERAL_CALL_ARGUMENTS; + private boolean COLLAPSE_ACCESSORS; + private boolean COLLAPSE_ONE_LINE_METHODS = true; + private boolean COLLAPSE_INNER_CLASSES; + private boolean COLLAPSE_ANONYMOUS_CLASSES; + private boolean COLLAPSE_ANNOTATIONS; + private boolean COLLAPSE_CLOSURES = true; + private boolean COLLAPSE_CONSTRUCTOR_GENERIC_PARAMETERS = true; + private boolean COLLAPSE_I18N_MESSAGES = true; + private boolean COLLAPSE_SUPPRESS_WARNINGS = true; + private boolean COLLAPSE_END_OF_LINE_COMMENTS; + private boolean INLINE_PARAMETER_NAMES_FOR_LITERAL_CALL_ARGUMENTS; + private int INLINE_LITERAL_PARAMETER_MIN_NAME_LENGTH = 3; + private int INLINE_LITERAL_PARAMETER_MIN_ARGS_TO_FOLD = 2; @Override public boolean isCollapseImports() { @@ -85,6 +87,11 @@ public class JavaCodeFoldingSettingsBase extends JavaCodeFoldingSettings { return COLLAPSE_ONE_LINE_METHODS; } + @Override + public void setCollapseOneLineMethods(boolean value) { + COLLAPSE_ONE_LINE_METHODS = value; + } + @Override public boolean isCollapseInnerClasses() { return COLLAPSE_INNER_CLASSES; @@ -174,4 +181,24 @@ public class JavaCodeFoldingSettingsBase extends JavaCodeFoldingSettings { public void setInlineParameterNamesForLiteralCallArguments(boolean value) { INLINE_PARAMETER_NAMES_FOR_LITERAL_CALL_ARGUMENTS = value; } + + @Override + public int getInlineLiteralParameterMinNameLength() { + return INLINE_LITERAL_PARAMETER_MIN_NAME_LENGTH; + } + + @Override + public void setInlineLiteralParameterMinNameLength(int value) { + INLINE_LITERAL_PARAMETER_MIN_NAME_LENGTH = value; + } + + @Override + public int getInlineLiteralParameterMinArgumentsToFold() { + return INLINE_LITERAL_PARAMETER_MIN_ARGS_TO_FOLD; + } + + @Override + public void setInlineLiteralParameterMinArgumentsToFold(int value) { + INLINE_LITERAL_PARAMETER_MIN_ARGS_TO_FOLD = value; + } } diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java index 0b06c9168b16..fc0431551602 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java @@ -807,13 +807,12 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem private static void inlineLiteralArgumentsNames(@NotNull PsiCallExpression expression, @NotNull List foldElements, - boolean quick) - { + boolean quick) { if (quick || !JavaCodeFoldingSettings.getInstance().isInlineParameterNamesForLiteralCallArguments()) { return; } ParameterNameFoldingManager manager = new ParameterNameFoldingManager(expression); - foldElements.addAll(manager.buildDescriptors()); + foldElements.addAll(manager.getDescriptors()); } private boolean addClosureFolding(@NotNull PsiClass aClass, diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/ParameterNameFoldingManager.java b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/ParameterNameFoldingManager.java index a8b2b40111ba..bddbe0c37c2d 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/ParameterNameFoldingManager.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/ParameterNameFoldingManager.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.folding.impl; +import com.intellij.codeInsight.folding.JavaCodeFoldingSettings; import com.intellij.lang.folding.FoldingDescriptor; import com.intellij.lang.folding.NamedFoldingDescriptor; import com.intellij.openapi.util.Couple; @@ -27,12 +28,11 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -public class ParameterNameFoldingManager { - private static final int MIN_NAME_LENGTH_THRESHOLD = 3; - private static final int MIN_ARGS_TO_FOLD = 2; - +class ParameterNameFoldingManager { private static final List> COMMONLY_USED_PARAMETER_PAIR = ContainerUtil.newArrayList( Couple.of("begin", "end"), Couple.of("start", "end"), @@ -43,16 +43,27 @@ public class ParameterNameFoldingManager { Couple.of("min", "max") ); - private final PsiCallExpression myCallExpression; + @NotNull + private final List myDescriptors; - private PsiExpression[] myCallArguments; - private PsiParameter[] myParameters; + ParameterNameFoldingManager(@NotNull PsiCallExpression callExpression) { + PsiExpression[] callArguments = getArguments(callExpression); + JavaResolveResult resolveResult = callExpression.resolveMethodGenerics(); - public ParameterNameFoldingManager(@NotNull PsiCallExpression callExpression) { - myCallExpression = callExpression; + JavaCodeFoldingSettings settings = JavaCodeFoldingSettings.getInstance(); + List descriptors = Collections.emptyList(); + if (callArguments.length >= settings.getInlineLiteralParameterMinArgumentsToFold() && + hasLiteralExpression(callArguments) && + resolveResult.getElement() instanceof PsiMethod) { + PsiMethod method = (PsiMethod)resolveResult.getElement(); + PsiParameter[] parameters = method.getParameterList().getParameters(); + descriptors = buildDescriptorsForLiteralArguments(callArguments, parameters, resolveResult); + } + + myDescriptors = descriptors; } - public static boolean isLiteralExpression(@Nullable PsiElement callArgument) { + static boolean isLiteralExpression(@Nullable PsiElement callArgument) { if (callArgument instanceof PsiLiteralExpression) return true; @@ -66,43 +77,32 @@ public class ParameterNameFoldingManager { return false; } - @Nullable - public PsiExpression[] getArguments(@NotNull PsiCallExpression call) { + @NotNull + private static PsiExpression[] getArguments(@NotNull PsiCallExpression call) { PsiExpressionList callArgumentsList = call.getArgumentList(); - return callArgumentsList != null ? callArgumentsList.getExpressions() : null; + return callArgumentsList == null ? PsiExpression.EMPTY_ARRAY : callArgumentsList.getExpressions(); } @NotNull - public List buildDescriptors() { - myCallArguments = getArguments(myCallExpression); - - if (myCallArguments != null && myCallArguments.length >= MIN_ARGS_TO_FOLD && hasLiteralExpression(myCallArguments)) { - PsiMethod method = myCallExpression.resolveMethod(); - - if (method != null) { - myParameters = method.getParameterList().getParameters(); - if (myParameters.length == myCallArguments.length) { - return buildDescriptorsForLiteralArguments(); - } - } - } - - return ContainerUtil.emptyList(); + List getDescriptors() { + return myDescriptors; } @NotNull - private List buildDescriptorsForLiteralArguments() { - List descriptors = ContainerUtil.newArrayList(); + private static List buildDescriptorsForLiteralArguments(@NotNull PsiExpression[] callArguments, + @NotNull PsiParameter[] parameters, + @NotNull JavaResolveResult resolveResult) { + List descriptors = new ArrayList(); int i = 0; - while (i < myCallArguments.length) { - if (i + 1 < myCallArguments.length && isCommonlyNamedParameterPair(i, i + 1)) { + while (i < callArguments.length && i < parameters.length) { + if (i + 1 < callArguments.length && isCommonlyNamedParameterPair(i, i + 1, parameters)) { i += 2; continue; } - if (shouldInlineParameterName(i)) { - descriptors.add(createFoldingDescriptor(myCallArguments[i], myParameters[i])); + if (!(parameters[i].getType() instanceof PsiEllipsisType) && shouldInlineParameterName(i, callArguments, parameters, resolveResult)) { + descriptors.add(createFoldingDescriptor(callArguments[i], parameters[i])); } i++; } @@ -112,16 +112,17 @@ public class ParameterNameFoldingManager { @NotNull private static NamedFoldingDescriptor createFoldingDescriptor(@NotNull PsiExpression callArgument, @NotNull PsiParameter methodParam) { - TextRange range = callArgument.getTextRange(); - String placeholderText = methodParam.getName() + ": " + callArgument.getText(); - return new NamedFoldingDescriptor(callArgument, range.getStartOffset(), range.getEndOffset(), null, placeholderText); + PsiElement lParenOrCommaOrWhitespaceOrComment = callArgument.getPrevSibling(); + TextRange range = lParenOrCommaOrWhitespaceOrComment.getTextRange(); + String placeholderText = StringUtil.last(lParenOrCommaOrWhitespaceOrComment.getText(), 1, false) + methodParam.getName() + ": "; + return new NamedFoldingDescriptor(callArgument, range.getEndOffset()-1, range.getEndOffset(), null, placeholderText); } - private boolean isCommonlyNamedParameterPair(int first, int second) { - assert first < myParameters.length && second < myParameters.length; + private static boolean isCommonlyNamedParameterPair(int first, int second, PsiParameter[] parameters) { + if (!(first < parameters.length && second < parameters.length)) return false; - String firstParamName = myParameters[first].getName(); - String secondParamName = myParameters[second].getName(); + String firstParamName = parameters[first].getName(); + String secondParamName = parameters[second].getName(); if (firstParamName == null || secondParamName == null) return false; for (Couple knownPair : COMMONLY_USED_PARAMETER_PAIR) { @@ -134,13 +135,18 @@ public class ParameterNameFoldingManager { return false; } - private boolean shouldInlineParameterName(int paramIndex) { - PsiExpression argument = myCallArguments[paramIndex]; + private static boolean shouldInlineParameterName(int paramIndex, + @NotNull PsiExpression[] callArguments, + @NotNull PsiParameter[] parameters, + @NotNull JavaResolveResult resolveResult) { + PsiExpression argument = callArguments[paramIndex]; if (isLiteralExpression(argument) && argument.getType() != null) { - PsiParameter parameter = myParameters[paramIndex]; + PsiParameter parameter = parameters[paramIndex]; String paramName = parameter.getName(); - if (paramName != null && paramName.length() >= MIN_NAME_LENGTH_THRESHOLD) { - return TypeConversionUtil.isAssignable(parameter.getType(), argument.getType()); + JavaCodeFoldingSettings settings = JavaCodeFoldingSettings.getInstance(); + if (paramName != null && paramName.length() >= settings.getInlineLiteralParameterMinNameLength()) { + PsiType parameterType = resolveResult.getSubstitutor().substitute(parameter.getType()); + return TypeConversionUtil.isAssignable(parameterType, argument.getType()); } } return false; diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy index 1e58ac71c79f..176df15c8b2c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy @@ -31,7 +31,6 @@ import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider import com.intellij.openapi.util.registry.Registry import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiClass -import com.intellij.psi.PsiLiteralExpression import com.intellij.psi.PsiMethod import com.intellij.psi.search.GlobalSearchScope import com.intellij.testFramework.EditorTestUtil @@ -687,15 +686,13 @@ class Test { def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } assert regions.size() == 9 - def literals = fooClass.methods[0].body.statements[6].children[0].children[1].children.findAll { it instanceof PsiLiteralExpression } - def parameters = fooClass.methods[1].parameterList.parameters - - for (int i = 0; i < literals.size(); i++) { - def currentElement = literals[i] - def correspondingFolding = regions[i + 1] - assert correspondingFolding.startOffset == currentElement.textRange.startOffset && correspondingFolding.endOffset == currentElement.textRange.endOffset - assert correspondingFolding.placeholderText == parameters[i].name + ": " + currentElement.text - } + checkTheFoldingStartsRightBefore(regions[1], text, "(testNow: ", "true") + checkTheFoldingStartsRightBefore(regions[2], text, " shouldIgnoreRoots: ", "false") + checkTheFoldingStartsRightBefore(regions[3], text, " times: ", "555") + checkTheFoldingStartsRightBefore(regions[4], text, " pii: ", "3.141f") + checkTheFoldingStartsRightBefore(regions[5], text, " title: ", '"Huge Title"') + checkTheFoldingStartsRightBefore(regions[6], text, " terminate: ", "'c'") + checkTheFoldingStartsRightBefore(regions[7], text, " file: ", "null") } public void "test do not inline name if setter"() { @@ -738,8 +735,7 @@ public class VarArgTest { configure text def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } assert regions.size() == 3 - checkRangeOffsetByPositionInText(regions[1], text, "13") - assert regions[1].placeholderText == "test: 13" + checkTheFoldingStartsRightBefore(regions[1], text, "(test: ", "13") } public void "test do not inline if parameter length is one or two"() { @@ -857,14 +853,11 @@ public class CharSymbol { def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } assert regions.size() == 5 - checkRangeOffsetByPositionInText(regions[1], text, "100") - assert regions[1].placeholderText == "test: 100" + checkTheFoldingStartsRightBefore(regions[1], text, "(test: ", "100") - checkRangeOffsetByPositionInText(regions[2], text, "false") - assert regions[2].placeholderText == "boo: false" + checkTheFoldingStartsRightBefore(regions[2], text, " boo: ", "false") - checkRangeOffsetByPositionInText(regions[3], text, '"Hi!"') - assert regions[3].placeholderText == 'seq: "Hi!"' + checkTheFoldingStartsRightBefore(regions[3], text, " seq: ", '"Hi!"') } public void "test inline negative and positive numbers"() { @@ -890,11 +883,53 @@ public class CharSymbol { def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } assert regions.size() == 4 - checkRangeOffsetByPositionInText(regions[1], text, "-1") - assert regions[1].placeholderText == "test: -1" + checkTheFoldingStartsRightBefore(regions[1], text, "(test: ", "-1") + checkTheFoldingStartsRightBefore(regions[2], text, "(test: ", "+1") + } - checkRangeOffsetByPositionInText(regions[2], text, "+1") - assert regions[2].placeholderText == "test: +1" + public void "test inline literal arguments with crazy settings"() { + myFoldingSettings.setInlineParameterNamesForLiteralCallArguments(true); + myFoldingSettings.setInlineLiteralParameterMinArgumentsToFold(1); + myFoldingSettings.setInlineLiteralParameterMinNameLength(1); + @Language("JAVA") + def text = """ +public class Test { + public void main(boolean isActive, boolean requestFocus, int xoo) { + System.out.println("AAA"); + main(true,false, /*comment*/2); + } +} +""" + configure text + def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } + + checkTheFoldingStartsRightBefore(regions[1], text, "(x: ", '"AAA"') + checkTheFoldingStartsRightBefore(regions[2], text, "(isActive: ", "true") + checkTheFoldingStartsRightBefore(regions[3], text, ",requestFocus: ", "false") + checkTheFoldingStartsRightBefore(regions[4], text, "/xoo: ", "2") + } + + public void "test inline literal arguments with generics"() { + myFoldingSettings.setInlineParameterNamesForLiteralCallArguments(true); + myFoldingSettings.setInlineLiteralParameterMinArgumentsToFold(1); + myFoldingSettings.setInlineLiteralParameterMinNameLength(1); + @Language("JAVA") + def text = """ +import java.util.*; +public class Test { + public void main(Comparator c, List l) { + c.compare(0, /** ddd */3); + l.add(1, "uuu"); + } +} +""" + configure text + def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } + + checkTheFoldingStartsRightBefore(regions[1], text, "(o1: ", '0') + checkTheFoldingStartsRightBefore(regions[2], text, "/o2: ", "3") + checkTheFoldingStartsRightBefore(regions[3], text, "(index: ", "1") + checkTheFoldingStartsRightBefore(regions[4], text, " element: ", '"uuu"') } public void "test inline constructor literal arguments names"() { @@ -922,11 +957,14 @@ public class Test { def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } assert regions.length == 6 - assert regions[1].placeholderText == "isActive: true" - assert regions[2].placeholderText == "requestFocus: false" + checkTheFoldingStartsRightBefore(regions[1], text, "(isActive: ", "true") + checkTheFoldingStartsRightBefore(regions[2], text, " requestFocus: ", "false") + } - checkRangeOffsetByPositionInText(regions[1], text, "true") - checkRangeOffsetByPositionInText(regions[2], text, "false") + private static def checkTheFoldingStartsRightBefore(FoldRegion region, String fileText, String placeHolder, String fileTextAfterFold) { + assert region.endOffset == fileText.indexOf(fileTextAfterFold) + assert region.startOffset == region.endOffset - 1 + assert region.placeholderText == placeHolder : "expected placeholder text: "+placeHolder+"; but got: "+region.placeholderText } public void "test inline anonymous class constructor literal arguments names"() { @@ -951,18 +989,10 @@ public class Test { def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } assert regions.length == 4 - assert regions[2].placeholderText == "counter: 10" - assert regions[3].placeholderText == "shouldTest: false" - - checkRangeOffsetByPositionInText(regions[2], text, "10") - checkRangeOffsetByPositionInText(regions[3], text, "false") + checkTheFoldingStartsRightBefore(regions[2], text, "(counter: ", "10") + checkTheFoldingStartsRightBefore(regions[3], text, " shouldTest: ", "false") } - private static def checkRangeOffsetByPositionInText(FoldRegion region, String text, String foldElement) { - assert region.startOffset == text.indexOf(foldElement) && region.endOffset == text.indexOf(foldElement) + foldElement.length() - } - - private def changeFoldRegions(Closure op) { myFixture.editor.foldingModel.runBatchFoldingOperationDoNotCollapseCaret(op) } diff --git a/platform/platform-api/src/com/intellij/openapi/options/BeanConfigurable.java b/platform/platform-api/src/com/intellij/openapi/options/BeanConfigurable.java index d554bc501452..52b98804324d 100644 --- a/platform/platform-api/src/com/intellij/openapi/options/BeanConfigurable.java +++ b/platform/platform-api/src/com/intellij/openapi/options/BeanConfigurable.java @@ -16,6 +16,8 @@ package com.intellij.openapi.options; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.Setter; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -48,26 +50,27 @@ public abstract class BeanConfigurable implements UnnamedConfigurable { return myComponent; } + @NotNull abstract T createComponent(); - boolean isModified(Object instance) { + boolean isModified(@NotNull Object instance) { final Object componentValue = getComponentValue(); final Object beanValue = getBeanValue(instance); return !Comparing.equal(componentValue, beanValue); } - void apply(Object instance) { + void apply(@NotNull Object instance) { setBeanValue(instance, getComponentValue()); } - void reset(Object instance) { + void reset(@NotNull Object instance) { setComponentValue(getBeanValue(instance)); } abstract Object getComponentValue(); abstract void setComponentValue(Object value); - Object getBeanValue(Object instance) { + Object getBeanValue(@NotNull Object instance) { try { Field field = instance.getClass().getField(myFieldName); return field.get(instance); @@ -121,6 +124,7 @@ public abstract class BeanConfigurable implements UnnamedConfigurable { myTitle = title; } + @NotNull @Override JCheckBox createComponent() { return new JCheckBox(myTitle); @@ -153,14 +157,81 @@ public abstract class BeanConfigurable implements UnnamedConfigurable { myInstance = beanInstance; } + @NotNull protected T getInstance() { return myInstance; } + /** + * @deprecated use {@link #checkBox(String, Getter, Setter)} instead + */ protected void checkBox(@NonNls String fieldName, String title) { myFields.add(new CheckboxField(fieldName, title)); } + /** + * Adds check box with given {@code title}. + * Initial checkbox value is obtained from {@code getter}. + * After the apply, the value from the check box is written back to model via {@code setter}. + */ + protected void checkBox(@NotNull String title, @NotNull Getter getter, @NotNull Setter setter) { + CheckboxField field = new CheckboxField("", title) { + @Override + Object getBeanValue(@NotNull Object instance) { + return getter.get(); + } + + @Override + void setBeanValue(Object instance, Object value) { + setter.set((Boolean)value); + } + }; + myFields.add(field); + } + + /** + * Adds custom component (e.g. edit box). + * Initial value is obtained from {@code beanGetter} and applied to the component via {@code componentSetter}. + * E.g. text is read from the model and set to the edit box. + * After the apply, the value from the component is queried via {@code componentGetter} and written back to model via {@code beanSetter}. + * E.g. text from the edit box is queried and saved back to model bean. + */ + protected void component(@NotNull JComponent component, @NotNull Getter beanGetter, @NotNull Setter beanSetter, @NotNull Getter componentGetter, @NotNull Setter componentSetter) { + BeanField field = new BeanField("") { + @NotNull + @Override + JComponent createComponent() { + return component; + } + + @Override + Object getComponentValue() { + return componentGetter.get(); + } + + @Override + void setComponentValue(Object value) { + componentSetter.set((V)value); + } + + @Override + protected Class getValueClass() { + return null; + } + + @Override + Object getBeanValue(@NotNull Object instance) { + return beanGetter.get(); + } + + @Override + void setBeanValue(Object instance, Object value) { + beanSetter.set((V)value); + } + }; + myFields.add(field); + } + @Override public JComponent createComponent() { final JPanel panel = new JPanel(new GridLayout(myFields.size(), 1)); diff --git a/platform/util-rt/anno/com/intellij/openapi/util/annotations.xml b/platform/util-rt/anno/com/intellij/openapi/util/annotations.xml index 1cd1344927fc..d177b0217baf 100644 --- a/platform/util-rt/anno/com/intellij/openapi/util/annotations.xml +++ b/platform/util-rt/anno/com/intellij/openapi/util/annotations.xml @@ -6,5 +6,8 @@ + + + diff --git a/platform/util-rt/src/com/intellij/openapi/util/Setter.java b/platform/util-rt/src/com/intellij/openapi/util/Setter.java new file mode 100644 index 000000000000..97a79f6c21e2 --- /dev/null +++ b/platform/util-rt/src/com/intellij/openapi/util/Setter.java @@ -0,0 +1,20 @@ +/* + * Copyright 2000-2016 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.openapi.util; + +public interface Setter { + void set(T value); +}