Java: Add code block and break-with-value when wrapping result expression of a switch expression (IDEA-204012)

This commit is contained in:
Pavel Dolgov
2019-01-21 17:58:22 +03:00
parent cf0248d263
commit 89bebb048d
40 changed files with 434 additions and 27 deletions
@@ -16,15 +16,15 @@
*/
package com.intellij.codeInsight.generation.surroundWith;
import com.intellij.psi.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.IncorrectOperationException;
import com.intellij.lang.surroundWith.Surrounder;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,4 +44,67 @@ abstract class JavaStatementsSurrounder implements Surrounder {
}
@Nullable protected abstract TextRange surroundStatements(final Project project, final Editor editor, final PsiElement container, final PsiElement[] statements) throws IncorrectOperationException;
@NotNull
protected PsiStatement addAfter(final PsiStatement statement, final PsiElement container, final PsiElement[] statements) {
if (container instanceof PsiSwitchLabeledRuleStatement && !(statement instanceof PsiBlockStatement)) {
Project project = container.getProject();
PsiManager manager = PsiManager.getInstance(project);
PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
PsiBlockStatement blockStatement = (PsiBlockStatement)factory.createStatementFromText("{\n}", null);
blockStatement = (PsiBlockStatement)codeStyleManager.reformat(blockStatement);
blockStatement = (PsiBlockStatement)container.addAfter(blockStatement, statements[statements.length - 1]);
return (PsiStatement)blockStatement.getCodeBlock().add(statement);
}
return (PsiStatement)container.addAfter(statement, statements[statements.length - 1]);
}
protected static void addRangeWithinContainer(PsiCodeBlock codeBlock, PsiElement container, PsiElement[] statements, boolean canBreak) {
if (container instanceof PsiSwitchLabeledRuleStatement && statements.length == 1) {
PsiElement statement = statements[0];
if (statement instanceof PsiExpressionStatement && canBreak) {
addBreakWithValue(codeBlock, (PsiExpressionStatement)statement);
return;
}
if (statement instanceof PsiBlockStatement) {
addCodeBlockContents(codeBlock, (PsiBlockStatement)statement);
return;
}
}
codeBlock.addRange(statements[0], statements[statements.length - 1]);
}
private static void addBreakWithValue(PsiCodeBlock codeBlock, PsiExpressionStatement statement) {
PsiExpressionStatement wrappedStatement = (PsiExpressionStatement)codeBlock.add(statement);
CommentTracker tracker = new CommentTracker();
tracker.markUnchanged(wrappedStatement.getExpression());
PsiElementFactory factory = JavaPsiFacade.getElementFactory(codeBlock.getProject());
PsiBreakStatement breakStatement = (PsiBreakStatement)factory.createStatementFromText("break 0;", null);
breakStatement = (PsiBreakStatement)tracker.replaceAndRestoreComments(wrappedStatement, breakStatement);
PsiExpression breakExpression = breakStatement.getExpression();
assert breakExpression != null : "breakExpression";
breakExpression.replace(statement.getExpression());
}
protected static void addCodeBlockContents(PsiCodeBlock codeBlock, PsiBlockStatement statement) {
// could just replace one code block with the other but then we lose some comments and formatting
PsiBlockStatement tempStatement = (PsiBlockStatement)codeBlock.add(statement);
PsiCodeBlock tempBlock = tempStatement.getCodeBlock();
PsiJavaToken lBrace = tempBlock.getLBrace();
PsiJavaToken rBrace = tempBlock.getRBrace();
if (lBrace != null && rBrace != null) {
CommentTracker tracker = new CommentTracker();
for (PsiElement element = lBrace.getNextSibling(); element != null && element != rBrace; element = element.getNextSibling()) {
tracker.markUnchanged(element);
codeBlock.addBefore(element, tempStatement);
}
tracker.deleteAndRestoreComments(tempStatement);
}
}
}
@@ -44,11 +44,11 @@ public class JavaWithBlockSurrounder extends JavaStatementsSurrounder{
PsiBlockStatement blockStatement = (PsiBlockStatement)factory.createStatementFromText(text, null);
blockStatement = (PsiBlockStatement)codeStyleManager.reformat(blockStatement);
blockStatement = (PsiBlockStatement)container.addBefore(blockStatement, statements[0]);
blockStatement = (PsiBlockStatement)addAfter(blockStatement, container, statements);
PsiCodeBlock body = blockStatement.getCodeBlock();
SurroundWithUtil.indentCommentIfNecessary(body, statements);
body.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(body, container, statements, true);
container.deleteChildRange(statements[0], statements[statements.length - 1]);
PsiElement firstChild = blockStatement.getFirstChild();
@@ -46,7 +46,7 @@ public class JavaWithDoWhileSurrounder extends JavaStatementsSurrounder{
PsiDoWhileStatement doWhileStatement = (PsiDoWhileStatement)factory.createStatementFromText(text, null);
doWhileStatement = (PsiDoWhileStatement)codeStyleManager.reformat(doWhileStatement);
doWhileStatement = (PsiDoWhileStatement)container.addAfter(doWhileStatement, statements[statements.length - 1]);
doWhileStatement = (PsiDoWhileStatement)addAfter(doWhileStatement, container, statements);
PsiStatement body = doWhileStatement.getBody();
if (!(body instanceof PsiBlockStatement)) {
@@ -54,7 +54,7 @@ public class JavaWithDoWhileSurrounder extends JavaStatementsSurrounder{
}
PsiCodeBlock bodyBlock = ((PsiBlockStatement)body).getCodeBlock();
SurroundWithUtil.indentCommentIfNecessary(bodyBlock, statements);
bodyBlock.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(bodyBlock, container, statements, false);
container.deleteChildRange(statements[0], statements[statements.length - 1]);
PsiExpression condition = doWhileStatement.getCondition();
@@ -47,7 +47,7 @@ public class JavaWithForSurrounder extends JavaStatementsSurrounder{
PsiForStatement forStatement = (PsiForStatement)factory.createStatementFromText(text, null);
forStatement = (PsiForStatement)codeStyleManager.reformat(forStatement);
forStatement = (PsiForStatement)container.addAfter(forStatement, statements[statements.length - 1]);
forStatement = (PsiForStatement)addAfter(forStatement, container, statements);
PsiStatement body = forStatement.getBody();
if (!(body instanceof PsiBlockStatement)) {
@@ -55,7 +55,7 @@ public class JavaWithForSurrounder extends JavaStatementsSurrounder{
}
PsiCodeBlock bodyBlock = ((PsiBlockStatement)body).getCodeBlock();
SurroundWithUtil.indentCommentIfNecessary(bodyBlock, statements);
bodyBlock.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(bodyBlock, container, statements, false);
container.deleteChildRange(statements[0], statements[statements.length - 1]);
forStatement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(forStatement);
@@ -47,7 +47,7 @@ public class JavaWithIfElseSurrounder extends JavaStatementsSurrounder{
PsiIfStatement ifStatement = (PsiIfStatement)factory.createStatementFromText(text, null);
ifStatement = (PsiIfStatement)codeStyleManager.reformat(ifStatement);
ifStatement = (PsiIfStatement)container.addAfter(ifStatement, statements[statements.length - 1]);
ifStatement = (PsiIfStatement)addAfter(ifStatement, container, statements);
PsiStatement thenBranch = ifStatement.getThenBranch();
if (!(thenBranch instanceof PsiBlockStatement)) {
@@ -55,7 +55,7 @@ public class JavaWithIfElseSurrounder extends JavaStatementsSurrounder{
}
PsiCodeBlock thenBlock = ((PsiBlockStatement)thenBranch).getCodeBlock();
SurroundWithUtil.indentCommentIfNecessary(thenBlock, statements);
thenBlock.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(thenBlock, container, statements, true);
container.deleteChildRange(statements[0], statements[statements.length - 1]);
ifStatement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(ifStatement);
PsiExpression condition = ifStatement.getCondition();
@@ -47,13 +47,13 @@ public class JavaWithIfSurrounder extends JavaStatementsSurrounder{
PsiIfStatement ifStatement = (PsiIfStatement)factory.createStatementFromText(text, null);
ifStatement = (PsiIfStatement)codeStyleManager.reformat(ifStatement);
ifStatement = (PsiIfStatement)container.addAfter(ifStatement, statements[statements.length - 1]);
ifStatement = (PsiIfStatement)addAfter(ifStatement, container, statements);
final PsiStatement thenBranch = ifStatement.getThenBranch();
if (thenBranch != null) {
PsiCodeBlock thenBlock = ((PsiBlockStatement)thenBranch).getCodeBlock();
SurroundWithUtil.indentCommentIfNecessary(thenBlock, statements);
thenBlock.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(thenBlock, container, statements, true);
container.deleteChildRange(statements[0], statements[statements.length - 1]);
}
@@ -53,7 +53,7 @@ public class JavaWithRunnableSurrounder extends JavaStatementsSurrounder{
PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)factory.createStatementFromText(text, null);
declarationStatement = (PsiDeclarationStatement)codeStyleManager.reformat(declarationStatement);
declarationStatement = (PsiDeclarationStatement)container.addAfter(declarationStatement, statements[statements.length - 1]);
declarationStatement = (PsiDeclarationStatement)addAfter(declarationStatement, container, statements);
final PsiVariable variable = (PsiVariable)declarationStatement.getDeclaredElements()[0];
@@ -71,8 +71,8 @@ public class JavaWithRunnableSurrounder extends JavaStatementsSurrounder{
makeVariablesFinal(body, body);
final int textOffset = variable.getNameIdentifier().getTextOffset();
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
final int textOffset = variable.getNameIdentifier().getTextOffset();
editor.getCaretModel().moveToOffset(textOffset);
editor.getSelectionModel().removeSelection();
new VariableInplaceRenamer(variable, editor){
@@ -47,14 +47,14 @@ public class JavaWithSynchronizedSurrounder extends JavaStatementsSurrounder{
PsiSynchronizedStatement synchronizedStatement = (PsiSynchronizedStatement)factory.createStatementFromText(text, null);
synchronizedStatement = (PsiSynchronizedStatement)codeStyleManager.reformat(synchronizedStatement);
synchronizedStatement = (PsiSynchronizedStatement)container.addAfter(synchronizedStatement, statements[statements.length - 1]);
synchronizedStatement = (PsiSynchronizedStatement)addAfter(synchronizedStatement, container, statements);
PsiCodeBlock synchronizedBlock = synchronizedStatement.getBody();
if (synchronizedBlock == null) {
return null;
}
SurroundWithUtil.indentCommentIfNecessary(synchronizedBlock, statements);
synchronizedBlock.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(synchronizedBlock, container, statements, true);
container.deleteChildRange(statements[0], statements[statements.length - 1]);
synchronizedStatement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(synchronizedStatement);
@@ -72,11 +72,11 @@ public class JavaWithTryCatchSurrounder extends JavaStatementsSurrounder {
PsiTryStatement tryStatement = (PsiTryStatement)factory.createStatementFromText(text, null);
tryStatement = (PsiTryStatement)CodeStyleManager.getInstance(project).reformat(tryStatement);
tryStatement = (PsiTryStatement)container.addAfter(tryStatement, statements[statements.length - 1]);
tryStatement = (PsiTryStatement)addAfter(tryStatement, container, statements);
PsiCodeBlock tryBlock = tryStatement.getTryBlock();
SurroundWithUtil.indentCommentIfNecessary(tryBlock, statements);
tryBlock.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(tryBlock, container, statements, true);
PsiCatchSection[] catchSections = tryStatement.getCatchSections();
@@ -49,14 +49,14 @@ public class JavaWithTryFinallySurrounder extends JavaStatementsSurrounder{
PsiTryStatement tryStatement = (PsiTryStatement)factory.createStatementFromText(text, null);
tryStatement = (PsiTryStatement)codeStyleManager.reformat(tryStatement);
tryStatement = (PsiTryStatement)container.addAfter(tryStatement, statements[statements.length - 1]);
tryStatement = (PsiTryStatement)addAfter(tryStatement, container, statements);
PsiCodeBlock tryBlock = tryStatement.getTryBlock();
if (tryBlock == null) {
return null;
}
SurroundWithUtil.indentCommentIfNecessary(tryBlock, statements);
tryBlock.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(tryBlock, container, statements, true);
container.deleteChildRange(statements[0], statements[statements.length - 1]);
PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
@@ -46,7 +46,7 @@ public class JavaWithWhileSurrounder extends JavaStatementsSurrounder{
PsiWhileStatement whileStatement = (PsiWhileStatement)factory.createStatementFromText(text, null);
whileStatement = (PsiWhileStatement)codeStyleManager.reformat(whileStatement);
whileStatement = (PsiWhileStatement)container.addAfter(whileStatement, statements[statements.length - 1]);
whileStatement = (PsiWhileStatement)addAfter(whileStatement, container, statements);
PsiStatement body = whileStatement.getBody();
if (!(body instanceof PsiBlockStatement)) {
@@ -54,7 +54,7 @@ public class JavaWithWhileSurrounder extends JavaStatementsSurrounder{
}
PsiCodeBlock bodyBlock = ((PsiBlockStatement)body).getCodeBlock();
SurroundWithUtil.indentCommentIfNecessary(bodyBlock, statements);
bodyBlock.addRange(statements[0], statements[statements.length - 1]);
addRangeWithinContainer(bodyBlock, container, statements, false);
container.deleteChildRange(statements[0], statements[statements.length - 1]);
PsiExpression condition = whileStatement.getCondition();
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> <selection>{ break "a"; }</selection>
default -> "";
};
}
}
@@ -0,0 +1,12 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> {
if () {
break "a";
}
}
default -> "";
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> <selection>"a".substring(1);</selection>
default -> "";
};
}
}
@@ -0,0 +1,12 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> {
if () {
break "a".substring(1);
}
}
default -> "";
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> /*0*/<selection>/*1*/"a" +/*2*/ "b"/*3*/;</selection>/*4*/
default -> "";
};
}
}
@@ -0,0 +1,13 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> /*0*/{
synchronized () {
/*1*/
"a" +/*2*/ "b"/*3*/;
}
}/*4*/
default -> "";
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> <selection>throw new RuntimeException();</selection>
default -> "";
};
}
}
@@ -0,0 +1,10 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> {
throw new RuntimeException();
}
default -> "";
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> <selection>throw new RuntimeException();</selection>
default -> "";
};
}
}
@@ -0,0 +1,12 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> {
if () {
throw new RuntimeException();
}
}
default -> "";
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> /*0*/<selection>/*1*/throw new /*2*/RuntimeException("a")/*3*/;</selection>/*4*/
default -> "";
};
}
}
@@ -0,0 +1,15 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> /*0*/{
try {
/*1*/
throw new /*2*/RuntimeException("a")/*3*/;/*4*/
} catch (RuntimeException e) {
e.printStackTrace();
}
}
default -> "";
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> <selection>{ break "a"; }</selection>
default -> "";
};
}
}
@@ -0,0 +1,12 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> {
for () {
break "a";
}
}
default -> "";
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> <selection>"a".substring(1);</selection>
default -> "";
};
}
}
@@ -0,0 +1,12 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> {
for () {
"a".substring(1);
}
}
default -> "";
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> <selection>{ break "a".substring(1); }</selection>;
};
}
}
@@ -0,0 +1,12 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> {
do {
break "a".substring(1);
} while (true);
};
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> /*0*/<selection>{/*1*/break /*2*/"a";/*3*/}</selection>/*4*/
};
}
}
@@ -0,0 +1,15 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> /*0*/{
try {
/*1*/
break /*2*/"a";/*3*/
} finally {
<caret>
}
}/*4*/
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> /*0*/<selection>/*1*/"a" + "b"/*2*/;</selection>/*3*/
};
}
}
@@ -0,0 +1,15 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> /*0*/{
Runnable runnable = new Runnable() {
public void run() {
/*1*/
"a" + "b"/*2*/;
}
};
}/*3*/
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> <selection>"a" + "b";</selection>;
};
}
}
@@ -0,0 +1,16 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> {
try {
break "a" + "b";
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
;
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> <selection>"a" + "b";</selection>
};
}
}
@@ -0,0 +1,12 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> {
while (true) {
"a" + "b";
}
}
};
}
}
@@ -0,0 +1,8 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> <selection>throw new RuntimeException("a");</selection>
};
}
}
@@ -0,0 +1,13 @@
class C {
void test(int n) {
String s = switch (n) {
case 1 -> "";
default -> {
if () {
throw new RuntimeException("a");
} else {
}
}
};
}
}
@@ -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.java.codeInsight.generation.surroundWith
import com.intellij.codeInsight.generation.surroundWith.*
import com.intellij.java.codeInsight.folding.JavaFoldingTestCase
import com.intellij.lang.LanguageSurrounders
import com.intellij.lang.java.JavaLanguage
import com.intellij.lang.surroundWith.Surrounder
import com.intellij.psi.PsiElement
import com.intellij.testFramework.LightCodeInsightTestCase
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.util.containers.ContainerUtil
class JavaSurroundWith12Test : LightCodeInsightTestCase() {
private val BASE_PATH = "/codeInsight/generation/surroundWith/java12/"
override fun getProjectDescriptor(): LightProjectDescriptor = JavaFoldingTestCase.JAVA_12
fun testCaseBlockWithIf() = doTest(JavaWithIfSurrounder())
fun testCaseResultWithIf() = doTest(JavaWithIfSurrounder())
fun testCaseThrowWithIf() = doTest(JavaWithIfSurrounder())
fun testCaseResultWithSynchronized() = doTest(JavaWithSynchronizedSurrounder())
fun testDefaultBlockWithTryFinally() = doTest(JavaWithTryFinallySurrounder())
fun testCaseThrowWithTryCatch() = doTest(JavaWithTryCatchSurrounder())
fun testDefaultResultWithTryCatchFinally() = doTest(JavaWithTryCatchFinallySurrounder())
fun testDefaultBlockWithDoWhile() = doTest(JavaWithDoWhileSurrounder())
fun testCaseThrowWithBlock() = doTest(JavaWithBlockSurrounder())
fun testDefaultResultWithRunnable() = doTest(JavaWithRunnableSurrounder())
fun testCatchBlockWithFor() = doTest(JavaWithForSurrounder())
fun testCatchResultWithFor() = doTest(JavaWithForSurrounder())
fun testDefaultThrowWithIfElse() = doTest(JavaWithIfElseSurrounder())
fun testDefaultResultWithWhile() = doTest(JavaWithWhileSurrounder())
private fun doTest(surrounder: Surrounder) = doTest(getTestName(false), surrounder)
private fun doTest(fileName: String, surrounder: Surrounder) {
configureByFile("$BASE_PATH$fileName.java")
val item = ContainerUtil.getFirstItem(LanguageSurrounders.INSTANCE.allForLanguage(JavaLanguage.INSTANCE))!!
val selectionModel = getEditor().selectionModel
val elements = item.getElementsToSurround(getFile(), selectionModel.selectionStart, selectionModel.selectionEnd)
assertTrue(surrounder.isApplicable(elements))
SurroundWithHandler.invoke(getProject(), getEditor(), getFile(), surrounder)
checkResultByFile(BASE_PATH + fileName + "_after.java")
}
}