| /* |
| * Copyright 2000-2012 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.refactoring.rename.inplace; |
| |
| import com.intellij.codeInsight.TargetElementUtilBase; |
| import com.intellij.lang.findUsages.DescriptiveNameUtil; |
| import com.intellij.lang.injection.InjectedLanguageManager; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.command.impl.FinishMarkAction; |
| import com.intellij.openapi.command.impl.StartMarkAction; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.RangeMarker; |
| import com.intellij.openapi.editor.impl.EditorImpl; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.fileEditor.FileEditorManager; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; |
| import com.intellij.psi.search.LocalSearchScope; |
| import com.intellij.psi.search.ProjectScope; |
| import com.intellij.psi.search.SearchScope; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.refactoring.RefactoringBundle; |
| import com.intellij.refactoring.rename.RenameProcessor; |
| import com.intellij.refactoring.rename.RenamePsiElementProcessor; |
| import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory; |
| import com.intellij.usageView.UsageViewUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| /** |
| * User: anna |
| * Date: 11/9/11 |
| */ |
| public class MemberInplaceRenamer extends VariableInplaceRenamer { |
| private final PsiElement mySubstituted; |
| private RangeMarker mySubstitutedRange; |
| |
| public MemberInplaceRenamer(@NotNull PsiNamedElement elementToRename, PsiElement substituted, Editor editor) { |
| this(elementToRename, substituted, editor, elementToRename.getName(), elementToRename.getName()); |
| } |
| |
| public MemberInplaceRenamer(@NotNull PsiNamedElement elementToRename, PsiElement substituted, Editor editor, String initialName, String oldName) { |
| super(elementToRename, editor, elementToRename.getProject(), initialName, oldName); |
| mySubstituted = substituted; |
| if (mySubstituted != null && mySubstituted != myElementToRename && mySubstituted.getTextRange() != null) { |
| final PsiFile containingFile = mySubstituted.getContainingFile(); |
| if (!notSameFile(containingFile.getVirtualFile(), containingFile)) { |
| mySubstitutedRange = myEditor.getDocument().createRangeMarker(mySubstituted.getTextRange()); |
| } |
| } |
| else { |
| mySubstitutedRange = null; |
| } |
| |
| showDialogAdvertisement("RenameElement"); |
| } |
| |
| @Override |
| protected VariableInplaceRenamer createInplaceRenamerToRestart(PsiNamedElement variable, Editor editor, String initialName) { |
| return new MemberInplaceRenamer(variable, getSubstituted(), editor, initialName, myOldName); |
| } |
| |
| @Override |
| protected boolean acceptReference(PsiReference reference) { |
| final PsiElement element = reference.getElement(); |
| final TextRange textRange = reference.getRangeInElement(); |
| final String referenceText = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset()); |
| return Comparing.strEqual(referenceText, myElementToRename.getName()); |
| } |
| |
| @Override |
| protected PsiElement checkLocalScope() { |
| PsiFile currentFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); |
| if (currentFile != null) { |
| return currentFile; |
| } |
| return super.checkLocalScope(); |
| } |
| |
| @Override |
| protected PsiElement getNameIdentifier() { |
| final PsiFile currentFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); |
| if (currentFile == myElementToRename.getContainingFile()){ |
| return super.getNameIdentifier(); |
| } |
| if (currentFile != null) { |
| int offset = myEditor.getCaretModel().getOffset(); |
| offset = TargetElementUtilBase.adjustOffset(currentFile, myEditor.getDocument(), offset); |
| final PsiElement elementAt = currentFile.findElementAt(offset); |
| if (elementAt != null) { |
| final PsiElement referenceExpression = elementAt.getParent(); |
| if (referenceExpression != null) { |
| final PsiReference reference = referenceExpression.getReference(); |
| if (reference != null && reference.resolve() == myElementToRename) { |
| return elementAt; |
| } |
| } |
| } |
| return null; |
| } |
| return null; |
| } |
| |
| @Override |
| protected Collection<PsiReference> collectRefs(SearchScope referencesSearchScope) { |
| final ArrayList<PsiReference> references = new ArrayList<PsiReference>(super.collectRefs(referencesSearchScope)); |
| final PsiNamedElement variable = getVariable(); |
| if (variable != null) { |
| final PsiElement substituted = getSubstituted(); |
| if (substituted != null && substituted != variable) { |
| references.addAll(ReferencesSearch.search(substituted, referencesSearchScope, false).findAll()); |
| } |
| } |
| return references; |
| } |
| |
| @Override |
| protected boolean notSameFile(@Nullable VirtualFile file, @NotNull PsiFile containingFile) { |
| final PsiFile currentFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); |
| if (currentFile == null) return true; |
| InjectedLanguageManager manager = InjectedLanguageManager.getInstance(containingFile.getProject()); |
| return manager.getTopLevelFile(containingFile) != manager.getTopLevelFile(currentFile); |
| } |
| |
| @Override |
| protected SearchScope getReferencesSearchScope(VirtualFile file) { |
| PsiFile currentFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); |
| return currentFile != null ? new LocalSearchScope(currentFile) |
| : ProjectScope.getProjectScope(myProject); |
| } |
| |
| @Override |
| protected boolean appendAdditionalElement(Collection<PsiReference> refs, Collection<Pair<PsiElement, TextRange>> stringUsages) { |
| boolean showChooser = super.appendAdditionalElement(refs, stringUsages); |
| PsiNamedElement variable = getVariable(); |
| if (variable != null) { |
| final PsiElement substituted = getSubstituted(); |
| if (substituted != null) { |
| appendAdditionalElement(stringUsages, variable, substituted); |
| RenamePsiElementProcessor processor = RenamePsiElementProcessor.forElement(substituted); |
| final HashMap<PsiElement, String> allRenames = new HashMap<PsiElement, String>(); |
| PsiFile currentFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); |
| processor.prepareRenaming(substituted, "", allRenames, new LocalSearchScope(currentFile)); |
| for (PsiElement element : allRenames.keySet()) { |
| appendAdditionalElement(stringUsages, variable, element); |
| } |
| } |
| } |
| return showChooser; |
| } |
| |
| @Override |
| protected boolean shouldCreateSnapshot() { |
| return false; |
| } |
| |
| @Override |
| protected String getRefactoringId() { |
| return null; |
| } |
| |
| private void appendAdditionalElement(Collection<Pair<PsiElement, TextRange>> stringUsages, |
| PsiNamedElement variable, |
| PsiElement element) { |
| if (element != variable && element instanceof PsiNameIdentifierOwner && |
| !notSameFile(null, element.getContainingFile())) { |
| final PsiElement identifier = ((PsiNameIdentifierOwner)element).getNameIdentifier(); |
| if (identifier != null) { |
| stringUsages.add(Pair.create(identifier, new TextRange(0, identifier.getTextLength()))); |
| } |
| } |
| } |
| |
| @Override |
| protected void performRefactoringRename(final String newName, |
| final StartMarkAction markAction) { |
| try { |
| final PsiNamedElement variable = getVariable(); |
| if (variable != null && !newName.equals(myOldName)) { |
| if (isIdentifier(newName, variable.getLanguage())) { |
| final PsiElement substituted = getSubstituted(); |
| if (substituted == null) { |
| return; |
| } |
| |
| final String commandName = RefactoringBundle |
| .message("renaming.0.1.to.2", UsageViewUtil.getType(variable), DescriptiveNameUtil.getDescriptiveName(variable), newName); |
| CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { |
| @Override |
| public void run() { |
| performRenameInner(substituted, newName); |
| PsiDocumentManager.getInstance(myProject).commitAllDocuments(); |
| } |
| }, commandName, null); |
| } |
| } |
| } |
| finally { |
| try { |
| ((EditorImpl)InjectedLanguageUtil.getTopLevelEditor(myEditor)).stopDumbLater(); |
| } |
| finally { |
| FinishMarkAction.finish(myProject, myEditor, markAction); |
| } |
| } |
| } |
| |
| protected void performRenameInner(PsiElement element, String newName) { |
| final RenamePsiElementProcessor elementProcessor = RenamePsiElementProcessor.forElement(element); |
| final RenameProcessor |
| renameProcessor = new RenameProcessor(myProject, element, newName, |
| elementProcessor.isToSearchInComments(element), |
| elementProcessor.isToSearchForTextOccurrences(element)){ |
| @Nullable |
| @Override |
| protected String getRefactoringId() { |
| return "refactoring.inplace.rename"; |
| } |
| |
| @Override |
| public void doRun() { |
| try { |
| super.doRun(); |
| } |
| finally { |
| restoreCaretOffsetAfterRename(); |
| } |
| } |
| }; |
| for (AutomaticRenamerFactory factory : Extensions.getExtensions(AutomaticRenamerFactory.EP_NAME)) { |
| if (factory.getOptionName() != null && factory.isApplicable(element)) { |
| renameProcessor.addRenamerFactory(factory); |
| } |
| } |
| renameProcessor.run(); |
| } |
| |
| protected void restoreCaretOffsetAfterRename() { |
| if (myBeforeRevert != null) { |
| myEditor.getCaretModel().moveToOffset(myBeforeRevert.getEndOffset()); |
| myBeforeRevert.dispose(); |
| } |
| } |
| |
| @Override |
| protected void collectAdditionalElementsToRename(List<Pair<PsiElement, TextRange>> stringUsages) { |
| //do not highlight non-code usages in file |
| } |
| |
| @Override |
| protected void revertStateOnFinish() { |
| final Editor editor = InjectedLanguageUtil.getTopLevelEditor(myEditor); |
| if (editor == FileEditorManager.getInstance(myProject).getSelectedTextEditor()) { |
| ((EditorImpl)editor).startDumb(); |
| } |
| revertState(); |
| } |
| |
| @Nullable |
| public PsiElement getSubstituted() { |
| if (mySubstituted != null && mySubstituted.isValid()){ |
| if (mySubstituted instanceof PsiNameIdentifierOwner) { |
| if (Comparing.strEqual(myOldName, ((PsiNameIdentifierOwner)mySubstituted).getName())) return mySubstituted; |
| |
| final RangeMarker rangeMarker = mySubstitutedRange != null ? mySubstitutedRange : myRenameOffset; |
| if (rangeMarker != null) |
| return PsiTreeUtil.findElementOfClassAtRange(mySubstituted.getContainingFile(), rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), PsiNameIdentifierOwner.class); |
| } |
| return mySubstituted; |
| } |
| if (mySubstitutedRange != null) { |
| final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); |
| if (psiFile != null) { |
| return PsiTreeUtil.findElementOfClassAtRange(psiFile, mySubstitutedRange.getStartOffset(), mySubstitutedRange.getEndOffset(), PsiNameIdentifierOwner.class); |
| } |
| } |
| return getVariable(); |
| } |
| } |