blob: acdcfc8062c2af3e894d95ac69d38c98a3163345 [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
/*
* Created by IntelliJ IDEA.
* User: mike
* Date: Aug 20, 2002
* Time: 8:49:24 PM
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.template.*;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.scope.BaseScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class RenameWrongRefFix implements IntentionAction {
private final PsiReferenceExpression myRefExpr;
@NonNls private static final String INPUT_VARIABLE_NAME = "INPUTVAR";
@NonNls private static final String OTHER_VARIABLE_NAME = "OTHERVAR";
private final boolean myUnresolvedOnly;
public RenameWrongRefFix(@NotNull PsiReferenceExpression refExpr) {
this(refExpr, false);
}
public RenameWrongRefFix(@NotNull PsiReferenceExpression refExpr, final boolean unresolvedOnly) {
myRefExpr = refExpr;
myUnresolvedOnly = unresolvedOnly;
}
@Override
@NotNull
public String getText() {
return QuickFixBundle.message("rename.wrong.reference.text");
}
@Override
@NotNull
public String getFamilyName() {
return QuickFixBundle.message("rename.wrong.reference.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
if (!myRefExpr.isValid() || !myRefExpr.getManager().isInProject(myRefExpr)) return false;
int offset = editor.getCaretModel().getOffset();
PsiElement refName = myRefExpr.getReferenceNameElement();
if (refName == null) return false;
TextRange textRange = refName.getTextRange();
if (textRange == null || offset < textRange.getStartOffset() ||
offset > textRange.getEndOffset()) {
return false;
}
return !CreateFromUsageUtils.isValidReference(myRefExpr, myUnresolvedOnly);
}
private class ReferenceNameExpression extends Expression {
class HammingComparator implements Comparator<LookupElement> {
@Override
public int compare(LookupElement lookupItem1, LookupElement lookupItem2) {
String s1 = lookupItem1.getLookupString();
String s2 = lookupItem2.getLookupString();
int diff1 = 0;
for (int i = 0; i < Math.min(s1.length(), myOldReferenceName.length()); i++) {
if (s1.charAt(i) != myOldReferenceName.charAt(i)) diff1++;
}
int diff2 = 0;
for (int i = 0; i < Math.min(s2.length(), myOldReferenceName.length()); i++) {
if (s2.charAt(i) != myOldReferenceName.charAt(i)) diff2++;
}
return diff1 - diff2;
}
}
ReferenceNameExpression(LookupElement[] items, String oldReferenceName) {
myItems = items;
myOldReferenceName = oldReferenceName;
Arrays.sort(myItems, new HammingComparator ());
}
LookupElement[] myItems;
private final String myOldReferenceName;
@Override
public Result calculateResult(ExpressionContext context) {
if (myItems == null || myItems.length == 0) {
return new TextResult(myOldReferenceName);
}
return new TextResult(myItems[0].getLookupString());
}
@Override
public Result calculateQuickResult(ExpressionContext context) {
return null;
}
@Override
public LookupElement[] calculateLookupItems(ExpressionContext context) {
if (myItems == null || myItems.length == 1) return null;
return myItems;
}
}
private LookupElement[] collectItems() {
Set<LookupElement> items = new LinkedHashSet<LookupElement>();
boolean qualified = myRefExpr.getQualifierExpression() != null;
if (!qualified && !(myRefExpr.getParent() instanceof PsiMethodCallExpression)) {
PsiVariable[] vars = CreateFromUsageUtils.guessMatchingVariables(myRefExpr);
for (PsiVariable var : vars) {
items.add(LookupElementBuilder.create(var.getName()));
}
} else {
class MyScopeProcessor extends BaseScopeProcessor {
ArrayList<PsiElement> myResult = new ArrayList<PsiElement>();
boolean myFilterMethods;
boolean myFilterStatics = false;
MyScopeProcessor(PsiReferenceExpression refExpression) {
myFilterMethods = refExpression.getParent() instanceof PsiMethodCallExpression;
PsiExpression qualifier = refExpression.getQualifierExpression();
if (qualifier instanceof PsiReferenceExpression) {
PsiElement e = ((PsiReferenceExpression) qualifier).resolve();
myFilterStatics = e instanceof PsiClass;
} else if (qualifier == null) {
PsiModifierListOwner scope = PsiTreeUtil.getParentOfType(refExpression, PsiModifierListOwner.class);
myFilterStatics = scope != null && scope.hasModifierProperty(PsiModifier.STATIC);
}
}
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
if (element instanceof PsiNamedElement
&& element instanceof PsiModifierListOwner
&& myFilterMethods == element instanceof PsiMethod) {
if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC) == myFilterStatics) {
myResult.add(element);
}
}
return true;
}
public PsiElement[] getVariants () {
return PsiUtilCore.toPsiElementArray(myResult);
}
}
items.add(LookupElementBuilder.create(myRefExpr.getReferenceName()));
MyScopeProcessor processor = new MyScopeProcessor(myRefExpr);
myRefExpr.processVariants(processor);
PsiElement[] variants = processor.getVariants();
for (PsiElement variant : variants) {
items.add(LookupElementBuilder.create(((PsiNamedElement)variant).getName()));
}
}
return items.toArray(new LookupElement[items.size()]);
}
@Override
public void invoke(@NotNull Project project, final Editor editor, PsiFile file) {
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
PsiReferenceExpression[] refs = CreateFromUsageUtils.collectExpressions(myRefExpr, PsiMember.class, PsiFile.class);
PsiElement element = PsiTreeUtil.getParentOfType(myRefExpr, PsiMember.class, PsiFile.class);
LookupElement[] items = collectItems();
ReferenceNameExpression refExpr = new ReferenceNameExpression(items, myRefExpr.getReferenceName());
TemplateBuilderImpl builder = new TemplateBuilderImpl(element);
for (PsiReferenceExpression expr : refs) {
if (!expr.equals(myRefExpr)) {
builder.replaceElement(expr.getReferenceNameElement(), OTHER_VARIABLE_NAME, INPUT_VARIABLE_NAME, false);
}
else {
builder.replaceElement(expr.getReferenceNameElement(), INPUT_VARIABLE_NAME, refExpr, true);
}
}
final float proportion = EditorUtil.calcVerticalScrollProportion(editor);
editor.getCaretModel().moveToOffset(element.getTextRange().getStartOffset());
/*for (int i = refs.length - 1; i >= 0; i--) {
TextRange range = refs[i].getReferenceNameElement().getTextRange();
document.deleteString(range.getStartOffset(), range.getEndOffset());
}
*/
Template template = builder.buildInlineTemplate();
editor.getCaretModel().moveToOffset(element.getTextRange().getStartOffset());
TemplateManager.getInstance(project).startTemplate(editor, template);
EditorUtil.setVerticalScrollProportion(editor, proportion);
}
@Override
public boolean startInWriteAction() {
return true;
}
}