parameter name folding reducing:

- do not inline if parameter length is less than 3
- do not inline if two subsequent parameters has interval names (beginIndex, endIndex; startId, endId; from, to...)
This commit is contained in:
Yaroslav Lepenkin
2014-07-22 10:09:55 +04:00
parent 90d58bef51
commit 73dfd94582
3 changed files with 202 additions and 62 deletions
@@ -45,7 +45,6 @@ import com.intellij.psi.util.*;
import com.intellij.util.Function;
import com.intellij.util.ObjectUtils;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -701,7 +700,7 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem
else if (element instanceof PsiComment) {
return settings.isCollapseEndOfLineComments();
}
else if (isLiteralExpression(element)
else if (ParameterNameFoldingManager.isLiteralExpression(element)
&& element.getParent() instanceof PsiExpressionList
&& (element.getParent().getParent() instanceof PsiCallExpression
|| element.getParent().getParent() instanceof PsiAnonymousClass)) {
@@ -760,55 +759,8 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem
if (quick || !JavaCodeFoldingSettings.getInstance().isInlineParameterNamesForLiteralCallArguments()) {
return;
}
PsiExpressionList callArgumentsList = expression.getArgumentList();
if (callArgumentsList == null) {
return;
}
PsiExpression[] callArguments = callArgumentsList.getExpressions();
if (callArguments.length > 1) {
PsiParameter[] parameters = null;
boolean isResolved = false;
for (int i = 0; i < callArguments.length; i++) {
PsiExpression callArgument = callArguments[i];
if (callArgument.getType() != null && isLiteralExpression(callArgument)) {
if (!isResolved) {
PsiMethod method = expression.resolveMethod();
isResolved = true;
if (method == null) {
return;
}
parameters = method.getParameterList().getParameters();
if (parameters.length != callArguments.length) {
return;
}
}
PsiParameter methodParam = parameters[i];
if (TypeConversionUtil.isAssignable(methodParam.getType(), callArgument.getType())) {
TextRange range = callArgument.getTextRange();
String placeholderText = methodParam.getName() + ": " + callArgument.getText();
foldElements.add(new NamedFoldingDescriptor(callArgument, range.getStartOffset(), range.getEndOffset(), null, placeholderText));
}
}
}
}
}
@Contract("null -> false")
private static boolean isLiteralExpression(@Nullable PsiElement callArgument) {
if (callArgument instanceof PsiLiteralExpression)
return true;
if (callArgument instanceof PsiPrefixExpression) {
PsiPrefixExpression expr = (PsiPrefixExpression)callArgument;
IElementType tokenType = expr.getOperationTokenType();
return JavaTokenType.MINUS.equals(tokenType) && expr.getOperand() instanceof PsiLiteralExpression;
}
return false;
ParameterNameFoldingManager manager = new ParameterNameFoldingManager(expression);
foldElements.addAll(manager.buildDescriptors());
}
private boolean addClosureFolding(final PsiClass aClass, final Document document, final List<FoldingDescriptor> foldElements,
@@ -0,0 +1,154 @@
/*
* 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 com.intellij.codeInsight.folding.impl;
import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.lang.folding.NamedFoldingDescriptor;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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;
private static final String[] RANGE_START_WORDS = {
"begin", "start", "from", "first"
};
private static final String[] RANGE_END_WORDS = {
"end", "to", "last"
};
private final PsiCallExpression myCallExpression;
private PsiExpression[] myCallArguments;
private PsiParameter[] myParameters;
public ParameterNameFoldingManager(@NotNull PsiCallExpression callExpression) {
myCallExpression = callExpression;
}
public static boolean isLiteralExpression(@Nullable PsiElement callArgument) {
if (callArgument instanceof PsiLiteralExpression)
return true;
if (callArgument instanceof PsiPrefixExpression) {
PsiPrefixExpression expr = (PsiPrefixExpression)callArgument;
IElementType tokenType = expr.getOperationTokenType();
return JavaTokenType.MINUS.equals(tokenType) && expr.getOperand() instanceof PsiLiteralExpression;
}
return false;
}
@Nullable
public PsiExpression[] getArguments(@NotNull PsiCallExpression call) {
PsiExpressionList callArgumentsList = call.getArgumentList();
return callArgumentsList != null ? callArgumentsList.getExpressions() : null;
}
@NotNull
public List<FoldingDescriptor> 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();
}
@NotNull
private List<FoldingDescriptor> buildDescriptorsForLiteralArguments() {
List<FoldingDescriptor> descriptors = ContainerUtil.newArrayList();
int i = 0;
while (i < myCallArguments.length) {
if (i + 1 < myCallArguments.length && isCommonlyNamedParameterPair(i, i + 1)) {
i += 2;
continue;
}
if (shouldInlineParameterName(i)) {
descriptors.add(createFoldingDescriptor(myCallArguments[i], myParameters[i]));
}
i++;
}
return descriptors;
}
@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);
}
private boolean isCommonlyNamedParameterPair(int first, int second) {
assert first < myParameters.length && second < myParameters.length;
String firstParamName = myParameters[first].getName();
String secondParamName = myParameters[second].getName();
if (firstParamName == null || secondParamName == null) return false;
if (containsAnyWord(firstParamName, RANGE_START_WORDS) && containsAnyWord(secondParamName, RANGE_END_WORDS)) {
return true;
}
return false;
}
private static boolean containsAnyWord(@NotNull String str, @NotNull String[] words) {
for (String word : words) {
if (StringUtil.containsIgnoreCase(str, word)) return true;
}
return false;
}
private boolean shouldInlineParameterName(int paramIndex) {
PsiExpression argument = myCallArguments[paramIndex];
if (isLiteralExpression(argument) && argument.getType() != null) {
PsiParameter parameter = myParameters[paramIndex];
String paramName = parameter.getName();
if (paramName != null && paramName.length() >= MIN_NAME_LENGTH_THRESHOLD) {
return TypeConversionUtil.isAssignable(parameter.getType(), argument.getType());
}
}
return false;
}
private static boolean hasLiteralExpression(@NotNull PsiExpression[] arguments) {
for (PsiExpression argument : arguments) {
if (isLiteralExpression(argument)) return true;
}
return false;
}
}
@@ -608,7 +608,7 @@ class Test {
configure(testNow, shouldIgnoreRoots(), fourteen, pi, title, c, file);
}
pubic void configure(boolean testNow, boolean shouldIgnoreRoots, int times, float pi, String title, char terminate, File file) {
pubic void configure(boolean testNow, boolean shouldIgnoreRoots, int times, float pii, String title, char terminate, File file) {
System.out.println();
System.out.println();
}
@@ -672,7 +672,7 @@ public class VarArgTest {
assert regions[1].placeholderText == "test: 13"
}
public void "test inline if argument length is one (EA-57555)"() {
public void "test do not inline if parameter length is one or two"() {
def text = """
public class CharSymbol {
@@ -681,7 +681,7 @@ public class CharSymbol {
count(1, false);
}
public void count(int test, boolean fast) {
public void count(int t, boolean fa) {
int temp = test;
boolean isFast = fast;
}
@@ -689,13 +689,47 @@ public class CharSymbol {
"""
configure text
def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset }
assert regions.size() == 4
assert regions.size() == 2
}
checkRangeOffsetByPositionInText(regions[1], text, "1")
assert regions[1].placeholderText == "test: 1"
public void "test do not inline paired ranged names"() {
def text = """
public class CharSymbol {
checkRangeOffsetByPositionInText(regions[2], text, "false")
assert regions[2].placeholderText == "fast: false"
public void main() {
String s = "AAA";
int last = 3;
substring1(1, last);
substring2(1, last);
substring3(1, last);
substring4(1, last);
}
public void substring1(int beginIndex, int endIndex) {
int start = beginIndex;
int end = endIndex;
}
public void substring2(int startIndex, int endIndex) {
int start = beginIndex;
int end = endIndex;
}
public void substring3(int from, int to) {
int start = beginIndex;
int end = endIndex;
}
public void substring4(int first, int last) {
int start = beginIndex;
int end = endIndex;
}
}
"""
configure text
def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset }
assert regions.size() == 5
}
public void "test inline names if literal expression can be assigned to method parameter"() {
@@ -764,7 +798,7 @@ public class Test {
}
abstract class Checker {
Checker(boolean applyToFirst, boolean applyToSecond) {}
Checker(boolean isActive, boolean requestFocus) {}
abstract void test();
}
}
@@ -773,8 +807,8 @@ public class Test {
def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset }
assert regions.length == 6
assert regions[1].placeholderText == "applyToFirst: true"
assert regions[2].placeholderText == "applyToSecond: false"
assert regions[1].placeholderText == "isActive: true"
assert regions[2].placeholderText == "requestFocus: false"
checkRangeOffsetByPositionInText(regions[1], text, "true")
checkRangeOffsetByPositionInText(regions[2], text, "false")