implement put parameters/arguments on separate lines intention

GitOrigin-RevId: a786006ab3d744c7e8cbec2be8ae10eca8902e82
This commit is contained in:
Roman.Ivanov
2019-07-02 06:52:16 +03:00
committed by intellij-monorepo-bot
parent c4fc27c44e
commit 0751454160
33 changed files with 610 additions and 0 deletions
@@ -1906,6 +1906,12 @@
<className>com.intellij.codeInsight.intention.impl.WrapWithUnmodifiableAction</className>
<category>Java/Other</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.lists.JavaChopParametersAction</className>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.lists.JavaChopArgumentsAction</className>
</intentionAction>
<externalAnnotationsArtifactsResolver implementation="com.intellij.jarRepository.ExternalAnnotationsRepositoryResolver"/>
<errorQuickFixProvider implementation="com.intellij.codeInsight.daemon.impl.analysis.JavaErrorQuickFixProvider"/>
<fileTypeDetector implementation="com.intellij.openapi.fileTypes.impl.JavaFileTypeDetector"/>
@@ -0,0 +1,90 @@
// Copyright 2000-2019 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.lists;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public abstract class AbstractChopListAction<L extends PsiElement, E extends PsiElement> extends AbstractListIntentionAction<L, E> {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
Context<L, E> context = from(element);
return context != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
Context<L, E> context = from(element);
if (context == null) return;
Document document = editor.getDocument();
List<E> elements = context.elements;
int size = elements.size();
for (int i = elements.size() - 1; i >= 0; i--) {
E el = elements.get(i);
if (!hasBreakAfter(el)) {
int offset = findPlaceForBreakAfter(el);
if (i == size - 1 && !needTailBreak(el)) continue;
document.insertString(offset, "\n");
}
}
E first = elements.get(0);
if (needHeadBreak(first)){
document.insertString(getOffsetOfBreakBeforeFirstElement(first), "\n");
}
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.commitDocument(document);
CodeStyleManager.getInstance(project).adjustLineIndent(context.list.getContainingFile(), context.list.getParent().getTextRange());
}
abstract int findPlaceForBreakAfter(E element);
protected int getOffsetOfBreakBeforeFirstElement(@NotNull E element) {
return element.getTextRange().getStartOffset();
}
private static class Context<L extends PsiElement, E extends PsiElement> {
final @NotNull L list;
final @NotNull List<E> elements;
private Context(@NotNull L list, @NotNull List<E> elements) {
this.list = list;
this.elements = elements;
}
}
@Nullable
Context<L, E> from(@NotNull PsiElement element) {
L list = extractList(element);
if (list == null) return null;
List<E> elements = getElements(list);
if (elements == null) return null;
if (elements.size() < minElementCount()) return null;
if (!hasElementsNotOnSeparateLines(elements)) return null;
return new Context<>(list, elements);
}
@Contract(pure = true)
private boolean hasElementsNotOnSeparateLines(@NotNull List<E> elements) {
int size = elements.size();
for (int i = 0; i < size; i++) {
E current = elements.get(i);
if (i == 0) {
if (needHeadBreak(current) && !hasBreakBefore(current)) return true;
}
if (!hasBreakAfter(current)) {
if (i == size - 1 && !needTailBreak(current)) continue;
return true;
}
}
return false;
}
}
@@ -0,0 +1,43 @@
// Copyright 2000-2019 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.lists;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import static com.intellij.util.ObjectUtils.tryCast;
public abstract class AbstractJavaChopListAction<L extends PsiElement, E extends PsiElement> extends AbstractChopListAction<L, E> {
@Override
boolean hasBreakBefore(@NotNull E element) {
PsiElement current = element.getPrevSibling();
while (current != null && isValidIntermediateElement(current)) {
if (current instanceof PsiWhiteSpace && current.textContains('\n')) return true;
current = current.getPrevSibling();
}
return false;
}
@Override
boolean hasBreakAfter(@NotNull E element) {
PsiElement current = element.getNextSibling();
while (current != null && isValidIntermediateElement(current)) {
if (current instanceof PsiWhiteSpace && current.textContains('\n')) return true;
current = current.getNextSibling();
}
return false;
}
private static boolean isValidIntermediateElement(@NotNull PsiElement element) {
return element instanceof PsiWhiteSpace || element instanceof PsiComment ||
(element instanceof PsiJavaToken && ((PsiJavaToken)element).getTokenType() == JavaTokenType.COMMA);
}
@Override
int findPlaceForBreakAfter(E element) {
PsiJavaToken token = tryCast(PsiTreeUtil.skipWhitespacesAndCommentsForward(element), PsiJavaToken.class);
if (token != null && token.getTokenType() == JavaTokenType.COMMA) return token.getTextRange().getEndOffset();
return element.getTextRange().getEndOffset();
}
}
@@ -0,0 +1,71 @@
// Copyright 2000-2019 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.lists;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public abstract class AbstractListIntentionAction<L extends PsiElement, E extends PsiElement> extends PsiElementBaseIntentionAction {
@Nullable("When failed")
abstract L extractList(@NotNull PsiElement element);
@Nullable("When failed")
abstract List<E> getElements(@NotNull L list);
abstract boolean hasBreakBefore(@NotNull E element);
abstract boolean hasBreakAfter(@NotNull E element);
/**
* Min count of elements for intention to work
* Expected to be > 0
*/
protected int minElementCount() {
return 2;
}
/**
* supposed to delegate to language formatter settings
* @return true if it requires line break after last element
*/
abstract boolean needTailBreak(@NotNull E last);
/**
* supposed to delegate to language formatter settings
* @return true if it requires line break before first element
*/
abstract boolean needHeadBreak(@NotNull E first);
//boolean hasNewlineBetween(@NotNull E left, @NotNull E right) {
// PsiElement current = left.getNextSibling();
// while (current != null && current != right) {
// if (isWhitespaceWithBreak(current)) {
// return true;
// }
// current = current.getNextSibling();
// }
// return false;
//}
//
//private boolean isWhitespaceWithBreak(PsiElement element) {
// return isWhitespace(element) && element.textContains('\n');
//}
//
//boolean hasLeadingNewline(@NotNull E element) {
// PsiElement current = element.getPrevSibling();
// while (current != null) {
// current = current.getPrevSibling();
// if (isWhitespaceWithBreak(current)) {
//
// }
// }
//}
//boolean hasTrailingNewline(@NotNull E element) {
//
//}
}
@@ -0,0 +1,51 @@
// Copyright 2000-2019 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.lists;
import com.intellij.application.options.CodeStyle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class JavaChopArgumentsAction extends AbstractJavaChopListAction<PsiExpressionList, PsiExpression> {
@Nullable("When failed")
@Override
PsiExpressionList extractList(@NotNull PsiElement element) {
return PsiTreeUtil.getParentOfType(element, PsiExpressionList.class, false);
}
@Nullable("When failed")
@Override
List<PsiExpression> getElements(@NotNull PsiExpressionList list) {
return Arrays.asList(list.getExpressions());
}
@Override
boolean needTailBreak(@NotNull PsiExpression last) {
return CodeStyle.getLanguageSettings(last.getContainingFile(), JavaLanguage.INSTANCE).CALL_PARAMETERS_RPAREN_ON_NEXT_LINE;
}
@Override
boolean needHeadBreak(@NotNull PsiExpression first) {
return CodeStyle.getLanguageSettings(first.getContainingFile(), JavaLanguage.INSTANCE).CALL_PARAMETERS_RPAREN_ON_NEXT_LINE;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return "Put arguments on separate lines";
}
@NotNull
@Override
public String getText() {
return getFamilyName();
}
}
@@ -0,0 +1,51 @@
// Copyright 2000-2019 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.lists;
import com.intellij.application.options.CodeStyle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class JavaChopParametersAction extends AbstractJavaChopListAction<PsiParameterList, PsiParameter> {
@Nullable("When failed")
@Override
PsiParameterList extractList(@NotNull PsiElement element) {
return PsiTreeUtil.getParentOfType(element, PsiParameterList.class, false);
}
@Nullable("When failed")
@Override
List<PsiParameter> getElements(@NotNull PsiParameterList list) {
return Arrays.asList(list.getParameters());
}
@Override
boolean needTailBreak(@NotNull PsiParameter last) {
return CodeStyle.getLanguageSettings(last.getContainingFile(), JavaLanguage.INSTANCE).METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE;
}
@Override
boolean needHeadBreak(@NotNull PsiParameter first) {
return CodeStyle.getLanguageSettings(first.getContainingFile(), JavaLanguage.INSTANCE).METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return "Put parameters on separate lines";
}
@NotNull
@Override
public String getText() {
return getFamilyName();
}
}
@@ -0,0 +1,9 @@
class A {
void foo(int a1, int a2, int a3) {
foo(
<spot>1,
2,
3</spot>
)
}
}
@@ -0,0 +1,5 @@
class A {
void foo(int a1, int a2, int a3) {
foo(<spot>1, 2, 3</spot>)
}
}
@@ -0,0 +1,6 @@
<html>
<body>
<p>Put arguments on separate lines. Tail and head breaks are set according to formatting settings.
</p>
</body>
</html>
@@ -0,0 +1,9 @@
class A {
void foo(
<spot>A a1,
A a2,
A a3</spot>
) {
}
}
@@ -0,0 +1,6 @@
class A {
void foo(<spot>A a1,
A a2, A a3</spot>) {
}
}
@@ -0,0 +1,7 @@
<html>
<body>
<p>Put parameters on separate lines. Tail and head breaks are set according to formatting settings.
</p>
<!-- tooltip end -->
</body>
</html>
@@ -0,0 +1,9 @@
// "Put arguments on separate lines" "true"
class A {
void foo(int a1, int a2, int a3) {
foo(1,
2,
3)
}
}
@@ -0,0 +1,10 @@
// "Put arguments on separate lines" "true"
// break after lparen
class A {
void foo(int a1, int a2, int a3) {
foo(1,
2,
3)
}
}
@@ -0,0 +1,13 @@
// "Put arguments on separate lines" "true"
// break after lparen
// break before rparen
class A {
void foo(int a1, int a2, int a3) {
foo(
1,
2,
3
)
}
}
@@ -0,0 +1,7 @@
// "Put arguments on separate lines" "true"
class A {
void foo(int a1, int a2, int a3) {
foo(1, 2<caret>, 3)
}
}
@@ -0,0 +1,8 @@
// "Put arguments on separate lines" "true"
// break after lparen
class A {
void foo(int a1, int a2, int a3) {
foo(1, 2<caret>, 3)
}
}
@@ -0,0 +1,9 @@
// "Put arguments on separate lines" "true"
// break after lparen
// break before rparen
class A {
void foo(int a1, int a2, int a3) {
foo(1, 2<caret>, 3)
}
}
@@ -0,0 +1,11 @@
// "Put parameters on separate lines" "true"
// break after lparen
class A {
void foo(
A a1,
A a2,
A a3) {
}
}
@@ -0,0 +1,9 @@
// "Put parameters on separate lines" "true"
class A {
void foo(A a1,
A a2,
A a3) {
}
}
@@ -0,0 +1,11 @@
// "Put parameters on separate lines" "true"
// break after lparen
class A {
void foo(
A a1,
A a2,
A a3) {
}
}
@@ -0,0 +1,13 @@
// "Put parameters on separate lines" "true"
// break after lparen
// break before rparen
class A {
void foo(
A a1,
A a2,
A a3
) {
}
}
@@ -0,0 +1,9 @@
// "Put parameters on separate lines" "true"
class A {
void foo(A a1,
A a2,
A a3) {
}
}
@@ -0,0 +1,11 @@
// "Put parameters on separate lines" "false"
class A {
void foo(
A a1,
A a2<caret>,
A a3
) {
}
}
@@ -0,0 +1,10 @@
// "Put parameters on separate lines" "true"
// break after lparen
class A {
void foo(A a1,
A a2<caret>,
A a3) {
}
}
@@ -0,0 +1,11 @@
// "Put parameters on separate lines" "false"
// break after lparen
class A {
void foo(
A a1,
A a2<caret>,
A a3) {
}
}
@@ -0,0 +1,11 @@
// "Put parameters on separate lines" "false"
// break before rparen
class A {
void foo(A a1,
A a2<caret>,
A a3
) {
}
}
@@ -0,0 +1,7 @@
// "Put parameters on separate lines" "true"
class A {
void foo(A a1, A a2<caret>, A a3) {
}
}
@@ -0,0 +1,8 @@
// "Put parameters on separate lines" "true"
// break after lparen
class A {
void foo(A a1, A a2<caret>, A a3) {
}
}
@@ -0,0 +1,9 @@
// "Put parameters on separate lines" "true"
// break after lparen
// break before rparen
class A {
void foo(A a1, A a2<caret>, A a3) {
}
}
@@ -0,0 +1,8 @@
// "Put parameters on separate lines" "true"
class A {
void foo(A a1,
A a2<caret>, A a3) {
}
}
@@ -0,0 +1,36 @@
// Copyright 2000-2019 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.intention;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
public class JavaChopArgumentsActionTest extends LightIntentionActionTestCase {
private boolean myBreakAfterLparen;
private boolean myBreakBeforeRparen;
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/lists/chopArguments";
}
@Override
protected void beforeActionStarted(final String testName, final String contents) {
super.beforeActionStarted(testName, contents);
final CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
myBreakAfterLparen = settings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE;
myBreakBeforeRparen = settings.CALL_PARAMETERS_RPAREN_ON_NEXT_LINE;
settings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE = contents.contains("break after lparen");
settings.CALL_PARAMETERS_RPAREN_ON_NEXT_LINE = contents.contains("break before rparen");
}
@Override
protected void afterActionCompleted(final String testName, final String contents) {
super.afterActionCompleted(testName, contents);
CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
settings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE = myBreakAfterLparen;
settings.CALL_PARAMETERS_RPAREN_ON_NEXT_LINE = myBreakBeforeRparen;
}
}
@@ -0,0 +1,36 @@
// Copyright 2000-2019 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.intention;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
public class JavaChopParametersActionTest extends LightIntentionActionTestCase {
private boolean myBreakAfterLparen;
private boolean myBreakBeforeRparen;
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/lists/chopParameters";
}
@Override
protected void beforeActionStarted(final String testName, final String contents) {
super.beforeActionStarted(testName, contents);
final CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
myBreakAfterLparen = settings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE;
myBreakBeforeRparen = settings.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE;
settings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE = contents.contains("break after lparen");
settings.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE = contents.contains("break before rparen");
}
@Override
protected void afterActionCompleted(final String testName, final String contents) {
super.afterActionCompleted(testName, contents);
CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
settings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE = myBreakAfterLparen;
settings.METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE = myBreakBeforeRparen;
}
}