Java: Support switch expressions in '.switch' template (IDEA-204010)

This commit is contained in:
Pavel Dolgov
2019-02-06 15:43:29 +03:00
parent 1d51f2ce2f
commit ddfaf087ee
22 changed files with 314 additions and 7 deletions
@@ -15,15 +15,31 @@
*/
package com.intellij.codeInsight.template.postfix.templates;
import com.intellij.codeInsight.CodeInsightUtilCore;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
import com.intellij.codeInsight.generation.surroundWith.JavaExpressionSurrounder;
import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils;
import com.intellij.lang.surroundWith.Surrounder;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils.selectorTopmost;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.openapi.util.Conditions.and;
public class SwitchStatementPostfixTemplate extends SurroundPostfixTemplateBase {
public class SwitchStatementPostfixTemplate extends StringBasedPostfixTemplate {
private static final Condition<PsiElement> SWITCH_TYPE = expression -> {
if (!(expression instanceof PsiExpression)) return false;
@@ -49,12 +65,106 @@ public class SwitchStatementPostfixTemplate extends StringBasedPostfixTemplate {
};
public SwitchStatementPostfixTemplate() {
super("switch", "switch (expr)", selectorTopmost(SWITCH_TYPE));
super("switch", "switch(expr)", JavaPostfixTemplatesUtils.JAVA_PSI_INFO, selectorTopmost(SWITCH_TYPE));
}
@Nullable
@NotNull
@Override
public String getTemplateString(@NotNull PsiElement element) {
return "switch ($expr$){\n$END$\n}";
protected Surrounder getSurrounder() {
return new JavaExpressionSurrounder() {
@Override
public boolean isApplicable(PsiExpression expr) {
return expr.isPhysical() && SWITCH_TYPE.value(expr);
}
@Override
public TextRange surroundExpression(Project project, Editor editor, PsiExpression expr) throws IncorrectOperationException {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
PsiElement parent = expr.getParent();
if (parent instanceof PsiExpressionStatement) {
PsiSwitchStatement switchStatement = (PsiSwitchStatement)factory.createStatementFromText("switch(1){case 1:}", null);
return postprocessSwitch(editor, expr, codeStyleManager, parent, switchStatement);
}
else if (HighlightUtil.Feature.ENHANCED_SWITCH.isAvailable(expr)) {
PsiSwitchExpression switchExpression = (PsiSwitchExpression)factory.createExpressionFromText("switch(1){case 1->1;}", null);
return postprocessSwitch(editor, expr, codeStyleManager, expr, switchExpression);
}
return TextRange.from(editor.getCaretModel().getOffset(), 0);
}
@NotNull
private TextRange postprocessSwitch(Editor editor,
PsiExpression expr,
CodeStyleManager codeStyleManager,
PsiElement toReplace,
PsiSwitchBlock switchBlock) {
switchBlock = (PsiSwitchBlock)codeStyleManager.reformat(switchBlock);
PsiExpression selectorExpression = switchBlock.getExpression();
if (selectorExpression != null) {
selectorExpression.replace(expr);
}
switchBlock = (PsiSwitchBlock)toReplace.replace(switchBlock);
PsiCodeBlock body = switchBlock.getBody();
if (body != null) {
body = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(body);
TextRange range = body.getStatements()[0].getTextRange();
editor.getDocument().deleteString(range.getStartOffset(), range.getEndOffset());
return TextRange.from(range.getStartOffset(), 0);
}
return TextRange.from(editor.getCaretModel().getOffset(), 0);
}
@Override
public String getTemplateDescription() {
return "switch (expr) {...}";
}
};
}
public static PostfixTemplateExpressionSelector selectorTopmost(Condition<PsiElement> additionalFilter) {
return new PostfixTemplateExpressionSelectorBase(additionalFilter) {
@Override
protected List<PsiElement> getNonFilteredExpressions(@NotNull PsiElement context, @NotNull Document document, int offset) {
boolean isEnhancedSwitchAvailable = HighlightUtil.Feature.ENHANCED_SWITCH.isAvailable(context);
List<PsiElement> result = new ArrayList<>();
for (PsiElement element = PsiTreeUtil.getNonStrictParentOfType(context, PsiExpression.class, PsiStatement.class);
element instanceof PsiExpression; element = element.getParent()) {
PsiElement parent = element.getParent();
if (parent instanceof PsiExpressionStatement) {
result.add(element);
}
else if (isEnhancedSwitchAvailable && (isVariableInitializer(element, parent) || isRightSideOfAssignment(element, parent))) {
result.add(element);
}
}
return result;
}
@Override
protected Condition<PsiElement> getFilters(int offset) {
return and(super.getFilters(offset), getPsiErrorFilter());
}
@NotNull
@Override
public Function<PsiElement, String> getRenderer() {
return JavaPostfixTemplatesUtils.getRenderer();
}
private boolean isVariableInitializer(PsiElement element, PsiElement parent) {
return parent instanceof PsiVariable && ((PsiVariable)parent).getInitializer() == element;
}
private boolean isRightSideOfAssignment(PsiElement element, PsiElement parent) {
return parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getRExpression() == element;
}
};
}
}
@@ -0,0 +1,6 @@
public class Foo {
void f(byte x) {
String s;
s = x.switch<caret>
}
}
@@ -0,0 +1,8 @@
public class Foo {
void f(byte x) {
String s;
s = switch (x) {
<caret>
}
}
}
@@ -0,0 +1,5 @@
public class Foo {
void f(byte x) {
String s = x.switch<caret>
}
}
@@ -0,0 +1,7 @@
public class Foo {
void f(byte x) {
String s = switch (x) {
<caret>
}
}
}
@@ -0,0 +1,6 @@
public class Foo {
void m() {
int i;
i = 42 + 42.switch<caret>
}
}
@@ -0,0 +1,8 @@
public class Foo {
void m() {
int i;
i = switch (42 + 42) {
<caret>
}
}
}
@@ -0,0 +1,5 @@
public class Foo {
void m() {
int i = 42 + 42.switch<caret>
}
}
@@ -0,0 +1,7 @@
public class Foo {
void m() {
int i = switch (42 + 42) {
<caret>
}
}
}
@@ -0,0 +1,8 @@
public enum Foo {
A, B, C;
void f() {
Foo foo;
foo = Foo.values()[0].switch<caret>
}
}
@@ -0,0 +1,10 @@
public enum Foo {
A, B, C;
void f() {
Foo foo;
foo = switch (Foo.values()[0]) {
<caret>
}
}
}
@@ -0,0 +1,7 @@
public enum Foo {
A, B, C;
void f() {
Foo foo = Foo.values()[0].switch<caret>
}
}
@@ -0,0 +1,9 @@
public enum Foo {
A, B, C;
void f() {
Foo foo = switch (Foo.values()[0]) {
<caret>
}
}
}
@@ -0,0 +1,6 @@
public class Foo {
int f(int x) {
int i;
i *= x.switch<caret>
}
}
@@ -0,0 +1,8 @@
public class Foo {
int f(int x) {
int i;
i *= switch (x) {
<caret>
}
}
}
@@ -0,0 +1,5 @@
public class Foo {
int f(int x) {
int i = x.switch<caret>
}
}
@@ -0,0 +1,7 @@
public class Foo {
int f(int x) {
int i = switch (x) {
<caret>
}
}
}
@@ -0,0 +1,6 @@
public class Foo {
void f() {
String s;
s += "abc".switch<caret>
}
}
@@ -0,0 +1,8 @@
public class Foo {
void f() {
String s;
s += switch ("abc") {
<caret>
}
}
}
@@ -0,0 +1,5 @@
public class Foo {
void f() {
String s = "abc".switch<caret>
}
}
@@ -0,0 +1,7 @@
public class Foo {
void f() {
String s = switch ("abc") {
<caret>
}
}
}
@@ -0,0 +1,59 @@
// 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.template.postfix.templates;
import com.intellij.testFramework.LightProjectDescriptor;
import org.jetbrains.annotations.NotNull;
public class SwitchExpressionPostfixTemplateTest extends PostfixTemplateTestCase {
@NotNull
@Override
protected String getSuffix() {
return "switch";
}
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return JAVA_12;
}
public void testIntExprInit() {
doTest();
}
public void testIntExprAssign() {
doTest();
}
public void testByteExprInit() {
doTest();
}
public void testByteExprAssign() {
doTest();
}
public void testEnumExprInit() {
doTest();
}
public void testEnumExprAssign() {
doTest();
}
public void testStringExprInit() {
doTest();
}
public void testStringExprAssign() {
doTest();
}
public void testCompositeExprInit() {
doTest();
}
public void testCompositeExprAssign() {
doTest();
}
}