Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -101,6 +103,10 @@ public class AdvancedExpressionFoldingBuilder extends FoldingBuilderEx {
add("unmodifiableSet");
add("unmodifiableList");
add("toString");
add("isBefore");
add("isAfter");
// LocalDate literal
add("of");
}
};

Expand Down Expand Up @@ -130,6 +136,7 @@ public class AdvancedExpressionFoldingBuilder extends FoldingBuilderEx {
add("java.util.Collections");
add("java.util.Objects");
add("java.util.stream.Stream");
add("java.time.LocalDate");
}
};

Expand Down Expand Up @@ -857,10 +864,33 @@ private static Expression getLiteralExpression(@NotNull PsiLiteralExpression ele
private static Expression getPrefixExpression(@NotNull PsiPrefixExpression element, @NotNull Document document) {
AdvancedExpressionFoldingSettings settings = AdvancedExpressionFoldingSettings.getInstance();
if (element.getOperand() != null) {
if (element.getOperationSign().getText().equals("!") && settings.getState().isComparingExpressionsCollapse()) {
@NotNull Expression operand = getAnyExpression(element.getOperand(), document);
if (operand instanceof Equal) {
return new NotEqual(element, element.getTextRange(), ((Equal) operand).getOperands());
if (element.getOperationSign().getText().equals("!")) {
if (settings.getState().isComparingLocalDatesCollapse()) {
if (element.getOperand() instanceof PsiMethodCallExpression) {
PsiMethodCallExpression operand = (PsiMethodCallExpression) element.getOperand();
Optional<MethodCallInformation> methodCallInformationOptional = MethodCallInformation.tryGet(operand, document, Arrays.asList("java.time.LocalDate", "java.time.LocalTime", "java.time.LocalDateTime", "java.time.Year", "java.time.YearMonth", "java.time.ChronoLocalDateTime", "java.time.Instant"), "isBefore", "isAfter");
if (methodCallInformationOptional.isPresent()) {
MethodCallInformation callInformation = methodCallInformationOptional.get();

if (callInformation.methodName.equals("isBefore")) {
return new GreaterEqual(element, element.getTextRange(), Arrays.asList(callInformation.qualifierExpression, callInformation.getFoldedArgument(0)));
}

if (callInformation.methodName.equals("isAfter")) {
return new LessEqual(element, element.getTextRange(), Arrays.asList(callInformation.qualifierExpression, callInformation.getFoldedArgument(0)));
}
}

}
}
if (settings.getState().isComparingExpressionsCollapse()) {
@NotNull Expression operand = getAnyExpression(element.getOperand(), document);
if (operand instanceof Equal) {
return new NotEqual(element, element.getTextRange(), ((Equal) operand).getOperands());
}
} else if (element.getOperationSign().getText().equals("-")) {
@NotNull Expression operand = getAnyExpression(element.getOperand(), document);
return new Negate(element, element.getTextRange(), Collections.singletonList(operand));
}
} else if (element.getOperationSign().getText().equals("-")) {
@NotNull Expression operand = getAnyExpression(element.getOperand(), document);
Expand Down Expand Up @@ -1532,6 +1562,20 @@ && startsWith(((PsiMethodCallExpression) argument).getMethodExpression().getRefe
break;
}
}
// LocalDate handling
case "isBefore":
if (settings.getState().isComparingLocalDatesCollapse()) {
return new Less(element, element.getTextRange(), Arrays.asList(qualifierExpression, argumentExpression));
} else {
break;
}

case "isAfter":
if (settings.getState().isComparingLocalDatesCollapse()) {
return new Greater(element, element.getTextRange(), Arrays.asList(qualifierExpression, argumentExpression));
} else {
break;
}
}
} else if (element.getArgumentList().getExpressions().length == 0) {
switch (methodName) {
Expand Down Expand Up @@ -1647,6 +1691,19 @@ && startsWith(((PsiMethodCallExpression) argument).getMethodExpression().getRefe
}
}
}
else if (element.getArgumentList().getExpressions().length == 3) {
PsiExpression a1 = element.getArgumentList().getExpressions()[0];
PsiExpression a2 = element.getArgumentList().getExpressions()[1];
PsiExpression a3 = element.getArgumentList().getExpressions()[2];
if (methodName.equals("of") && className.equals("java.time.LocalDate") && settings.getState().isLocalDateLiteralCollapse()) {
if (a1 instanceof PsiLiteralExpression && a2 instanceof PsiLiteralExpression && a3 instanceof PsiLiteralExpression) {
PsiLiteralExpression year = (PsiLiteralExpression) a1;
PsiLiteralExpression month = (PsiLiteralExpression) a2;
PsiLiteralExpression day = (PsiLiteralExpression) a3;
return new LocalDateLiteral(element, element.getTextRange(), year, month, day);
}
}
}
if (element.getArgumentList().getExpressions().length == 1) {
PsiExpression argument = element.getArgumentList().getExpressions()[0];
if (method.getName().equals("valueOf") && argument instanceof PsiLiteralExpression) {
Expand Down Expand Up @@ -1971,4 +2028,57 @@ public boolean isCollapsedByDefault(@NotNull ASTNode astNode) {
}
return false;
}

private static class MethodCallInformation {
private final PsiMethodCallExpression element;
private final Expression qualifierExpression;
private final PsiExpression[] arguments;
private final Document document;
private final String methodName;
private final String className;
private final PsiClass psiClass;

public MethodCallInformation(PsiMethodCallExpression element, Expression qualifierExpression, String methodName, String className, PsiClass psiClass, Document document) {
this.element = element;
this.qualifierExpression = qualifierExpression;
this.methodName = methodName;
this.className = className;
this.psiClass = psiClass;
this.arguments = element.getArgumentList().getExpressions();
this.document = document;
}

Expression getFoldedArgument(int index) {
return getAnyExpression(element.getArgumentList().getExpressions()[index], document);
}

static Optional<MethodCallInformation> tryGet(PsiMethodCallExpression element, @NotNull Document document, List<String> classNames, String... methodNames) {
return tryGet(element, document, Arrays.asList(methodNames)::contains, (actualClassName, methodName) -> classNames.contains(actualClassName));
}

static Optional<MethodCallInformation> tryGet(PsiMethodCallExpression element, @NotNull Document document, Predicate<String> isMethodNameSupported, BiPredicate<String, String> isMethodSupported) {
PsiReferenceExpression referenceExpression = element.getMethodExpression();
Optional<PsiElement> identifier = Stream.of(referenceExpression.getChildren())
.filter(c -> c instanceof PsiIdentifier).findAny();
@Nullable PsiExpression qualifier = element.getMethodExpression().getQualifierExpression();

if (identifier.isPresent() && isMethodNameSupported.test(identifier.get().getText())) {
PsiMethod method = (PsiMethod) referenceExpression.resolve();
if (method != null) {
PsiClass psiClass = method.getContainingClass();
if (psiClass != null && psiClass.getQualifiedName() != null) {
String className = eraseGenerics(psiClass.getQualifiedName());
String methodName = identifier.get().getText();
if (isMethodSupported.test(className, methodName)
&& qualifier != null) {

@NotNull Expression qualifierExpression = getAnyExpression(qualifier, document);
return Optional.of(new MethodCallInformation(element, qualifierExpression, methodName, className, psiClass, document));
}
}
}
}
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ public class AdvancedExpressionFoldingOptionsProvider extends com.intellij.opena
protected AdvancedExpressionFoldingOptionsProvider() {
super(AdvancedExpressionFoldingSettings.getInstance().getState());
AdvancedExpressionFoldingSettings settings = AdvancedExpressionFoldingSettings.getInstance();
AdvancedExpressionFoldingSettings.State state = settings.getState();

checkBox("arithmeticExpressionsCollapse", "Math, BigDecimal and BigInteger expressions (deprecated)");
checkBox("concatenationExpressionsCollapse", "StringBuilder.append and Collection.add/remove expressions, interpolated Strings and Stream expressions");
checkBox("slicingExpressionsCollapse", "List.subList and String.substring expressions");
checkBox("comparingExpressionsCollapse", "Object.equals and Comparable.compareTo expressions");
checkBox("java.time isBefore/isAfter expressions", state::isComparingLocalDatesCollapse, state::setComparingLocalDatesCollapse);
checkBox("LocalDate.of literals (e.g. 2018-02-12)" , state::isLocalDateLiteralCollapse, state::setLocalDateLiteralCollapse);
checkBox("Postfix LocalDate literals (e.g. 2018Y-02M-12D) " , state::isLocalDateLiteralPostfix, state::setLocalDateLiteralPostfix);
checkBox("getExpressionsCollapse", "List.get, List.set, Map.get and Map.put expressions, array and list literals");
checkBox("rangeExpressionsCollapse", "For loops, range expressions");
checkBox("checkExpressionsCollapse", "Null safe calls");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public static final class State {
private boolean CONCATENATION_EXPRESSIONS = true;
private boolean SLICING_EXPRESSIONS = true;
private boolean COMPARING_EXPRESSIONS = true;

private boolean COMPARING_LOCAL_DATES = true;
private boolean LOCAL_DATE_LITERAL = true;
private boolean LOCAL_DATE_LITERAL_POSTFIX = true;

private boolean GET_EXPRESSIONS = true;
private boolean RANGE_EXPRESSIONS = true;
private boolean CHECK_EXPRESSIONS = true;
Expand Down Expand Up @@ -76,6 +81,18 @@ public boolean isGetExpressionsCollapse() {
return GET_EXPRESSIONS;
}

public boolean isComparingLocalDatesCollapse() {
return COMPARING_LOCAL_DATES;
}

public boolean isLocalDateLiteralCollapse() {
return LOCAL_DATE_LITERAL;
}

public boolean isLocalDateLiteralPostfix() {
return LOCAL_DATE_LITERAL_POSTFIX;
}

public boolean isRangeExpressionsCollapse() {
return RANGE_EXPRESSIONS;
}
Expand Down Expand Up @@ -132,6 +149,19 @@ public void setComparingExpressionsCollapse(boolean value) {
COMPARING_EXPRESSIONS = value;
}

public void setComparingLocalDatesCollapse(boolean value) {
this.COMPARING_LOCAL_DATES = value;
}

public void setLocalDateLiteralCollapse(boolean value) {
this.LOCAL_DATE_LITERAL = value;
}

public void setLocalDateLiteralPostfix(boolean value) {
this.LOCAL_DATE_LITERAL_POSTFIX = value;
}


public void setGetExpressionsCollapse(boolean value) {
GET_EXPRESSIONS = value;
}
Expand Down Expand Up @@ -192,6 +222,9 @@ public void disableAll() {
this.setRangeExpressionsCollapse(false);
this.setSemicolonsCollapse(false);
this.setSlicingExpressionsCollapse(false);
this.setComparingLocalDatesCollapse(false);
this.setLocalDateLiteralCollapse(false);
this.setLocalDateLiteralPostfix(false);
}
}
}
103 changes: 103 additions & 0 deletions src/com/intellij/advancedExpressionFolding/LocalDateLiteral.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.intellij.advancedExpressionFolding;

import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.FoldingGroup;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLiteralExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class LocalDateLiteral extends Expression {
public static final String DATE_SEPARATOR = "-";

public static final String YEAR_POSTFIX = "Y";
public static final String MONTH_POSTFIX = "M";
public static final String DAY_POSTFIX = "D";

@NotNull
private final PsiLiteralExpression year;
@NotNull
private final PsiLiteralExpression month;
@NotNull
private final PsiLiteralExpression day;

public LocalDateLiteral(@NotNull PsiElement element, @NotNull TextRange textRange, @NotNull PsiLiteralExpression year, @NotNull PsiLiteralExpression month, @NotNull PsiLiteralExpression day) {
super(element, textRange);
this.year = year;
this.month = month;
this.day = day;
}

@Override
public boolean supportsFoldRegions(@NotNull Document document,
@Nullable Expression parent) {
return true;
}

@Override
public FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement element, @NotNull Document document, @Nullable Expression parent) {
FoldingGroup group = FoldingGroup.newGroup(ListLiteral.class.getName());
ArrayList<FoldingDescriptor> descriptors = new ArrayList<>();
descriptors.add(new FoldingDescriptor(element.getNode(), TextRange.create(textRange.getStartOffset(),
year.getTextRange().getStartOffset()), group) {
@NotNull
@Override
public String getPlaceholderText() {
return "";
}
});

boolean usePostfix = AdvancedExpressionFoldingSettings.getInstance().getState().isLocalDateLiteralPostfix();

final String dateSep = DATE_SEPARATOR;
final String yearPostfix = usePostfix ? YEAR_POSTFIX : "";
final String monthPostfix = usePostfix ? MONTH_POSTFIX : "";
final String dayPostfix = usePostfix ? DAY_POSTFIX : "";

descriptors.add(new FoldingDescriptor(element.getNode(), TextRange.create(year.getTextRange().getEndOffset(),
month.getTextRange().getStartOffset()), group) {
@NotNull
@Override
public String getPlaceholderText() {
// Add leading zero to month if month is only a single digit
if (month.getTextLength() == 1) {
return yearPostfix + dateSep + "0";
} else {
return yearPostfix + dateSep;
}
}
});

descriptors.add(new FoldingDescriptor(element.getNode(), TextRange.create(month.getTextRange().getEndOffset(),
day.getTextRange().getStartOffset()), group) {
@NotNull
@Override
public String getPlaceholderText() {
// Add leading zero to day if day is only a single digit
if (day.getTextLength() == 1) {
return monthPostfix + dateSep + "0";
} else {
return monthPostfix + dateSep;
}
}
});

descriptors.add(new FoldingDescriptor(element.getNode(), TextRange.create(day.getTextRange().getEndOffset(),
textRange.getEndOffset()), group) {
@NotNull
@Override
public String getPlaceholderText() {
return dayPostfix + "";
}
});
// descriptors.add(year.buildFoldRegions(year.getElement(), document, this);
// descriptors.add(month.buildFoldRegions(month.getElement(), document, this);
// descriptors.add(day.buildFoldRegions(day.getElement(), document, this);
return descriptors.toArray(new FoldingDescriptor[0]);
}}
19 changes: 19 additions & 0 deletions test/com/intellij/advancedExpressionFolding/FoldingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,25 @@ public void testControlFlowMultiStatementTestData() {
doReadOnlyFoldingTest();
}

public void testLocalDateTestData() {
AdvancedExpressionFoldingSettings.State state = AdvancedExpressionFoldingSettings.getInstance().getState();
state.setComparingLocalDatesCollapse(true);
doReadOnlyFoldingTest();
}

public void testLocalDateLiteralTestData() {
AdvancedExpressionFoldingSettings.State state = AdvancedExpressionFoldingSettings.getInstance().getState();
state.setLocalDateLiteralCollapse(true);
doReadOnlyFoldingTest();
}

public void testLocalDateLiteralPostfixTestData() {
AdvancedExpressionFoldingSettings.State state = AdvancedExpressionFoldingSettings.getInstance().getState();
state.setLocalDateLiteralCollapse(true);
state.setLocalDateLiteralPostfix(true);
doReadOnlyFoldingTest();
}

public void testCompactControlFlowTestData() {
AdvancedExpressionFoldingSettings.getInstance().getState().setCompactControlFlowSyntaxCollapse(true);
doFoldingTest();
Expand Down
Loading