mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
[java] "Surround with try-with-resources" template ditched in favor of intention action (IDEA-149130, IDEA-152348)
This commit is contained in:
+150
-37
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,12 @@
|
||||
package com.intellij.codeInsight.intention.impl;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.CodeInsightUtilCore;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.codeInsight.template.*;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -29,12 +33,16 @@ import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
|
||||
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
|
||||
@Override
|
||||
@@ -42,16 +50,20 @@ public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
|
||||
if (!element.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return false;
|
||||
if (!PsiUtil.getLanguageLevel(element).isAtLeast(LanguageLevel.JDK_1_7)) return false;
|
||||
|
||||
final PsiLocalVariable variable = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class);
|
||||
if (variable == null) return false;
|
||||
final PsiExpression initializer = variable.getInitializer();
|
||||
if (initializer == null) return false;
|
||||
final PsiElement declaration = variable.getParent();
|
||||
if (!(declaration instanceof PsiDeclarationStatement)) return false;
|
||||
final PsiElement codeBlock = declaration.getParent();
|
||||
if (!(codeBlock instanceof PsiCodeBlock)) return false;
|
||||
PsiType type = null;
|
||||
|
||||
return InheritanceUtil.isInheritor(variable.getType(), CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE);
|
||||
PsiLocalVariable variable = findVariable(element);
|
||||
if (variable != null) {
|
||||
type = variable.getType();
|
||||
}
|
||||
else {
|
||||
PsiExpression expression = findExpression(element);
|
||||
if (expression != null) {
|
||||
type = expression.getType();
|
||||
}
|
||||
}
|
||||
|
||||
return type != null && InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,35 +72,85 @@ public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiLocalVariable variable = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class);
|
||||
if (variable == null) return;
|
||||
final PsiExpression initializer = variable.getInitializer();
|
||||
if (initializer == null) return;
|
||||
final PsiElement declaration = variable.getParent();
|
||||
if (!(declaration instanceof PsiDeclarationStatement)) return;
|
||||
final PsiElement codeBlock = declaration.getParent();
|
||||
if (!(codeBlock instanceof PsiCodeBlock)) return;
|
||||
PsiLocalVariable variable = findVariable(element);
|
||||
if (variable != null) {
|
||||
processVariable(project, editor, variable);
|
||||
}
|
||||
else {
|
||||
PsiExpression expression = findExpression(element);
|
||||
if (expression != null) {
|
||||
processExpression(project, editor, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final LocalSearchScope scope = new LocalSearchScope(codeBlock);
|
||||
private static PsiLocalVariable findVariable(PsiElement element) {
|
||||
PsiLocalVariable variable = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class);
|
||||
|
||||
if (variable != null &&
|
||||
variable.getInitializer() != null &&
|
||||
variable.getParent() instanceof PsiDeclarationStatement &&
|
||||
variable.getParent().getParent() instanceof PsiCodeBlock) {
|
||||
return variable;
|
||||
}
|
||||
|
||||
if (variable == null && element instanceof PsiWhiteSpace) {
|
||||
PsiElement sibling = element.getPrevSibling();
|
||||
if (sibling instanceof PsiDeclarationStatement) {
|
||||
PsiElement lastVar = ArrayUtil.getLastElement(((PsiDeclarationStatement)sibling).getDeclaredElements());
|
||||
if (lastVar instanceof PsiLocalVariable) {
|
||||
return (PsiLocalVariable)lastVar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PsiExpression findExpression(PsiElement element) {
|
||||
PsiExpression expression = PsiTreeUtil.getParentOfType(element, PsiExpression.class);
|
||||
|
||||
if (expression != null &&
|
||||
expression.getParent() instanceof PsiExpressionStatement &&
|
||||
expression.getParent().getParent() instanceof PsiCodeBlock) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
if (expression == null && element instanceof PsiWhiteSpace) {
|
||||
PsiElement sibling = element.getPrevSibling();
|
||||
if (sibling instanceof PsiExpressionStatement) {
|
||||
return ((PsiExpressionStatement)sibling).getExpression();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void processVariable(Project project, Editor editor, PsiLocalVariable variable) {
|
||||
PsiExpression initializer = ObjectUtils.assertNotNull(variable.getInitializer());
|
||||
PsiElement declaration = variable.getParent();
|
||||
PsiElement codeBlock = declaration.getParent();
|
||||
|
||||
LocalSearchScope scope = new LocalSearchScope(codeBlock);
|
||||
PsiElement last = null;
|
||||
for (PsiReference reference : ReferencesSearch.search(variable, scope).findAll()) {
|
||||
final PsiElement usage = PsiTreeUtil.findPrevParent(codeBlock, reference.getElement());
|
||||
PsiElement usage = PsiTreeUtil.findPrevParent(codeBlock, reference.getElement());
|
||||
if ((last == null || usage.getTextOffset() > last.getTextOffset())) {
|
||||
last = usage;
|
||||
}
|
||||
}
|
||||
|
||||
final String text = "try (" + variable.getTypeElement().getText() + " " + variable.getName() + " = " + initializer.getText() + ") {}";
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
final PsiTryStatement armStatement = (PsiTryStatement)declaration.replace(factory.createStatementFromText(text, codeBlock));
|
||||
String text = "try (" + variable.getTypeElement().getText() + " " + variable.getName() + " = " + initializer.getText() + ") {}";
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiTryStatement armStatement = (PsiTryStatement)declaration.replace(factory.createStatementFromText(text, codeBlock));
|
||||
|
||||
List<PsiElement> toFormat = null;
|
||||
if (last != null) {
|
||||
toFormat = moveStatements(last, armStatement);
|
||||
}
|
||||
|
||||
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
|
||||
final PsiElement formattedElement = codeStyleManager.reformat(armStatement);
|
||||
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
|
||||
PsiElement formattedElement = codeStyleManager.reformat(armStatement);
|
||||
if (toFormat != null) {
|
||||
for (PsiElement psiElement : toFormat) {
|
||||
codeStyleManager.reformat(psiElement);
|
||||
@@ -96,9 +158,9 @@ public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
|
||||
}
|
||||
|
||||
if (last == null) {
|
||||
final PsiCodeBlock tryBlock = ((PsiTryStatement)formattedElement).getTryBlock();
|
||||
PsiCodeBlock tryBlock = ((PsiTryStatement)formattedElement).getTryBlock();
|
||||
if (tryBlock != null) {
|
||||
final PsiJavaToken brace = tryBlock.getLBrace();
|
||||
PsiJavaToken brace = tryBlock.getLBrace();
|
||||
if (brace != null) {
|
||||
editor.getCaretModel().moveToOffset(brace.getTextOffset() + 1);
|
||||
}
|
||||
@@ -110,8 +172,9 @@ public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
|
||||
PsiCodeBlock tryBlock = statement.getTryBlock();
|
||||
assert tryBlock != null : statement.getText();
|
||||
PsiElement parent = statement.getParent();
|
||||
LocalSearchScope scope = new LocalSearchScope(parent);
|
||||
|
||||
List<PsiElement> toFormat = new SmartList<PsiElement>();
|
||||
List<PsiElement> toFormat = new SmartList<>();
|
||||
PsiElement stopAt = last.getNextSibling();
|
||||
|
||||
PsiElement i = statement.getNextSibling();
|
||||
@@ -125,13 +188,8 @@ public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
|
||||
for (PsiElement declared : ((PsiDeclarationStatement)child).getDeclaredElements()) {
|
||||
if (!(declared instanceof PsiLocalVariable)) continue;
|
||||
|
||||
final int endOffset = last.getTextRange().getEndOffset();
|
||||
boolean contained = ReferencesSearch.search(declared, new LocalSearchScope(parent)).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
public boolean process(PsiReference ref) {
|
||||
return ref.getElement().getTextOffset() <= endOffset;
|
||||
}
|
||||
});
|
||||
int endOffset = last.getTextRange().getEndOffset();
|
||||
boolean contained = ReferencesSearch.search(declared, scope).forEach(ref -> ref.getElement().getTextOffset() <= endOffset);
|
||||
|
||||
if (!contained) {
|
||||
PsiLocalVariable var = (PsiLocalVariable)declared;
|
||||
@@ -163,6 +221,38 @@ public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
|
||||
return toFormat;
|
||||
}
|
||||
|
||||
private static void processExpression(Project project, Editor editor, PsiExpression expression) {
|
||||
PsiType type = ObjectUtils.assertNotNull(expression.getType());
|
||||
PsiElement statement = expression.getParent();
|
||||
PsiElement codeBlock = statement.getParent();
|
||||
|
||||
String text = "try (" + type.getCanonicalText(true) + " r = " + expression.getText() + ") {}";
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiTryStatement tryStatement = (PsiTryStatement)statement.replace(factory.createStatementFromText(text, codeBlock));
|
||||
|
||||
tryStatement = (PsiTryStatement)CodeStyleManager.getInstance(project).reformat(tryStatement);
|
||||
|
||||
tryStatement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(tryStatement);
|
||||
|
||||
PsiResourceList resourceList = tryStatement.getResourceList();
|
||||
if (resourceList != null) {
|
||||
PsiResourceVariable var = (PsiResourceVariable)resourceList.iterator().next();
|
||||
PsiIdentifier id = var.getNameIdentifier();
|
||||
PsiExpression initializer = var.getInitializer();
|
||||
if (id != null && initializer != null) {
|
||||
type = initializer.getType();
|
||||
String[] names = IntroduceVariableBase.getSuggestedName(type, initializer).names;
|
||||
PsiType[] types = Stream.of(new TypeSelectorManagerImpl(project, type, initializer, PsiExpression.EMPTY_ARRAY).getTypesForAll())
|
||||
.filter(t -> InheritanceUtil.isInheritor(t, CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE))
|
||||
.toArray(PsiType[]::new);
|
||||
TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(var);
|
||||
builder.replaceElement(id, new NamesExpression(names));
|
||||
builder.replaceElement(var.getTypeElement(), new TypeExpression(project, types));
|
||||
builder.run(editor, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
@@ -174,4 +264,27 @@ public class SurroundAutoCloseableAction extends PsiElementBaseIntentionAction {
|
||||
public String getText() {
|
||||
return getFamilyName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class NamesExpression extends Expression {
|
||||
private final String[] myNames;
|
||||
|
||||
public NamesExpression(String[] names) {
|
||||
myNames = names;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result calculateResult(ExpressionContext context) {
|
||||
return calculateQuickResult(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result calculateQuickResult(ExpressionContext context) {
|
||||
return new TextResult(myNames[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context) {
|
||||
return Stream.of(myNames).map(LookupElementBuilder::create).toArray(LookupElement[]::new);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.io.*;
|
||||
|
||||
class C {
|
||||
void m(File file) throws IOException {
|
||||
<caret>new FileInputStream(file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.io.*;
|
||||
|
||||
class C {
|
||||
void m(File file) throws IOException {
|
||||
new FileInputStream(file)<caret>
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import java.io.*;
|
||||
|
||||
class C {
|
||||
void m(File file) throws IOException {
|
||||
try (FileInputStream fileInputStream = new FileInputStream(file)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.io.*;
|
||||
|
||||
class C {
|
||||
void m(File file) throws IOException {
|
||||
try (FileInputStream fileInputStream = new FileInputStream(file)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
class C {
|
||||
public void read(URLConnection connection) {
|
||||
InputStream stream = connection.getInputStream();<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
class C {
|
||||
public void read(URLConnection connection) {
|
||||
try (InputStream stream = connection.getInputStream()) {<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,14 +27,17 @@ public class SurroundAutoCloseableActionTest extends LightCodeInsightFixtureTest
|
||||
}
|
||||
|
||||
public void testSimple() { doTest(); }
|
||||
public void testSimplePast() { doTest(); }
|
||||
public void testUsage() { doTest(); }
|
||||
public void testMixedUsages() { doTest(); }
|
||||
public void testLastDeclaration() { doTest(); }
|
||||
public void testSplitVar() { doTest(); }
|
||||
public void testExpression() { doTest(); }
|
||||
public void testExpressionIncomplete() { doTest(); }
|
||||
|
||||
private void doTest() {
|
||||
String name = getTestName(false);
|
||||
String intention = CodeInsightBundle.message("intention.surround.resource.with.ARM.block");
|
||||
CodeInsightTestUtil.doIntentionTest(myFixture, intention, name + ".java", name + "_after.java");
|
||||
}
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.template
|
||||
|
||||
import com.intellij.codeInsight.template.impl.InvokeTemplateAction
|
||||
import com.intellij.codeInsight.template.impl.SurroundWithTemplateHandler
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
|
||||
class SurroundWithTemplateTest extends LightCodeInsightFixtureTestCase {
|
||||
public void testSurroundWithTryWithResources() {
|
||||
myFixture.configureByText "C.java", """\
|
||||
import java.io.*;
|
||||
class C {
|
||||
void m() {
|
||||
new FileReader("/dev/null")<caret>
|
||||
}
|
||||
}""".stripIndent()
|
||||
|
||||
invokeTemplate("TR")
|
||||
|
||||
myFixture.checkResult """\
|
||||
import java.io.*;
|
||||
class C {
|
||||
void m() {
|
||||
try (FileReader fileReader = new FileReader("/dev/null")) {
|
||||
<caret>
|
||||
}
|
||||
|
||||
}
|
||||
}""".stripIndent()
|
||||
}
|
||||
|
||||
private void invokeTemplate(String template) {
|
||||
DefaultActionGroup group = SurroundWithTemplateHandler.createActionGroup(project, editor, file)
|
||||
assertNotNull(group)
|
||||
InvokeTemplateAction action = group.childActionsOrStubs.find {
|
||||
it instanceof InvokeTemplateAction && (it as InvokeTemplateAction).template.key == template
|
||||
} as InvokeTemplateAction
|
||||
assertNotNull(action)
|
||||
action.perform()
|
||||
}
|
||||
}
|
||||
@@ -427,7 +427,6 @@ livetemplate.description.surround.cdata.in.xmlorhtmlorjsp=Surround with CDATA se
|
||||
livetemplate.description.surround.with.callable=Surround with Callable
|
||||
livetemplate.description.surround.with.read.lock=Surround with ReadWriteLock.readLock
|
||||
livetemplate.description.surround.with.write.lock=Surround with ReadWriteLock.writeLock
|
||||
livetemplate.description.surround.with.ARM=Surround with try-with-resources
|
||||
quickfix.add.variable.text=Initialize variable ''{0}''
|
||||
quickfix.add.variable.family.name=Initialize variable
|
||||
inspection.i18n.quickfix.annotate.as=Annotate as @{0}
|
||||
|
||||
@@ -73,14 +73,4 @@
|
||||
</context>
|
||||
</template>
|
||||
|
||||
<template name="TR" value="try ($ELEMENT_TYPE$ $VAR$ = $SELECTION$) { $END$ } "
|
||||
resource-bundle="messages.CodeInsightBundle" key="livetemplate.description.surround.with.ARM" toReformat="true" toShortenFQNames="true">
|
||||
<variable name="ELEMENT_TYPE" expression="expressionType(SELECTION)" defaultValue="java.lang.AutoCloseable" alwaysStopAt="false" />
|
||||
<variable name="VAR" expression="suggestVariableName()" defaultValue="" alwaysStopAt="true" />
|
||||
<context>
|
||||
<option name="JAVA_CODE" value="false" />
|
||||
<option name="JAVA_STATEMENT" value="true" />
|
||||
</context>
|
||||
</template>
|
||||
|
||||
</templateSet>
|
||||
|
||||
Reference in New Issue
Block a user