mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
IDEA-243021 Intention action to duplicate statement(s) into both 'if' branches
GitOrigin-RevId: dd97488c1ffef6880136db2b04052a36287653a6
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f61a94b5c9
commit
4440291577
@@ -1935,6 +1935,10 @@
|
||||
<className>com.intellij.codeInsight.intention.impl.UnrollLoopAction</className>
|
||||
<category>Java/Control Flow</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.MoveIntoIfBranchesAction</className>
|
||||
<category>Java/Control Flow</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.InvertIfConditionAction</className>
|
||||
<category>Java/Control Flow</category>
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2000-2020 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.BlockUtils;
|
||||
import com.intellij.codeInsight.CodeInsightUtil;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName;
|
||||
import com.intellij.codeInspection.util.IntentionName;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.SelectionModel;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import one.util.streamex.MoreCollectors;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class MoveIntoIfBranchesAction implements IntentionAction {
|
||||
@Override
|
||||
public @IntentionName @NotNull String getText() {
|
||||
return JavaBundle.message("intention.name.move.into.if.branches");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull @IntentionFamilyName String getFamilyName() {
|
||||
return getText();
|
||||
}
|
||||
|
||||
private static List<PsiStatement> extractStatements(Editor editor, PsiFile file) {
|
||||
if (!(file instanceof PsiJavaFile)) return Collections.emptyList();
|
||||
SelectionModel model = editor.getSelectionModel();
|
||||
if (!model.hasSelection()) {
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
PsiElement pos = file.findElementAt(offset);
|
||||
PsiStatement statement = PsiTreeUtil.getParentOfType(pos, PsiStatement.class, false, PsiMember.class, PsiCodeBlock.class);
|
||||
return statement == null ? Collections.emptyList() : Collections.singletonList(statement);
|
||||
}
|
||||
int startOffset = model.getSelectionStart();
|
||||
int endOffset = model.getSelectionEnd();
|
||||
PsiElement[] elements = CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset);
|
||||
return StreamEx.of(elements)
|
||||
.map(e -> tryCast(e, PsiStatement.class))
|
||||
.collect(MoreCollectors.ifAllMatch(Objects::nonNull, Collectors.toList()))
|
||||
.orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
private static boolean hasConflictingDeclarations(@NotNull PsiIfStatement ifStatement, @NotNull List<PsiStatement> statements) {
|
||||
PsiStatement lastStatement = statements.get(statements.size() - 1);
|
||||
List<PsiElement> afterLast = new ArrayList<>();
|
||||
for (PsiElement e = lastStatement.getNextSibling(); e != null; e = e.getNextSibling()) {
|
||||
if (!(e instanceof PsiComment) && !(e instanceof PsiWhiteSpace)) {
|
||||
afterLast.add(e);
|
||||
}
|
||||
}
|
||||
if (afterLast.isEmpty()) return false;
|
||||
Set<PsiNamedElement> declared = StreamEx.of(statements).select(PsiDeclarationStatement.class)
|
||||
.flatArray(PsiDeclarationStatement::getDeclaredElements).select(PsiNamedElement.class).toSet();
|
||||
if (declared.isEmpty()) return false;
|
||||
if (SyntaxTraverser.psiTraverser().withRoots(afterLast).filter(PsiJavaCodeReferenceElement.class)
|
||||
.filter(ref -> declared.contains(ref.resolve())).first() != null) {
|
||||
return true;
|
||||
}
|
||||
return StreamEx.of(ifStatement.getThenBranch(), ifStatement.getElseBranch()).flatArray(ControlFlowUtils::unwrapBlock)
|
||||
.select(PsiDeclarationStatement.class).flatArray(PsiDeclarationStatement::getDeclaredElements)
|
||||
.select(PsiNamedElement.class).map(PsiNamedElement::getName).nonNull()
|
||||
.anyMatch(name -> ContainerUtil.exists(declared, d -> name.equals(d.getName())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
List<PsiStatement> statements = extractStatements(editor, file);
|
||||
if (statements.isEmpty()) return false;
|
||||
PsiElement prev = PsiTreeUtil.skipWhitespacesAndCommentsBackward(statements.get(0));
|
||||
return prev instanceof PsiIfStatement && !hasConflictingDeclarations((PsiIfStatement)prev, statements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
List<PsiStatement> statements = extractStatements(editor, file);
|
||||
if (statements.isEmpty()) return;
|
||||
PsiIfStatement ifStatement = tryCast(PsiTreeUtil.skipWhitespacesAndCommentsBackward(statements.get(0)), PsiIfStatement.class);
|
||||
if (ifStatement == null || hasConflictingDeclarations(ifStatement, statements)) return;
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiStatement thenBranch = ifStatement.getThenBranch();
|
||||
if (thenBranch == null) {
|
||||
ifStatement.setThenBranch(factory.createStatementFromText("{}", null));
|
||||
thenBranch = Objects.requireNonNull(ifStatement.getThenBranch());
|
||||
}
|
||||
else if (!(thenBranch instanceof PsiBlockStatement)) {
|
||||
thenBranch = (PsiStatement)BlockUtils.expandSingleStatementToBlockStatement(thenBranch).getParent().getParent();
|
||||
}
|
||||
PsiStatement elseBranch = ifStatement.getElseBranch();
|
||||
if (elseBranch == null) {
|
||||
ifStatement.setElseBranch(factory.createStatementFromText("{}", null));
|
||||
elseBranch = Objects.requireNonNull(ifStatement.getElseBranch());
|
||||
}
|
||||
else if (!(elseBranch instanceof PsiBlockStatement)) {
|
||||
elseBranch = (PsiStatement)BlockUtils.expandSingleStatementToBlockStatement(elseBranch).getParent().getParent();
|
||||
}
|
||||
PsiCodeBlock thenBlock = ((PsiBlockStatement)thenBranch).getCodeBlock();
|
||||
PsiCodeBlock elseBlock = ((PsiBlockStatement)elseBranch).getCodeBlock();
|
||||
PsiJavaToken thenBrace = thenBlock.getRBrace();
|
||||
PsiJavaToken elseBrace = elseBlock.getRBrace();
|
||||
if (thenBrace == null || elseBrace == null) return;
|
||||
thenBlock.addRangeBefore(statements.get(0), statements.get(statements.size() - 1), thenBrace);
|
||||
elseBlock.addRangeBefore(statements.get(0), statements.get(statements.size() - 1), elseBrace);
|
||||
ifStatement.getParent().deleteChildRange(statements.get(0), statements.get(statements.size() - 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
if (size == 1) {
|
||||
<spot>for(int i = 0; i < size; i++) {
|
||||
System.out.println(i);
|
||||
}</spot>
|
||||
} else {
|
||||
<spot>for(int i = 0; i < size; i++) {
|
||||
System.out.println(i);
|
||||
}</spot>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
if (size == 1) {
|
||||
}
|
||||
<spot>for(int i = 0; i < size; i++) {
|
||||
System.out.println(i);
|
||||
}</spot>
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention moves the selected statements into both branches of previous <code>if</code> statement.
|
||||
The semantics of the program is preserved. It can be useful as an intermediate step of more complex refactoring.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Move up into 'if' statement branches" "true"
|
||||
class X {
|
||||
void test(int x) {
|
||||
if (x > 0) {
|
||||
System.out.println(">0");
|
||||
int y = x * 2;
|
||||
System.out.println(y);
|
||||
} else {
|
||||
if (x < 0) {}
|
||||
int y = x * 2;
|
||||
System.out.println(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Move up into 'if' statement branches" "true"
|
||||
class X {
|
||||
void test(int x) {
|
||||
if (x > 0) {
|
||||
System.out.println(x);
|
||||
} else {
|
||||
System.out.println(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Move up into 'if' statement branches" "false"
|
||||
class X {
|
||||
void test(int x) {
|
||||
if (x > 0) System.out.println(">0"); else {
|
||||
int y = 3;
|
||||
}
|
||||
<selection>int y = x * 2;
|
||||
System.out.println(y);</selection>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Move up into 'if' statement branches" "false"
|
||||
class X {
|
||||
void test(int x) {
|
||||
if (x > 0) System.out.println(">0"); else if (x < 0) {}
|
||||
<selection>int y = x * 2;</selection>
|
||||
System.out.println(y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Move up into 'if' statement branches" "true"
|
||||
class X {
|
||||
void test(int x) {
|
||||
if (x > 0) System.out.println(">0"); else if (x < 0) {}
|
||||
<selection>int y = x * 2;
|
||||
System.out.println(y);</selection>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Move up into 'if' statement branches" "true"
|
||||
class X {
|
||||
void test(int x) {
|
||||
if (x > 0) {}
|
||||
<caret>System.out.println(x);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2000-2020 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.codeInsight.daemon.LightIntentionActionTestCase;
|
||||
|
||||
public class MoveIntoIfBranchesActionTest extends LightIntentionActionTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/moveIntoIf";
|
||||
}
|
||||
}
|
||||
@@ -1253,4 +1253,5 @@ action.dfa.from.stacktrace.text=Find why ''{0}'' could be {1}
|
||||
slice.usage.message.assertion.violated=(assertion violated!)
|
||||
slice.usage.message.in.file.stopped.here=(in {0} file - stopped here)
|
||||
slice.usage.message.tracking.container.contents=(Tracking container ''{0}{1}'' contents)
|
||||
slice.usage.message.location=in {0}
|
||||
slice.usage.message.location=in {0}
|
||||
intention.name.move.into.if.branches=Move up into 'if' statement branches
|
||||
Reference in New Issue
Block a user