From 4e8bbcd4a312e3825e04441e4b14272cc92cb0e0 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 1 Nov 2017 16:03:28 +0700 Subject: [PATCH] create SortContent intention to sort varargs and array initializers --- .../intention/impl/SortContentAction.java | 480 ++++++++++++++++++ .../quickFix/sortContent/afterArrayEnums.java | 11 + .../quickFix/sortContent/afterEnumVararg.java | 13 + .../quickFix/sortContent/afterIntArray.java | 9 + .../quickFix/sortContent/afterIntVararg.java | 11 + .../sortContent/afterIntVarargSameType.java | 11 + .../sortContent/afterStringArray.java | 9 + .../sortContent/afterStringArrayComments.java | 13 + .../afterStringArrayCommentsLast.java | 12 + .../sortContent/beforeArrayEnums.java | 11 + .../sortContent/beforeEnumVararg.java | 13 + .../beforeEnumVarargWithFunction.java | 15 + .../quickFix/sortContent/beforeErrorType.java | 9 + .../quickFix/sortContent/beforeIntArray.java | 9 + .../quickFix/sortContent/beforeIntVararg.java | 11 + .../sortContent/beforeIntVarargSameType.java | 11 + .../sortContent/beforeStringArray.java | 9 + .../beforeStringArrayComments.java | 13 + .../beforeStringArrayCommentsLast.java | 12 + .../sortContent/beforeStringArrayNull.java | 9 + .../daemon/quickFix/SortContentTest.java | 14 + .../SortContentAction/after.java.template | 9 + .../SortContentAction/before.java.template | 9 + .../SortContentAction/description.html | 7 + resources/src/META-INF/IdeaPlugin.xml | 5 + 25 files changed, 735 insertions(+) create mode 100644 java/java-impl/src/com/intellij/codeInsight/intention/impl/SortContentAction.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterArrayEnums.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterEnumVararg.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntArray.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntVararg.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntVarargSameType.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArray.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArrayComments.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArrayCommentsLast.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeArrayEnums.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumVararg.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumVarargWithFunction.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeErrorType.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntArray.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntVararg.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntVarargSameType.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArray.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayComments.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayCommentsLast.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayNull.java create mode 100644 java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/SortContentTest.java create mode 100644 resources-en/src/intentionDescriptions/SortContentAction/after.java.template create mode 100644 resources-en/src/intentionDescriptions/SortContentAction/before.java.template create mode 100644 resources-en/src/intentionDescriptions/SortContentAction/description.html diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/SortContentAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/SortContentAction.java new file mode 100644 index 000000000000..a920974b6f83 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/SortContentAction.java @@ -0,0 +1,480 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInsight.intention.impl; + +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.ig.psiutils.ExpressionUtils; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; + +import static com.intellij.util.ObjectUtils.tryCast; + +public class SortContentAction extends PsiElementBaseIntentionAction { + + private static final ExpressionSortableList[] ourSortableLists = new ExpressionSortableList[]{ + new ArraySortableList(), + new VarargSortableList() + }; + public static final int MIN_EXPRESSION_COUNT = 3; + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Sort content"; + } + + @NotNull + @Override + public String getText() { + return getFamilyName(); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + for (ExpressionSortableList list : ourSortableLists) { + if (list.extract(element) != null) break; + } + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + for (ExpressionSortableList sortableList : ourSortableLists) { + if (sortableList.isAvailable(element)) return true; + } + return false; + } + + private static boolean isOrdered(@NotNull T[] array, @NotNull Comparator comparator) { + for (int i = 0; i < array.length - 1; i++) { + if (comparator.compare(array[i], array[i + 1]) > 0) { + return false; + } + } + return true; + } + + // If this method called, that's means that all elements have the same type as argument + @Contract("null -> null") + @Nullable + private static Comparator getComparator(@Nullable PsiType type) { + return getComparator(type, expression -> expression); + } + + private static Comparator getComparator(@Nullable PsiType type, Function keySelector) { + if (type == null) return null; + if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { + return Comparator.comparing(o -> (String)ExpressionUtils.computeConstantExpression(keySelector.apply(o))); + } + if (isNumericType(type)) { + return Comparator + .comparingLong(o -> ((Number)Objects.requireNonNull(ExpressionUtils.computeConstantExpression(keySelector.apply(o)))).longValue()); + } + if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ENUM)) { + return Comparator.comparing(expr -> ((PsiReferenceExpression)expr).getReferenceName()); + } + return null; + } + + private static boolean isNumericType(@NotNull PsiType type) { + return type.equals(PsiType.INT) || + type.equals(PsiType.CHAR) || + type.equals(PsiType.LONG) || + type.equals(PsiType.SHORT) || + type.equals(PsiType.BYTE); + } + + /** + * Base class for something that can be sorted and contains as elements expressions + * @param context class that will be available after extraction + */ + private abstract static class ExpressionSortableList { + + /** + * Extracts context that can be accessed later during extraction + * If context extracted action will be available + * @param originElement the element on which this action was called + * @return context object + */ + @Nullable + protected abstract C extractContext(@NotNull PsiElement originElement); + + /** + * @param context context object + * @return first element to start extracting from + */ + @Nullable + protected abstract PsiElement getFirst(@NotNull C context); + + /** + * @param current element to decide if it is last + * @return true if and only if end is reached and no expressions expected any more + */ + protected abstract boolean isLast(@NotNull PsiElement current); + + /** + * Replace element with expressions with sorted list + * @param listContext list of expressions to be sorted + * @param context context object + * @return element with sorted expressions + */ + @Nullable + protected abstract PsiElement replace(@NotNull EntryListContext listContext, + @NotNull PsiElementFactory factory, + @NotNull C context); + + public PsiElement extract(@NotNull PsiElement element) { + C context = extractContext(element); + if (context == null) return null; + EntryListContext listContext = EntryListContext.from(getFirst(context), + SortContentAction::isSortableExpression, + SortContentAction::isSeparator, + this::isLast); + if (listContext == null) return null; + listContext.sortContent(); + return replace(listContext, JavaPsiFacade.getElementFactory(element.getProject()), context); + } + + private boolean isAvailable(@NotNull PsiElement element) { + return extractContext(element) != null; + } + } + + private static boolean isSortableExpression(@NotNull PsiExpression current) { + return ExpressionUtils.isEvaluatedAtCompileTime(current) || isEnumConstant(current); + } + + private static boolean isSeparator(@NotNull PsiElement current) { + return current instanceof PsiJavaToken && ((PsiJavaToken)current).getTokenType() == JavaTokenType.COMMA; + } + + @Contract("null -> false") + private static boolean isEnumConstant(PsiExpression current) { + return current instanceof PsiReferenceExpression && ((PsiReferenceExpression)current).resolve() instanceof PsiEnumConstant; + } + + private static boolean isSortableEnums(@NotNull StreamEx expressions, @NotNull PsiType expectedType) { + return expressions.allMatch(current -> expectedType.equals(current.getType()) && isEnumConstant(current)); + } + + private static boolean isSortableConstants(@NotNull StreamEx expressions, @NotNull PsiType expectedType) { + return expressions + .allMatch(current -> expectedType.equals(current.getType()) && ExpressionUtils.computeConstantExpression(current) != null); + } + + private static boolean isSortableExpressions(@NotNull PsiExpression[] expressions, @NotNull PsiType expectedType) { + return isSortableConstants(StreamEx.of(expressions), expectedType) || isSortableEnums(StreamEx.of(expressions), expectedType); + } + + private static class ArraySortableList extends ExpressionSortableList { + + @Nullable + @Override + protected PsiArrayInitializerExpression extractContext(@NotNull PsiElement originElement) { + PsiArrayInitializerExpression initializerExpression = PsiTreeUtil.getParentOfType(originElement, PsiArrayInitializerExpression.class); + if (initializerExpression == null) return null; + PsiExpression[] initializers = initializerExpression.getInitializers(); + if (initializers.length < MIN_EXPRESSION_COUNT) return null; + PsiNewExpression newExpr = tryCast(initializerExpression.getParent(), PsiNewExpression.class); + if (newExpr == null) return null; + PsiArrayType arrayType = tryCast(newExpr.getType(), PsiArrayType.class); + if (arrayType == null) return null; + PsiType componentType = arrayType.getComponentType(); + if (!isSortableExpressions(initializers, componentType)) return null; + Comparator comparator = getComparator(componentType); + if (comparator == null) return null; + if (isOrdered(initializers, comparator)) return null; + return initializerExpression; + } + + @Override + public PsiElement getFirst(@NotNull PsiArrayInitializerExpression context) { + return context.getChildren()[1]; + } + + @Override + public PsiElement replace(@NotNull EntryListContext listContext, + @NotNull PsiElementFactory factory, + @NotNull PsiArrayInitializerExpression toReplace) { + return toReplace.replace(factory.createExpressionFromText("{" + listContext.generate() + "}", toReplace)); + } + + @Override + public boolean isLast(@NotNull PsiElement current) { + return current instanceof PsiJavaToken && + ((PsiJavaToken)current).getTokenType() == JavaTokenType.RBRACE; + } + } + + private static class VarargSortableList extends ExpressionSortableList { + + static class VarargContext { + private final @NotNull PsiExpressionList myExpressionList; + private final @NotNull PsiExpression[] myArguments; + + private VarargContext(@NotNull PsiExpressionList expressionList, @NotNull PsiExpression[] arguments) { + myExpressionList = expressionList; + myArguments = arguments; + } + } + + @Nullable + @Override + protected VarargContext extractContext(@NotNull PsiElement originElement) { + PsiExpressionList list = PsiTreeUtil.getParentOfType(originElement, PsiExpressionList.class); + PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(list, PsiMethodCallExpression.class); + if (call == null) return null; + PsiExpression[] arguments = call.getArgumentList().getExpressions(); + if (arguments.length < MIN_EXPRESSION_COUNT + 1) return null; + PsiMethod method = tryCast(call.getMethodExpression().resolve(), PsiMethod.class); + if (method == null) return null; + PsiParameterList parameterList = method.getParameterList(); + PsiParameter[] parameters = parameterList.getParameters(); + PsiExpression[] varargArguments = getVarargArguments(arguments, originElement, parameters); + if(varargArguments == null) return null; + PsiParameter varargParameter = parameters[parameters.length - 1]; + PsiEllipsisType ellipsisType = tryCast(varargParameter.getType(), PsiEllipsisType.class); + if(ellipsisType == null) return null; + if (!isSortableExpressions(varargArguments, ellipsisType.getComponentType())) return null; + Comparator comparator = getComparator(ellipsisType.getComponentType()); + if (comparator == null) return null; + if(isOrdered(varargArguments, comparator)) return null; + return new VarargContext(list, varargArguments); + } + + @Nullable + private static PsiExpression[] getVarargArguments(@NotNull PsiExpression[] arguments, + @NotNull PsiElement originElement, + @NotNull PsiParameter[] parameters) { + PsiParameter last = ArrayUtil.getLastElement(parameters); + if (last == null) return null; + if (!last.isVarArgs()) return null; + PsiExpression closestExpression = getClosestExpression(originElement); + if (closestExpression == null) return null; + int indexOfCurrent = Arrays.asList(arguments).indexOf(closestExpression); + if (-1 == indexOfCurrent) return null; + if (indexOfCurrent < parameters.length - 1) return null; + if (arguments.length < parameters.length + MIN_EXPRESSION_COUNT - 1) return null; + return Arrays.copyOfRange(arguments, parameters.length - 1, arguments.length); + } + + @Nullable + private static PsiExpression getClosestExpression(@NotNull PsiElement element) { + while (element != null) { + if (element instanceof PsiWhiteSpace) { + element = element.getNextSibling(); + continue; + } + if (element instanceof PsiJavaToken) { + IElementType tokenType = ((PsiJavaToken)element).getTokenType(); + if (!(tokenType.equals(JavaTokenType.COMMA) || tokenType.equals(TokenType.WHITE_SPACE))) { + break; + } + element = element.getNextSibling(); + continue; + } + if (!(element instanceof PsiComment)) break; + element = element.getNextSibling(); + } + return PsiTreeUtil.getParentOfType(element, PsiExpression.class, false); + } + + @Override + protected PsiElement getFirst(@NotNull VarargContext context) { + return context.myArguments[0]; + } + + @Override + protected boolean isLast(@NotNull PsiElement current) { + return current instanceof PsiJavaToken && + ((PsiJavaToken)current).getTokenType() == JavaTokenType.RPARENTH; + } + + @Nullable + @Override + protected PsiElement replace(@NotNull EntryListContext listContext, + @NotNull PsiElementFactory factory, + @NotNull VarargContext context) { + PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(context.myExpressionList, PsiMethodCallExpression.class); + if (call == null) return null; + PsiExpression[] arguments = call.getArgumentList().getExpressions(); + PsiMethod method = tryCast(call.getMethodExpression().resolve(), PsiMethod.class); + if (method == null) return null; + int parametersCount = method.getParameters().length; + PsiExpression last = arguments[parametersCount - 1]; + PsiElement current = context.myExpressionList.getChildren()[0]; + String methodText = call.getMethodExpression().getText(); + StringBuilder sb = new StringBuilder(); + sb.append(methodText); + while (current != null && current != last) { + sb.append(current.getText()); + current = current.getNextSibling(); + } + sb.append(listContext.generate()); + sb.append(")"); + return call.replace(factory.createExpressionFromText(sb.toString(), call)); + } + } + + + private static class EntryListContext { + private final @NotNull List myBeforeFirst; + private final @NotNull List myEntries; + + private EntryListContext(@NotNull List first, + @NotNull List entries) { + myBeforeFirst = first; + myEntries = entries; + } + + @Nullable("when failed to extract") + static EntryListContext from(final PsiElement startingElement, // first element after { + Predicate expressionPredicate, + Predicate separatorPredicate, + Predicate endPredicate) { + List beforeFirst = new ArrayList<>(); + PsiElement current = startingElement; + while (current != null && !testWhenExpression(expressionPredicate, current)) { + beforeFirst.add(current); + current = current.getNextSibling(); + } + List entries = extractEntries(current, expressionPredicate, separatorPredicate, endPredicate); + if (entries == null || entries.size() < MIN_EXPRESSION_COUNT) return null; + SortableEntry last = entries.get(entries.size() - 1); + last.myIsLast = true; + return new EntryListContext(beforeFirst, entries); + } + + private static boolean testWhenExpression(Predicate expressionPredicate, PsiElement current) { + return current instanceof PsiExpression && expressionPredicate.test((PsiExpression)current); + } + + private void sortContent() { + PsiExpression exampleExpression = myEntries.get(0).myExpression; + Comparator comparator = SortableEntry.getEntryComparator(exampleExpression); + if (comparator == null) return; + Collections.sort(myEntries, comparator); + } + + + @Nullable("when failed to extract") + private static List extractEntries(PsiElement startingElement, + Predicate expressionPredicate, + Predicate separatorPredicate, + Predicate endPredicate) { + PsiElement current = startingElement; + List entries = new ArrayList<>(); + while (current != null) { + if (!testWhenExpression(expressionPredicate, current)) return null; + PsiExpression expression = (PsiExpression)current; + current = current.getNextSibling(); + List beforeSeparator = new ArrayList<>(); + while (current != null) { + if (separatorPredicate.test(current)) { + current = current.getNextSibling(); + break; + } + if (endPredicate.test(current)) { + entries.add(new SortableEntry(expression, beforeSeparator, new ArrayList<>())); + return entries; + } + beforeSeparator.add(current); + current = current.getNextSibling(); + } + List afterSeparator = new ArrayList<>(); + while (current != null && !testWhenExpression(expressionPredicate, current)) { + if (endPredicate.test(current)) break; + afterSeparator.add(current); + current = current.getNextSibling(); + } + entries.add(new SortableEntry(expression, beforeSeparator, afterSeparator)); + if (endPredicate.test(current)) return entries; + } + return null; + } + + @NotNull + private String generate() { + StringBuilder sb = new StringBuilder(); + for (PsiElement element : myBeforeFirst) { + sb.append(element.getText()); + } + long newLinesCount = StreamEx.of(myEntries).filter(SortableEntry::hasNewLineAfterSeparator).count(); + for (int i = 0, size = myEntries.size(); i < size; i++) { + SortableEntry entry = myEntries.get(i); + entry.generate(sb, newLinesCount >= 2, i == size - 1); + } + return sb.toString(); + } + } + + private static class SortableEntry { + private final @NotNull PsiExpression myExpression; + private final @NotNull List myBeforeSeparator; + private final @NotNull List myAfterSeparator; + private boolean myIsLast; + + private SortableEntry(@NotNull PsiExpression expression, + @NotNull List beforeSeparator, + @NotNull List afterSeparator) { + myExpression = expression; + myBeforeSeparator = beforeSeparator; + myAfterSeparator = afterSeparator; + myIsLast = false; + } + + @Nullable + private static Comparator getEntryComparator(@NotNull PsiExpression exampleExpression) { + if (exampleExpression instanceof PsiReferenceExpression) { + if (!(((PsiReferenceExpression)exampleExpression).resolve() instanceof PsiEnumConstant)) return null; + return Comparator + .comparing(entry -> ((PsiReferenceExpression)entry.myExpression).getReferenceName()); + } + PsiType type = exampleExpression.getType(); + Comparator comparator = getComparator(type); + if (comparator == null) return null; + return Comparator.comparing(entry -> (PsiExpression)entry.myExpression, comparator); + } + + public boolean hasNewLineAfterSeparator() { + for (PsiElement element : myAfterSeparator) { + PsiWhiteSpace space = tryCast(element, PsiWhiteSpace.class); + if (space == null) continue; + if (space.getText().contains("\n")) { + return true; + } + } + return false; + } + + void generate(StringBuilder sb, boolean anotherEntriesWithEnters, boolean isLastNow) { + sb.append(myExpression.getText()); + for (PsiElement element : myBeforeSeparator) { + sb.append(element.getText()); + } + if (!isLastNow) { + sb.append(","); + } + for (PsiElement element : myAfterSeparator) { + sb.append(element.getText()); + } + if (myIsLast != isLastNow && !hasNewLineAfterSeparator() && anotherEntriesWithEnters) { + sb.append("\n"); + } + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterArrayEnums.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterArrayEnums.java new file mode 100644 index 000000000000..5f5629b04ef4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterArrayEnums.java @@ -0,0 +1,11 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + enum E {A,B,C,D} + + private void test() { + new E[]{E.A, E.A, E.B, E.C, E.D}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterEnumVararg.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterEnumVararg.java new file mode 100644 index 000000000000..b72beeecf3bf --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterEnumVararg.java @@ -0,0 +1,13 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + enum E {A,B,C,D} + + private static void foo(String a, E... vararg) {} + + private void test() { + foo("bar", E.A, E.A, E.B, E.C, E.D); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntArray.java new file mode 100644 index 000000000000..2a51b4243412 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntArray.java @@ -0,0 +1,9 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private void test() { + new int[]{1, 3, 4}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntVararg.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntVararg.java new file mode 100644 index 000000000000..97651469734f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntVararg.java @@ -0,0 +1,11 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private static void foo(String a, int... vararg) {} + + private void test() { + foo("bar", 1, 3, 4); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntVarargSameType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntVarargSameType.java new file mode 100644 index 000000000000..bdc3e3fdbba2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterIntVarargSameType.java @@ -0,0 +1,11 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private static void foo(int a, int... vararg) {} + + private void test() { + foo(5, 1, 3, 4); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArray.java new file mode 100644 index 000000000000..40b8388649d5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArray.java @@ -0,0 +1,9 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private void test() { + new String[]{"aaa", "bbb", "ccc"}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArrayComments.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArrayComments.java new file mode 100644 index 000000000000..7efeac9fe624 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArrayComments.java @@ -0,0 +1,13 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private void test() { + new String[]{ + "aaa", // a + "bbb", // b + "ccc" /* c */ + }; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArrayCommentsLast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArrayCommentsLast.java new file mode 100644 index 000000000000..8f436e0ba245 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterStringArrayCommentsLast.java @@ -0,0 +1,12 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private void test() { + new String[]{ + "aaa", // a + "bbb", // b + "ccc" /* c */}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeArrayEnums.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeArrayEnums.java new file mode 100644 index 000000000000..4a3f27dbd788 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeArrayEnums.java @@ -0,0 +1,11 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + enum E {A,B,C,D} + + private void test() { + new E[] {E.B, E.A, E.C, E.D, E.A}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumVararg.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumVararg.java new file mode 100644 index 000000000000..715c0bebf647 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumVararg.java @@ -0,0 +1,13 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + enum E {A,B,C,D} + + private static void foo(String a, E... vararg) {} + + private void test() { + foo("bar", E.B, E.A, E.C, E.D, E.A); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumVarargWithFunction.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumVarargWithFunction.java new file mode 100644 index 000000000000..34c9ed2a01bb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumVarargWithFunction.java @@ -0,0 +1,15 @@ +// "Sort content" "false" + +import java.util.*; + +public class Main { + enum E {A,B,C,D} + + private static void foo(String a, E... vararg) {} + + private E enumConstant() {} + + private void test() { + foo("bar", E.B, E.A, enumConstant(), E.D, E.A); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeErrorType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeErrorType.java new file mode 100644 index 000000000000..85566ef74ca0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeErrorType.java @@ -0,0 +1,9 @@ +// "Sort content" "false" + +import java.util.*; + +public class Main { + private void test() { + new Object[] {"foo", 1, "bar"}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntArray.java new file mode 100644 index 000000000000..0edc18354039 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntArray.java @@ -0,0 +1,9 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private void test() { + new int[] {1, 4, 3}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntVararg.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntVararg.java new file mode 100644 index 000000000000..95f2ae70275c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntVararg.java @@ -0,0 +1,11 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private static void foo(String a, int... vararg) {} + + private void test() { + foo("bar", 1, 4, 3); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntVarargSameType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntVarargSameType.java new file mode 100644 index 000000000000..8eab5d39696b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeIntVarargSameType.java @@ -0,0 +1,11 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private static void foo(int a, int... vararg) {} + + private void test() { + foo(5, 1, 4, 3); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArray.java new file mode 100644 index 000000000000..f7e11d23cb09 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArray.java @@ -0,0 +1,9 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private void test() { + new String[] {"bbb", "aaa", "ccc"}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayComments.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayComments.java new file mode 100644 index 000000000000..835f44c4d4a9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayComments.java @@ -0,0 +1,13 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private void test() { + new String[] { + "bbb", // b + "aaa", // a + "ccc" /* c */ + }; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayCommentsLast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayCommentsLast.java new file mode 100644 index 000000000000..6d22c14a3a03 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayCommentsLast.java @@ -0,0 +1,12 @@ +// "Sort content" "true" + +import java.util.*; + +public class Main { + private void test() { + new String[] { + "bbb", // b + "aaa", // a + "ccc" /* c */}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayNull.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayNull.java new file mode 100644 index 000000000000..12e2c1d607bc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeStringArrayNull.java @@ -0,0 +1,9 @@ +// "Sort content" "false" + +import java.util.*; + +public class Main { + private void test() { + new String[] {"bbb", "aaa", "ccc", null}; + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/SortContentTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/SortContentTest.java new file mode 100644 index 000000000000..09febd8619a2 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/SortContentTest.java @@ -0,0 +1,14 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.java.codeInsight.daemon.quickFix; + +import com.intellij.codeInsight.daemon.LightIntentionActionTestCase; + +public class SortContentTest extends LightIntentionActionTestCase { + public void test() { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/sortContent"; + } +} + diff --git a/resources-en/src/intentionDescriptions/SortContentAction/after.java.template b/resources-en/src/intentionDescriptions/SortContentAction/after.java.template new file mode 100644 index 000000000000..c7272b3d7866 --- /dev/null +++ b/resources-en/src/intentionDescriptions/SortContentAction/after.java.template @@ -0,0 +1,9 @@ +public class X { + void varargFunction(int x, String... y) { + ... + } + + void f(int i) { + varargFunction(12, "AAA", "BBB", "CCC"); + } +} \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/SortContentAction/before.java.template b/resources-en/src/intentionDescriptions/SortContentAction/before.java.template new file mode 100644 index 000000000000..5229da7758ad --- /dev/null +++ b/resources-en/src/intentionDescriptions/SortContentAction/before.java.template @@ -0,0 +1,9 @@ +public class X { + void varargFunction(int x, String... y) { + ... + } + + void f(int i) { + varargFunction(12, "BBB", "AAA", "CCC"); + } +} \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/SortContentAction/description.html b/resources-en/src/intentionDescriptions/SortContentAction/description.html new file mode 100644 index 000000000000..99b16ae66c9b --- /dev/null +++ b/resources-en/src/intentionDescriptions/SortContentAction/description.html @@ -0,0 +1,7 @@ + + +

Intention to sort content of array or vararg

+ + + + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index fe864f3d56ee..e01d3fedea97 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1261,6 +1261,11 @@ Other + + com.intellij.codeInsight.intention.impl.SortContentAction + Other + + com.intellij.codeInsight.intention.impl.ReplaceCastWithVariableAction Java/Imports