mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-42116 inline local based on control flow improvemnt
This commit is contained in:
@@ -24,5 +24,5 @@ import java.util.Collection;
|
||||
* @author Max Medvedev
|
||||
*/
|
||||
public interface ReferencesToInlineSearcher {
|
||||
Collection<PsiReference> findReferences(PsiElement element);
|
||||
Collection<? extends PsiReference> findReferences(PsiElement element, InlineHandler.Settings settings);
|
||||
}
|
||||
|
||||
@@ -57,18 +57,18 @@ public class GenericInlineHandler {
|
||||
return settings != null;
|
||||
}
|
||||
|
||||
final Collection<PsiReference> allReferences;
|
||||
final Collection<? extends PsiReference> allReferences;
|
||||
|
||||
if (settings.isOnlyOneReferenceToInline()) {
|
||||
allReferences = Collections.singleton(invocationReference);
|
||||
} else {
|
||||
final Ref<Collection<PsiReference>> usagesRef = new Ref<Collection<PsiReference>>();
|
||||
final Ref<Collection<? extends PsiReference>> usagesRef = new Ref<Collection<? extends PsiReference>>();
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final ReferencesToInlineSearcher searcher = ReferencesToInlineSearchers.getSearcher(element.getLanguage());
|
||||
if (searcher != null) {
|
||||
usagesRef.set(searcher.findReferences(element));
|
||||
usagesRef.set(searcher.findReferences(element, settings));
|
||||
}
|
||||
else {
|
||||
usagesRef.set(ReferencesSearch.search(element).findAll());
|
||||
@@ -185,7 +185,7 @@ public class GenericInlineHandler {
|
||||
}
|
||||
|
||||
//order of usages across different files is irrelevant
|
||||
public static PsiReference[] sortDepthFirstRightLeftOrder(final Collection<PsiReference> allReferences) {
|
||||
public static PsiReference[] sortDepthFirstRightLeftOrder(final Collection<? extends PsiReference> allReferences) {
|
||||
final PsiReference[] usages = allReferences.toArray(new PsiReference[allReferences.size()]);
|
||||
Arrays.sort(usages, new Comparator<PsiReference>() {
|
||||
public int compare(final PsiReference usage1, final PsiReference usage2) {
|
||||
|
||||
+111
-3
@@ -15,9 +15,13 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.codeInspection.utils;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
|
||||
@@ -41,21 +45,26 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ControlFlowBuilder;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.IfEndInstruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.MaybeReturnInstruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DfaInstance;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice;
|
||||
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings({"OverlyComplexClass"})
|
||||
public class ControlFlowUtils {
|
||||
private static final Logger LOG = Logger.getInstance(ControlFlowUtils.class);
|
||||
|
||||
private ControlFlowUtils() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public static boolean statementMayCompleteNormally(
|
||||
@Nullable GrStatement statement) {
|
||||
if (statement == null) {
|
||||
@@ -616,4 +625,103 @@ public class ControlFlowUtils {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* searches for next or previous write access to local variable
|
||||
* @param local variable to analyze
|
||||
* @param place place to start searching
|
||||
* @param ahead if true search for next write. if false searches for previous write
|
||||
* @return all write instructions leading to (or preceding) the place
|
||||
*/
|
||||
public static ReadWriteVariableInstruction[] findWriteAccess(GrVariable local, final PsiElement place, boolean ahead) {
|
||||
LOG.assertTrue(GroovyRefactoringUtil.isLocalVariable(local), local.getClass());
|
||||
|
||||
final GrControlFlowOwner owner = findControlFlowOwner(local);
|
||||
LOG.assertTrue(owner != null);
|
||||
|
||||
final Instruction cur = findInstruction(place, owner.getControlFlow());
|
||||
|
||||
if (cur == null) throw new IllegalArgumentException("place is not in the flow");
|
||||
|
||||
final ArrayList<ReadWriteVariableInstruction> result = new ArrayList<ReadWriteVariableInstruction>();
|
||||
final HashSet<Instruction> visited = new HashSet<Instruction>();
|
||||
visited.add(cur);
|
||||
writeAccess(cur, local.getName(), visited, result, ahead);
|
||||
return result.toArray(new ReadWriteVariableInstruction[result.size()]);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Instruction findInstruction(final PsiElement place, Instruction[] controlFlow) {
|
||||
return ContainerUtil.find(controlFlow, new Condition<Instruction>() {
|
||||
@Override
|
||||
public boolean value(Instruction instruction) {
|
||||
return instruction.getElement() == place;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void writeAccess(Instruction cur, String name, Set<Instruction> visited, Collection<ReadWriteVariableInstruction> result, boolean ahead) {
|
||||
final Iterable<? extends Instruction> toIterate = ahead ? cur.allSucc() : cur.allPred();
|
||||
for (Instruction i : toIterate) {
|
||||
if (visited.contains(i)) continue;
|
||||
visited.add(i);
|
||||
if (i instanceof ReadWriteVariableInstruction && ((ReadWriteVariableInstruction)i).isWrite() && name.equals(((ReadWriteVariableInstruction)i).getVariableName())) {
|
||||
result.add((ReadWriteVariableInstruction)i);
|
||||
}
|
||||
else {
|
||||
writeAccess(i, name, visited, result, ahead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ArrayList<BitSet> inferWriteAccessMap(final Instruction[] flow, final GrVariable var) {
|
||||
|
||||
final Semilattice<BitSet> sem = new Semilattice<BitSet>() {
|
||||
@Override
|
||||
public BitSet join(ArrayList<BitSet> ins) {
|
||||
BitSet result = new BitSet(flow.length);
|
||||
for (BitSet set : ins) {
|
||||
result.or(set);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eq(BitSet e1, BitSet e2) {
|
||||
return e1.equals(e2);
|
||||
}
|
||||
};
|
||||
|
||||
DfaInstance<BitSet> dfa = new DfaInstance<BitSet>() {
|
||||
@Override
|
||||
public void fun(BitSet bitSet, Instruction instruction) {
|
||||
if (!(instruction instanceof ReadWriteVariableInstruction)) return;
|
||||
if (!((ReadWriteVariableInstruction)instruction).isWrite()) return;
|
||||
|
||||
final PsiElement element = instruction.getElement();
|
||||
if (element instanceof GrVariable && element != var) return;
|
||||
if (element instanceof GrReferenceExpression) {
|
||||
final GrReferenceExpression ref = (GrReferenceExpression)element;
|
||||
if (ref.isQualified() || ref.resolve() != var) return;
|
||||
}
|
||||
|
||||
bitSet.clear();
|
||||
bitSet.set(instruction.num());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public BitSet initial() {
|
||||
return new BitSet(flow.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForward() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
return new DFAEngine<BitSet>(flow, dfa, sem).performDFA();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
@@ -312,4 +312,13 @@ public abstract class GrVariableBaseImpl<T extends StubElement> extends GrStubEl
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteChildInternal(@NotNull ASTNode child) {
|
||||
final PsiElement psi = child.getPsi();
|
||||
if (psi == getInitializerGroovy()) {
|
||||
deleteChildInternal(findChildByType(GroovyTokenTypes.mASSIGN).getNode());
|
||||
}
|
||||
super.deleteChildInternal(child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1192,4 +1192,25 @@ public class PsiUtil {
|
||||
|
||||
return !ref.isQualified() && name.equals(ref.getName());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GrExpression getInitializerFor(GrReferenceExpression lValue) {
|
||||
if (!isLValue(lValue)) throw new IllegalArgumentException("arg is not lValue");
|
||||
|
||||
final PsiElement parent = lValue.getParent();
|
||||
if (parent instanceof GrAssignmentExpression) return ((GrAssignmentExpression)parent).getRValue();
|
||||
if (parent instanceof GrTupleExpression) {
|
||||
final int i = ((GrTupleExpression)parent).indexOf(lValue);
|
||||
final PsiElement pparent = parent.getParent();
|
||||
LOG.assertTrue(pparent instanceof GrAssignmentExpression);
|
||||
|
||||
final GrExpression rValue = ((GrAssignmentExpression)pparent).getRValue();
|
||||
if (rValue instanceof GrListOrMap && !((GrListOrMap)rValue).isMap()) {
|
||||
final GrExpression[] initializers = ((GrListOrMap)rValue).getInitializers();
|
||||
if (initializers.length < i) return initializers[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -43,7 +43,7 @@ cannot.find.a.single.definition.to.inline.local.var=Cannot find a single definit
|
||||
cannot.find.a.single.definition.to.inline.field=No initializer present for the field
|
||||
inline.local.command=Inline Variable
|
||||
local.varaible.is.lvalue=Variable to be inlined has occurrence as lvalue in assignment
|
||||
inline.local.variable.prompt=Inline local variable ''{0}''?
|
||||
inline.local.variable.prompt.0.1=Inline local variable ''{0}'' ({1,choice,1#1 occurrence|2#{1, number} occurrences})?
|
||||
inline.field.prompt=Inline field ''{0}''?
|
||||
|
||||
########################################################################################################################
|
||||
@@ -162,3 +162,4 @@ files.to.be.converted=Files to be converted
|
||||
converting.files.to.java=Converting files to Java
|
||||
rename.groovy.property=Rename &Groovy property
|
||||
reference.to.accessor.0.is.used=Reference to accessor <bold>{0}<bold> cannot be inlined.
|
||||
variable.is.accessed.for.writing=Variable ''{0}'' is accessed for writing
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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 org.jetbrains.plugins.groovy.refactoring.inline;
|
||||
|
||||
import com.intellij.lang.refactoring.InlineHandler;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.refactoring.HelpID;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
|
||||
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
|
||||
|
||||
/**
|
||||
* @author Max Medvedev
|
||||
*/
|
||||
public class GrInlineFieldUtil {
|
||||
|
||||
public static final String INLINE_FIELD = RefactoringBundle.message("inline.field.title");
|
||||
|
||||
private GrInlineFieldUtil() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static InlineHandler.Settings inlineFieldSettings(final GrField field, Editor editor, boolean invokedOnReference) {
|
||||
final Project project = field.getProject();
|
||||
|
||||
if (!field.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
String message = RefactoringBundle.message("0.refactoring.is.supported.only.for.final.fields", INLINE_FIELD);
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_FIELD, HelpID.INLINE_FIELD);
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
|
||||
if (field.getInitializerGroovy() == null) {
|
||||
String message = GroovyRefactoringBundle.message("cannot.find.a.single.definition.to.inline.field");
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_FIELD, HelpID.INLINE_FIELD);
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
|
||||
return inlineFieldDialogResult(project, field, invokedOnReference);
|
||||
}
|
||||
|
||||
private static InlineHandler.Settings inlineFieldDialogResult(Project project, GrField field, final boolean invokedOnReference) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return new InlineHandler.Settings() {
|
||||
@Override
|
||||
public boolean isOnlyOneReferenceToInline() {
|
||||
return invokedOnReference;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
final InlineGroovyFieldDialog dialog = new InlineGroovyFieldDialog(project, field, invokedOnReference);
|
||||
dialog.show();
|
||||
if (dialog.isOK()) {
|
||||
return new InlineHandler.Settings() {
|
||||
@Override
|
||||
public boolean isOnlyOneReferenceToInline() {
|
||||
return dialog.isInlineThisOnly();
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -50,12 +50,17 @@ public class GrVariableInliner implements InlineHandler.Inliner {
|
||||
private Project myProject;
|
||||
private final GrExpression myTempExpr;
|
||||
|
||||
public GrVariableInliner(GrVariable variable) {
|
||||
public GrVariableInliner(GrVariable variable, InlineHandler.Settings settings) {
|
||||
myProject = variable.getProject();
|
||||
|
||||
GrExpression initializer = variable.getInitializerGroovy();
|
||||
LOG.assertTrue(initializer != null);
|
||||
|
||||
GrExpression initializer;
|
||||
if (settings instanceof InlineLocalVarSettings) {
|
||||
initializer = ((InlineLocalVarSettings)settings).getInitializer();
|
||||
}
|
||||
else {
|
||||
initializer = variable.getInitializerGroovy();
|
||||
LOG.assertTrue(initializer != null);
|
||||
}
|
||||
myTempExpr = (GrExpression)skipParentheses(initializer, false);
|
||||
}
|
||||
|
||||
|
||||
+29
-16
@@ -49,14 +49,14 @@ public class GroovyInlineHandler implements InlineHandler, ReferencesToInlineSea
|
||||
public Settings prepareInlineElement(final PsiElement element, Editor editor, boolean invokedOnReference) {
|
||||
if (element instanceof GrVariable) {
|
||||
if (GroovyRefactoringUtil.isLocalVariable((GrVariable)element)) {
|
||||
return GroovyInlineVariableUtil.inlineLocalVariableSettings((GrVariable)element, editor);
|
||||
return GroovyInlineVariableUtil.inlineLocalVariableSettings((GrVariable)element, editor, invokedOnReference);
|
||||
}
|
||||
else if (element instanceof GrField) {
|
||||
return GroovyInlineVariableUtil.inlineFieldSettings((GrField)element, editor, invokedOnReference);
|
||||
return GrInlineFieldUtil.inlineFieldSettings((GrField)element, editor, invokedOnReference);
|
||||
}
|
||||
}
|
||||
else if (element instanceof GrAccessorMethod) {
|
||||
return GroovyInlineVariableUtil.inlineFieldSettings(((GrAccessorMethod)element).getProperty(), editor, invokedOnReference);
|
||||
return GrInlineFieldUtil.inlineFieldSettings(((GrAccessorMethod)element).getProperty(), editor, invokedOnReference);
|
||||
}
|
||||
else if (element instanceof GrMethod) {
|
||||
return GroovyInlineMethodUtil.inlineMethodSettings((GrMethod)element, editor, invokedOnReference);
|
||||
@@ -82,6 +82,11 @@ public class GroovyInlineHandler implements InlineHandler, ReferencesToInlineSea
|
||||
if (element instanceof GrAccessorMethod) {
|
||||
element = ((GrAccessorMethod)element).getProperty();
|
||||
}
|
||||
|
||||
if (element instanceof GrVariable && GroovyRefactoringUtil.isLocalVariable((GrVariable)element)) {
|
||||
GroovyInlineVariableUtil.removeDefinition(element, settings);
|
||||
return;
|
||||
}
|
||||
final PsiElement owner = element.getParent().getParent();
|
||||
if (element instanceof GrVariable && owner instanceof GrVariableDeclarationOwner) {
|
||||
((GrVariableDeclarationOwner)owner).removeVariable(((GrVariable)element));
|
||||
@@ -94,10 +99,10 @@ public class GroovyInlineHandler implements InlineHandler, ReferencesToInlineSea
|
||||
@Nullable
|
||||
public Inliner createInliner(PsiElement element, Settings settings) {
|
||||
if (element instanceof GrVariable) {
|
||||
return GroovyInlineVariableUtil.createInlinerForVariable(((GrVariable)element));
|
||||
return GroovyInlineVariableUtil.createInlinerForVariable((GrVariable)element, settings);
|
||||
}
|
||||
if (element instanceof GrAccessorMethod) {
|
||||
return GroovyInlineVariableUtil.createInlinerForVariable(((GrAccessorMethod)element).getProperty());
|
||||
return GroovyInlineVariableUtil.createInlinerForVariable(((GrAccessorMethod)element).getProperty(), settings);
|
||||
}
|
||||
if (element instanceof GrMethod) {
|
||||
return new GroovyMethodInliner((GrMethod)element);
|
||||
@@ -106,23 +111,31 @@ public class GroovyInlineHandler implements InlineHandler, ReferencesToInlineSea
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PsiReference> findReferences(PsiElement element) {
|
||||
if (element instanceof GrAccessorMethod) element = ((GrAccessorMethod)element).getProperty();
|
||||
|
||||
if (!(element instanceof GrField)) {
|
||||
return ReferencesSearch.search(element).findAll();
|
||||
public Collection<? extends PsiReference> findReferences(PsiElement element, Settings settings) {
|
||||
if (element instanceof GrVariable && GroovyRefactoringUtil.isLocalVariable((GrVariable)element)) {
|
||||
if (settings instanceof InlineLocalVarSettings) {
|
||||
return ((InlineLocalVarSettings)settings).getRefs();
|
||||
}
|
||||
}
|
||||
|
||||
LOG.assertTrue(((GrField)element).getSetter() == null);
|
||||
else if (element instanceof GrAccessorMethod || element instanceof GrField) {
|
||||
if (element instanceof GrAccessorMethod) {
|
||||
element = ((GrAccessorMethod)element).getProperty();
|
||||
}
|
||||
|
||||
Collection<PsiReference> result = new ArrayList<PsiReference>();
|
||||
result.addAll(ReferencesSearch.search(element).findAll());
|
||||
LOG.assertTrue(((GrField)element).getSetter() == null);
|
||||
|
||||
for (GrAccessorMethod getter : ((GrField)element).getGetters()) {
|
||||
result.addAll(MethodReferencesSearch.search(getter).findAll());
|
||||
Collection<PsiReference> result = new ArrayList<PsiReference>();
|
||||
result.addAll(ReferencesSearch.search(element).findAll());
|
||||
|
||||
for (GrAccessorMethod getter : ((GrField)element).getGetters()) {
|
||||
result.addAll(MethodReferencesSearch.search(getter).findAll());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
return ReferencesSearch.search(element).findAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+166
-61
@@ -16,64 +16,218 @@
|
||||
|
||||
package org.jetbrains.plugins.groovy.refactoring.inline;
|
||||
|
||||
import com.intellij.codeInsight.TargetElementUtilBase;
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager;
|
||||
import com.intellij.lang.refactoring.InlineHandler;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import com.intellij.refactoring.HelpID;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.refactoring.util.RefactoringMessageDialog;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
|
||||
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author ilyas
|
||||
*/
|
||||
public class GroovyInlineVariableUtil {
|
||||
private static final Logger LOG = Logger.getInstance(GroovyInlineVariableUtil.class);
|
||||
|
||||
public static final String INLINE_VARIABLE = RefactoringBundle.message("inline.variable.title");
|
||||
public static final String INLINE_FIELD = RefactoringBundle.message("inline.field.title");
|
||||
|
||||
private GroovyInlineVariableUtil() {
|
||||
}
|
||||
|
||||
public static void removeDefinition(PsiElement element, InlineHandler.Settings settings) {
|
||||
LOG.assertTrue(element instanceof GrVariable && GroovyRefactoringUtil.isLocalVariable((GrVariable)element), element.getClass());
|
||||
|
||||
if (settings instanceof InlineLocalVarSettings) {
|
||||
final GrExpression initializer = ((InlineLocalVarSettings)settings).getInitializer();
|
||||
final PsiElement parent = initializer.getParent();
|
||||
|
||||
if (parent instanceof GrAssignmentExpression) {
|
||||
parent.delete();
|
||||
return;
|
||||
}
|
||||
else if (parent instanceof GrVariable) {
|
||||
if (!((InlineLocalVarSettings)settings).isRemoveDeclaration()) {
|
||||
initializer.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final PsiElement owner = element.getParent().getParent();
|
||||
if (owner instanceof GrVariableDeclarationOwner) {
|
||||
((GrVariableDeclarationOwner)owner).removeVariable((GrVariable)element);
|
||||
}
|
||||
else {
|
||||
element.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new inliner for local variable occurences
|
||||
* Creates new inliner for local variable occurrences
|
||||
*/
|
||||
static InlineHandler.Inliner createInlinerForVariable(final GrVariable variable) {
|
||||
return new GrVariableInliner(variable);
|
||||
static InlineHandler.Inliner createInlinerForVariable(final GrVariable variable, InlineHandler.Settings settings) {
|
||||
return new GrVariableInliner(variable, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Settings object for referenced definition in case of local variable
|
||||
*/
|
||||
@Nullable
|
||||
static InlineHandler.Settings inlineLocalVariableSettings(final GrVariable variable, Editor editor) {
|
||||
static InlineHandler.Settings inlineLocalVariableSettings(final GrVariable variable, @Nullable Editor editor, boolean invokedOnReference) {
|
||||
final String localName = variable.getName();
|
||||
final Project project = variable.getProject();
|
||||
|
||||
if (variable.getInitializerGroovy() == null) {
|
||||
|
||||
|
||||
final Collection<PsiReference> refs = ReferencesSearch.search(variable).findAll();
|
||||
|
||||
GrExpression initializer = null;
|
||||
|
||||
Instruction writeInstr = null;
|
||||
|
||||
final Instruction[] flow = ControlFlowUtils.findControlFlowOwner(variable).getControlFlow();
|
||||
final ArrayList<BitSet> writes = ControlFlowUtils.inferWriteAccessMap(flow, variable);
|
||||
|
||||
GrReferenceExpression refExpr = null;
|
||||
if (invokedOnReference) {
|
||||
LOG.assertTrue(editor != null, "null editor but invokedOnReference==true");
|
||||
final PsiReference ref = TargetElementUtilBase.findReference(editor);
|
||||
LOG.assertTrue(ref != null);
|
||||
final PsiElement cur = ref.getElement();
|
||||
if (cur instanceof GrReferenceExpression) {
|
||||
refExpr = (GrReferenceExpression)cur;
|
||||
final Instruction instruction = ContainerUtil.find(flow, new Condition<Instruction>() {
|
||||
@Override
|
||||
public boolean value(Instruction instruction) {
|
||||
return instruction.getElement() == cur;
|
||||
}
|
||||
});
|
||||
|
||||
LOG.assertTrue(instruction != null);
|
||||
final BitSet prev = writes.get(instruction.num());
|
||||
if (prev.cardinality() == 1) {
|
||||
writeInstr = flow[prev.nextSetBit(0)];
|
||||
final PsiElement element = writeInstr.getElement();
|
||||
if (element instanceof GrVariable) {
|
||||
initializer = ((GrVariable)element).getInitializerGroovy();
|
||||
}
|
||||
else if (element instanceof GrReferenceExpression) {
|
||||
initializer = PsiUtil.getInitializerFor((GrReferenceExpression)element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
initializer = variable.getInitializerGroovy();
|
||||
writeInstr = ContainerUtil.find(flow, new Condition<Instruction>() {
|
||||
@Override
|
||||
public boolean value(Instruction instruction) {
|
||||
return instruction.getElement() == variable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (initializer == null || writeInstr == null) {
|
||||
String message = GroovyRefactoringBundle.message("cannot.find.a.single.definition.to.inline.local.var");
|
||||
CommonRefactoringUtil.showErrorHint(variable.getProject(), editor, message, INLINE_VARIABLE, HelpID.INLINE_VARIABLE);
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
|
||||
return inlineLocalVarDialogResult(localName, project);
|
||||
ArrayList<GrReferenceExpression> toInline = new ArrayList<GrReferenceExpression>();
|
||||
for (Instruction instruction : flow) {
|
||||
if (!(instruction instanceof ReadWriteVariableInstruction)) continue;
|
||||
if (((ReadWriteVariableInstruction)instruction).isWrite()) continue;
|
||||
|
||||
final PsiElement element = instruction.getElement();
|
||||
if (element instanceof GrVariable && element != variable) continue;
|
||||
if (!(element instanceof GrReferenceExpression)) continue;
|
||||
|
||||
final GrReferenceExpression ref = (GrReferenceExpression)element;
|
||||
if (ref.isQualified() || ref.resolve() != variable) continue;
|
||||
|
||||
final BitSet prev = writes.get(instruction.num());
|
||||
if (prev.cardinality() == 1 && prev.get(writeInstr.num())) {
|
||||
toInline.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
if (toInline.size()==0) {
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, GroovyRefactoringBundle.message("variable.is.never.used.0", localName), INLINE_VARIABLE, HelpID.INLINE_VARIABLE);
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
|
||||
ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
|
||||
HighlightManager highlightManager = HighlightManager.getInstance(project);
|
||||
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
|
||||
TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
|
||||
final TextAttributes writeAttributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES);
|
||||
|
||||
if (refExpr != null && PsiUtil.isAccessedForReading(refExpr) && !toInline.contains(refExpr)) {
|
||||
highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{refExpr}, attributes, true, null);
|
||||
String message = RefactoringBundle.getCannotRefactorMessage(GroovyRefactoringBundle.message("variable.is.accessed.for.writing", localName));
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_VARIABLE, HelpID.INLINE_VARIABLE);
|
||||
WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
|
||||
for (GrReferenceExpression ref : toInline) {
|
||||
if (PsiUtil.isAccessedForWriting(ref)) {
|
||||
final String message = GroovyRefactoringBundle.message("variable.is.accessed.for.writing", localName);
|
||||
HighlightManager.getInstance(project).addOccurrenceHighlights(editor, new PsiElement[]{ref}, writeAttributes, true, null);
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_VARIABLE, HelpID.INLINE_VARIABLE);
|
||||
WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
|
||||
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
}
|
||||
|
||||
highlightManager.addOccurrenceHighlights(editor, PsiUtilBase.toPsiElementArray(toInline), attributes, false, highlighters);
|
||||
return inlineLocalVarDialogResult(localName, project, toInline, initializer, toInline.size() == refs.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows dialog with question to inline
|
||||
*/
|
||||
@Nullable
|
||||
private static InlineHandler.Settings inlineLocalVarDialogResult(String localName, Project project) {
|
||||
private static InlineHandler.Settings inlineLocalVarDialogResult(String localName,
|
||||
Project project,
|
||||
Collection<GrReferenceExpression> refs,
|
||||
GrExpression initializer,
|
||||
boolean removeDeclaration) {
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
final String question = GroovyRefactoringBundle.message("inline.local.variable.prompt", localName);
|
||||
final String question = GroovyRefactoringBundle.message("inline.local.variable.prompt.0.1", localName, refs.size());
|
||||
RefactoringMessageDialog dialog = new RefactoringMessageDialog(INLINE_VARIABLE, question, HelpID.INLINE_VARIABLE, "OptionPane.questionIcon", true, project);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) {
|
||||
@@ -82,55 +236,6 @@ public class GroovyInlineVariableUtil {
|
||||
}
|
||||
}
|
||||
|
||||
return new InlineHandler.Settings() {
|
||||
public boolean isOnlyOneReferenceToInline() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static InlineHandler.Settings inlineFieldSettings(final GrField field, Editor editor, boolean invokedOnReference) {
|
||||
final Project project = field.getProject();
|
||||
|
||||
if (!field.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
String message = RefactoringBundle.message("0.refactoring.is.supported.only.for.final.fields", INLINE_FIELD);
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_FIELD, HelpID.INLINE_FIELD);
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
|
||||
if (field.getInitializerGroovy() == null) {
|
||||
String message = GroovyRefactoringBundle.message("cannot.find.a.single.definition.to.inline.field");
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_FIELD, HelpID.INLINE_FIELD);
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
|
||||
return inlineFieldDialogResult(project, field, invokedOnReference);
|
||||
}
|
||||
|
||||
private static InlineHandler.Settings inlineFieldDialogResult(Project project, GrField field, final boolean invokedOnReference) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return new InlineHandler.Settings() {
|
||||
@Override
|
||||
public boolean isOnlyOneReferenceToInline() {
|
||||
return invokedOnReference;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
final InlineGroovyFieldDialog dialog = new InlineGroovyFieldDialog(project, field, invokedOnReference);
|
||||
dialog.show();
|
||||
if (dialog.isOK()) {
|
||||
return new InlineHandler.Settings() {
|
||||
@Override
|
||||
public boolean isOnlyOneReferenceToInline() {
|
||||
return dialog.isInlineThisOnly();
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
WindowManager.getInstance().getStatusBar(project).setInfo(GroovyRefactoringBundle.message("press.escape.to.remove.the.highlighting"));
|
||||
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
|
||||
}
|
||||
return new InlineLocalVarSettings(initializer, refs, removeDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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 org.jetbrains.plugins.groovy.refactoring.inline;
|
||||
|
||||
import com.intellij.lang.refactoring.InlineHandler;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Max Medvedev
|
||||
*/
|
||||
public class InlineLocalVarSettings implements InlineHandler.Settings {
|
||||
private GrExpression myInitializer;
|
||||
private Collection<GrReferenceExpression> myRefs;
|
||||
private boolean myRemoveDeclaration;
|
||||
|
||||
public InlineLocalVarSettings(GrExpression initializer, Collection<GrReferenceExpression> refs, boolean removeDeclaration) {
|
||||
myInitializer = initializer;
|
||||
|
||||
myRefs = refs;
|
||||
myRemoveDeclaration = removeDeclaration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnlyOneReferenceToInline() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public GrExpression getInitializer() {
|
||||
return myInitializer;
|
||||
}
|
||||
|
||||
|
||||
public Collection<? extends PsiReference> getRefs() {
|
||||
return myRefs;
|
||||
}
|
||||
|
||||
public boolean isRemoveDeclaration() {
|
||||
return myRemoveDeclaration;
|
||||
}
|
||||
}
|
||||
+5
@@ -40,6 +40,11 @@ public class InlineVariableTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
public void testField() {doTest();}
|
||||
|
||||
public void testPartial1() {doTest();}
|
||||
public void testPartial2() {doTest();}
|
||||
public void testPartial3() {doTest();}
|
||||
public void testPartial4() {doTest();}
|
||||
|
||||
protected void doTest() {
|
||||
InlineMethodTest.doInlineTest(myFixture, getTestDataPath() + getTestName(true) + ".test", new GroovyInlineHandler());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
def a = 2
|
||||
|
||||
if (b) {
|
||||
print <selection>a</selection>
|
||||
}
|
||||
else {
|
||||
a = foo(a)
|
||||
}
|
||||
|
||||
print a
|
||||
-----
|
||||
def a
|
||||
|
||||
if (b) {
|
||||
print 2
|
||||
}
|
||||
else {
|
||||
a = foo(2)
|
||||
}
|
||||
|
||||
print a
|
||||
@@ -0,0 +1,9 @@
|
||||
def a = 4
|
||||
print <selection>a</selection>
|
||||
a = a+2+a
|
||||
print a
|
||||
-----
|
||||
def a
|
||||
print 4
|
||||
a = 4+2+4
|
||||
print a
|
||||
@@ -0,0 +1,5 @@
|
||||
def <selection>a</selection> = 3
|
||||
|
||||
a=3
|
||||
-----
|
||||
FAIL: Variable 'a' is never used
|
||||
@@ -0,0 +1,28 @@
|
||||
def a = 2
|
||||
print a
|
||||
a = 4
|
||||
|
||||
if (b) {
|
||||
print <selection>a</selection>
|
||||
}
|
||||
else {
|
||||
print a
|
||||
a = 5
|
||||
print a
|
||||
}
|
||||
|
||||
print a
|
||||
-----
|
||||
def a = 2
|
||||
print a
|
||||
|
||||
if (b) {
|
||||
print 4
|
||||
}
|
||||
else {
|
||||
print 4
|
||||
a = 5
|
||||
print a
|
||||
}
|
||||
|
||||
print a
|
||||
Reference in New Issue
Block a user