mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
inplace: decouple introduces from rename (I)
This commit is contained in:
+21
-27
@@ -172,33 +172,31 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveOffsetAfter(boolean success) {
|
||||
if (success) {
|
||||
JavaRefactoringSettings.getInstance().INTRODUCE_CONSTANT_MOVE_TO_ANOTHER_CLASS = myMoveToAnotherClassCb.isSelected();
|
||||
if (myMoveToAnotherClassCb.isSelected()) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myEditor.putUserData(ACTIVE_INTRODUCE, InplaceIntroduceConstantPopup.this);
|
||||
try {
|
||||
final IntroduceConstantHandler constantHandler = new IntroduceConstantHandler();
|
||||
final PsiLocalVariable localVariable = (PsiLocalVariable)getLocalVariable();
|
||||
if (localVariable != null) {
|
||||
constantHandler.invokeImpl(myProject, localVariable, myEditor);
|
||||
}
|
||||
else {
|
||||
constantHandler.invokeImpl(myProject, myExpr, myEditor);
|
||||
}
|
||||
protected boolean performRefactoring() {
|
||||
JavaRefactoringSettings.getInstance().INTRODUCE_CONSTANT_MOVE_TO_ANOTHER_CLASS = myMoveToAnotherClassCb.isSelected();
|
||||
if (myMoveToAnotherClassCb.isSelected()) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myEditor.putUserData(ACTIVE_INTRODUCE, InplaceIntroduceConstantPopup.this);
|
||||
try {
|
||||
final IntroduceConstantHandler constantHandler = new IntroduceConstantHandler();
|
||||
final PsiLocalVariable localVariable = (PsiLocalVariable)getLocalVariable();
|
||||
if (localVariable != null) {
|
||||
constantHandler.invokeImpl(myProject, localVariable, myEditor);
|
||||
}
|
||||
finally {
|
||||
myEditor.putUserData(ACTIVE_INTRODUCE, null);
|
||||
else {
|
||||
constantHandler.invokeImpl(myProject, myExpr, myEditor);
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
myEditor.putUserData(ACTIVE_INTRODUCE, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
super.moveOffsetAfter(success);
|
||||
return super.performRefactoring();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -249,8 +247,4 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField
|
||||
protected String getActionName() {
|
||||
return "IntroduceConstant";
|
||||
}
|
||||
|
||||
public String getCommandName() {
|
||||
return IntroduceConstantHandler.REFACTORING_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -185,11 +185,6 @@ public class InplaceIntroduceFieldPopup extends AbstractInplaceIntroduceFieldPop
|
||||
return "IntroduceField";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCommandName() {
|
||||
return IntroduceFieldHandler.REFACTORING_NAME;
|
||||
}
|
||||
|
||||
public BaseExpressionToFieldHandler.InitializationPlace getInitializerPlace() {
|
||||
return myIntroduceFieldPanel.getInitializerPlace();
|
||||
}
|
||||
|
||||
-4
@@ -287,10 +287,6 @@ public class InplaceIntroduceParameterPopup extends AbstractJavaInplaceIntroduce
|
||||
return "IntroduceParameter";
|
||||
}
|
||||
|
||||
public String getCommandName() {
|
||||
return IntroduceParameterHandler.REFACTORING_NAME;
|
||||
}
|
||||
|
||||
private void normalizeParameterIdxAccordingToRemovedParams(TIntArrayList parametersToRemove) {
|
||||
parametersToRemove.forEach(new TIntProcedure() {
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -573,7 +573,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
|
||||
IntroduceVariableBase.REFACTORING_NAME);
|
||||
renamer.initInitialText(expressionText);
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
|
||||
renamer.performInplaceRename(false, new LinkedHashSet<String>(Arrays.asList(suggestedName.names)));
|
||||
renamer.performInplaceRefactoring(new LinkedHashSet<String>(Arrays.asList(suggestedName.names)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -194,7 +194,7 @@ public class JavaVariableInplaceIntroducer extends InplaceVariableIntroducer<Psi
|
||||
myCanBeFinalCb.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
new WriteCommandAction(myProject, IntroduceVariableBase.REFACTORING_NAME, IntroduceVariableBase.REFACTORING_NAME) {
|
||||
new WriteCommandAction(myProject, getCommandName(), getCommandName()) {
|
||||
@Override
|
||||
protected void run(com.intellij.openapi.application.Result result) throws Throwable {
|
||||
PsiDocumentManager.getInstance(myProject).commitDocument(myEditor.getDocument());
|
||||
|
||||
+3
-3
@@ -27,7 +27,7 @@ import com.intellij.psi.scope.processor.VariablesProcessor;
|
||||
import com.intellij.psi.scope.util.PsiScopesUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
|
||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -153,10 +153,10 @@ public class ReassignVariableUtil {
|
||||
|
||||
private static void finishTemplate(Editor editor) {
|
||||
final TemplateState templateState = TemplateManagerImpl.getTemplateState(editor);
|
||||
final VariableInplaceRenamer renamer = editor.getUserData(VariableInplaceRenamer.INPLACE_RENAMER);
|
||||
final InplaceRefactoring renamer = editor.getUserData(InplaceRefactoring.INPLACE_RENAMER);
|
||||
if (templateState != null && renamer != null) {
|
||||
templateState.gotoEnd(true);
|
||||
editor.putUserData(VariableInplaceRenamer.INPLACE_RENAMER, null);
|
||||
editor.putUserData(InplaceRefactoring.INPLACE_RENAMER, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.impl.source.codeStyle.CodeStyleManagerImpl;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
|
||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.PairProcessor;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
@@ -505,7 +505,7 @@ public class TemplateState implements Disposable {
|
||||
final LookupImpl lookup = (LookupImpl)lookupManager.showLookup(myEditor, lookupItems);
|
||||
if (lookup == null) return;
|
||||
|
||||
if (CodeInsightSettings.getInstance().AUTO_POPUP_COMPLETION_LOOKUP && myEditor.getUserData(VariableInplaceRenamer.INPLACE_RENAMER) == null) {
|
||||
if (CodeInsightSettings.getInstance().AUTO_POPUP_COMPLETION_LOOKUP && myEditor.getUserData(InplaceRefactoring.INPLACE_RENAMER) == null) {
|
||||
lookup.setStartCompletionWhenNothingMatches(true);
|
||||
}
|
||||
|
||||
|
||||
+30
-47
@@ -40,11 +40,10 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
|
||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.ui.DottedBorder;
|
||||
import com.intellij.util.ui.PositionTracker;
|
||||
@@ -70,7 +69,6 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
protected final String myExprText;
|
||||
private final String myLocalName;
|
||||
|
||||
protected String myConstantName;
|
||||
public static final Key<AbstractInplaceIntroducer> ACTIVE_INTRODUCE = Key.create("ACTIVE_INTRODUCE");
|
||||
|
||||
private EditorEx myPreview;
|
||||
@@ -168,13 +166,6 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
*/
|
||||
protected abstract String getActionName();
|
||||
|
||||
/**
|
||||
* Returns the name of the command performed by the refactoring.
|
||||
*
|
||||
* @return command name
|
||||
*/
|
||||
protected abstract String getCommandName();
|
||||
|
||||
/**
|
||||
* Creates an initial version of the declaration for the introduced element. Note that this method is not called in a write action
|
||||
* and most likely needs to create one itself.
|
||||
@@ -231,7 +222,7 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
initOccurrencesMarkers();
|
||||
setElementToRename(variable);
|
||||
updateTitle(getVariable());
|
||||
started = AbstractInplaceIntroducer.super.performInplaceRename(false, nameSuggestions);
|
||||
started = AbstractInplaceIntroducer.super.performInplaceRefactoring(nameSuggestions);
|
||||
if (started) {
|
||||
myDocumentAdapter = new DocumentAdapter() {
|
||||
@Override
|
||||
@@ -239,7 +230,7 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
if (myPreview == null) return;
|
||||
final TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
|
||||
if (templateState != null) {
|
||||
final TextResult value = templateState.getVariableValue(VariableInplaceRenamer.PRIMARY_VARIABLE_NAME);
|
||||
final TextResult value = templateState.getVariableValue(InplaceRefactoring.PRIMARY_VARIABLE_NAME);
|
||||
if (value != null) {
|
||||
updateTitle(getVariable(), value.getText());
|
||||
}
|
||||
@@ -320,7 +311,7 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
}
|
||||
|
||||
public String getInputName() {
|
||||
return myConstantName;
|
||||
return myInsertedName;
|
||||
}
|
||||
|
||||
|
||||
@@ -367,12 +358,7 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean appendAdditionalElement(List<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collectAdditionalElementsToRename(boolean processTextOccurrences, List<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
protected void collectAdditionalElementsToRename(List<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
if (isReplaceAllOccurrences()) {
|
||||
for (E expression : getOccurrences()) {
|
||||
stringUsages.add(Pair.<PsiElement, TextRange>create(expression, new TextRange(0, expression.getTextLength())));
|
||||
@@ -425,7 +411,6 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
}
|
||||
|
||||
protected void restoreState(final V psiField) {
|
||||
myConstantName = psiField.getName();
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
final PsiFile containingFile = psiField.getContainingFile();
|
||||
@@ -480,29 +465,32 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveOffsetAfter(boolean success) {
|
||||
if (success) {
|
||||
if (getLocalVariable() == null && myExpr == null ||
|
||||
getInputName() == null ||
|
||||
getLocalVariable() != null && !getLocalVariable().isValid() ||
|
||||
myExpr != null && !myExpr.isValid()) {
|
||||
super.moveOffsetAfter(false);
|
||||
return;
|
||||
}
|
||||
if (getLocalVariable() != null) {
|
||||
new WriteCommandAction(myProject, getCommandName(), getCommandName()) {
|
||||
@Override
|
||||
protected void run(Result result) throws Throwable {
|
||||
getLocalVariable().setName(myLocalName);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
performIntroduce();
|
||||
V variable = getVariable();
|
||||
if (variable != null) {
|
||||
saveSettings(variable);
|
||||
}
|
||||
protected boolean performRefactoring() {
|
||||
if (getLocalVariable() == null && myExpr == null ||
|
||||
getInputName() == null ||
|
||||
getLocalVariable() != null && !getLocalVariable().isValid() ||
|
||||
myExpr != null && !myExpr.isValid()) {
|
||||
super.moveOffsetAfter(false);
|
||||
return false;
|
||||
}
|
||||
if (getLocalVariable() != null) {
|
||||
new WriteCommandAction(myProject, getCommandName(), getCommandName()) {
|
||||
@Override
|
||||
protected void run(Result result) throws Throwable {
|
||||
getLocalVariable().setName(myLocalName);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
performIntroduce();
|
||||
V variable = getVariable();
|
||||
if (variable != null) {
|
||||
saveSettings(variable);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveOffsetAfter(boolean success) {
|
||||
if (getLocalVariable() != null && getLocalVariable().isValid()) {
|
||||
myEditor.getCaretModel().moveToOffset(getLocalVariable().getTextOffset());
|
||||
myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
|
||||
@@ -552,11 +540,6 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void restoreStateBeforeDialogWouldBeShown() {
|
||||
stopIntroduce(InjectedLanguageUtil.getTopLevelEditor(myEditor));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void navigateToAlreadyStarted(Document oldDocument, int exitCode) {
|
||||
finish();
|
||||
|
||||
+30
-27
@@ -25,20 +25,16 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.impl.StartMarkAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.SelectionModel;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.BalloonBuilder;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.refactoring.rename.NameSuggestionProvider;
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
|
||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.util.ui.PositionTracker;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -54,7 +50,7 @@ import java.util.List;
|
||||
* User: anna
|
||||
* Date: 3/15/11
|
||||
*/
|
||||
public class InplaceVariableIntroducer<E extends PsiElement> extends VariableInplaceRenamer {
|
||||
public abstract class InplaceVariableIntroducer<E extends PsiElement> extends InplaceRefactoring {
|
||||
public static final Key<Boolean> INTRODUCE_RESTART = Key.create("INTRODUCE_RESTART");
|
||||
|
||||
protected E myExpr;
|
||||
@@ -66,13 +62,14 @@ public class InplaceVariableIntroducer<E extends PsiElement> extends VariableInp
|
||||
protected Balloon myBalloon;
|
||||
protected String myTitle;
|
||||
protected RelativePoint myTarget;
|
||||
private RangeMarker myCaretRangeMarker;
|
||||
|
||||
public InplaceVariableIntroducer(PsiNamedElement elementToRename,
|
||||
Editor editor,
|
||||
Project project,
|
||||
String title, E[] occurrences,
|
||||
@Nullable E expr) {
|
||||
super(elementToRename, editor, project);
|
||||
super(editor, elementToRename, project);
|
||||
myTitle = title;
|
||||
myOccurrences = occurrences;
|
||||
myExpr = expr;
|
||||
@@ -86,8 +83,8 @@ public class InplaceVariableIntroducer<E extends PsiElement> extends VariableInp
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getOffsetForCaret(RangeMarker rangeMarker, int offset) {
|
||||
return rangeMarker.isValid() ? rangeMarker.getStartOffset() : offset;
|
||||
protected int restoreCaretOffset(int offset) {
|
||||
return myCaretRangeMarker.isValid() ? myCaretRangeMarker.getStartOffset() : offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,8 +138,28 @@ public class InplaceVariableIntroducer<E extends PsiElement> extends VariableInp
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performInplaceRename(boolean processTextOccurrences, LinkedHashSet<String> nameSuggestions) {
|
||||
final boolean result = super.performInplaceRename(processTextOccurrences, nameSuggestions);
|
||||
protected boolean performRefactoring() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeTemplateStart() {
|
||||
myCaretRangeMarker = myEditor.getDocument()
|
||||
.createRangeMarker(new TextRange(myEditor.getCaretModel().getOffset(), myEditor.getCaretModel().getOffset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collectAdditionalElementsToRename(List<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCommandName() {
|
||||
return myTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performInplaceRefactoring(LinkedHashSet<String> nameSuggestions) {
|
||||
final boolean result = super.performInplaceRefactoring(nameSuggestions);
|
||||
if (result) {
|
||||
if (myBalloon == null) {
|
||||
showBalloon();
|
||||
@@ -225,21 +242,7 @@ public class InplaceVariableIntroducer<E extends PsiElement> extends VariableInp
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.createLookupItems(lookupItems, name);
|
||||
return lookupItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextRange preserveSelectedRange(SelectionModel selectionModel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performOnInvalidIdentifier(String newName, LinkedHashSet<String> nameSuggestions) {
|
||||
//move logic to performRefactoring
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean performAutomaticRename() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ public class CompletionContributorForInplaceRename extends CompletionContributor
|
||||
final Editor editor = parameters.getLookup().getEditor();
|
||||
final TemplateState state = TemplateManagerImpl.getTemplateState(editor);
|
||||
if (state != null) {
|
||||
if (editor.getUserData(VariableInplaceRenamer.INPLACE_RENAMER) != null) {
|
||||
if (editor.getUserData(InplaceRefactoring.INPLACE_RENAMER) != null) {
|
||||
result.stopHere();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class EscapeHandler extends EditorActionHandler {
|
||||
final SelectionModel selectionModel = editor.getSelectionModel();
|
||||
if (selectionModel.hasSelection()) {
|
||||
final TemplateState state = TemplateManagerImpl.getTemplateState(editor);
|
||||
if (state != null && editor.getUserData(VariableInplaceRenamer.INPLACE_RENAMER) != null) {
|
||||
if (state != null && editor.getUserData(InplaceRefactoring.INPLACE_RENAMER) != null) {
|
||||
final LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(editor);
|
||||
if (lookup != null) {
|
||||
selectionModel.removeSelection();
|
||||
|
||||
+722
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
* 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.completion.InsertHandler;
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.codeInsight.lookup.LookupManager;
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
||||
import com.intellij.codeInsight.template.*;
|
||||
import com.intellij.codeInsight.template.Result;
|
||||
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
|
||||
import com.intellij.codeInsight.template.impl.TemplateState;
|
||||
import com.intellij.injected.editor.VirtualFileWindow;
|
||||
import com.intellij.openapi.actionSystem.Shortcut;
|
||||
import com.intellij.openapi.application.*;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.command.impl.FinishMarkAction;
|
||||
import com.intellij.openapi.command.impl.StartMarkAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
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.extensions.Extensions;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.fileEditor.TextEditor;
|
||||
import com.intellij.openapi.keymap.Keymap;
|
||||
import com.intellij.openapi.keymap.KeymapManager;
|
||||
import com.intellij.openapi.keymap.KeymapUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Key;
|
||||
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.PsiSearchHelper;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.rename.NameSuggestionProvider;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
* Date: 1/11/12
|
||||
*/
|
||||
public abstract class InplaceRefactoring {
|
||||
protected static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.rename.inplace.VariableInplaceRenamer");
|
||||
@NonNls protected static final String PRIMARY_VARIABLE_NAME = "PrimaryVariable";
|
||||
@NonNls protected static final String OTHER_VARIABLE_NAME = "OtherVariable";
|
||||
protected static final Stack<InplaceRefactoring> ourRenamersStack = new Stack<InplaceRefactoring>();
|
||||
public static final Key<InplaceRefactoring> INPLACE_RENAMER = Key.create("EditorInplaceRenamer");
|
||||
protected PsiNamedElement myElementToRename;
|
||||
protected final Editor myEditor;
|
||||
protected final Project myProject;
|
||||
protected RangeMarker myRenameOffset;
|
||||
private String myAdvertisementText;
|
||||
private ArrayList<RangeHighlighter> myHighlighters;
|
||||
protected String myInitialName;
|
||||
protected final String myOldName;
|
||||
protected RangeMarker myBeforeRevert = null;
|
||||
protected String myInsertedName;
|
||||
protected LinkedHashSet<String> myNameSuggestions;
|
||||
|
||||
protected StartMarkAction myMarkAction;
|
||||
protected PsiElement myScope;
|
||||
|
||||
public InplaceRefactoring(Editor editor, PsiNamedElement elementToRename, Project project) {
|
||||
this(editor, elementToRename, project, elementToRename != null ? elementToRename.getName() : null,
|
||||
elementToRename != null ? elementToRename.getName() : null);
|
||||
}
|
||||
|
||||
public InplaceRefactoring(Editor editor, PsiNamedElement elementToRename, Project project, final String oldName) {
|
||||
this(editor, elementToRename, project, elementToRename != null ? elementToRename.getName() : null, oldName);
|
||||
}
|
||||
|
||||
public InplaceRefactoring(
|
||||
Editor editor, PsiNamedElement elementToRename, Project project, String initialName, final String oldName) {
|
||||
myEditor = /*(editor instanceof EditorWindow)? ((EditorWindow)editor).getDelegate() : */editor;
|
||||
myElementToRename = elementToRename;
|
||||
myProject = project;
|
||||
myOldName = oldName;
|
||||
if (myElementToRename != null) {
|
||||
myInitialName = initialName;
|
||||
final PsiFile containingFile = myElementToRename.getContainingFile();
|
||||
if (!notSameFile(getTopLevelVirtualFile(containingFile.getViewProvider()), containingFile)) {
|
||||
myRenameOffset = myElementToRename != null && myElementToRename.getTextRange() != null ? myEditor.getDocument()
|
||||
.createRangeMarker(myElementToRename.getTextRange()) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setAdvertisementText(String advertisementText) {
|
||||
myAdvertisementText = advertisementText;
|
||||
}
|
||||
|
||||
|
||||
public boolean performInplaceRefactoring(final LinkedHashSet<String> nameSuggestions) {
|
||||
myNameSuggestions = nameSuggestions;
|
||||
if (InjectedLanguageUtil.isInInjectedLanguagePrefixSuffix(myElementToRename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final FileViewProvider fileViewProvider = myElementToRename.getContainingFile().getViewProvider();
|
||||
VirtualFile file = getTopLevelVirtualFile(fileViewProvider);
|
||||
|
||||
SearchScope referencesSearchScope = getReferencesSearchScope(file);
|
||||
|
||||
final Collection<PsiReference> refs = collectRefs(referencesSearchScope);
|
||||
|
||||
addReferenceAtCaret(refs);
|
||||
|
||||
for (PsiReference ref : refs) {
|
||||
final PsiFile containingFile = ref.getElement().getContainingFile();
|
||||
|
||||
if (notSameFile(file, containingFile)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiElement scope = checkLocalScope();
|
||||
|
||||
if (scope == null) {
|
||||
return false; // Should have valid local search scope for inplace rename
|
||||
}
|
||||
|
||||
final PsiFile containingFile = scope.getContainingFile();
|
||||
if (containingFile == null) {
|
||||
return false; // Should have valid local search scope for inplace rename
|
||||
}
|
||||
//no need to process further when file is read-only
|
||||
if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, containingFile)) return true;
|
||||
|
||||
myEditor.putUserData(INPLACE_RENAMER, this);
|
||||
ourRenamersStack.push(this);
|
||||
|
||||
final List<Pair<PsiElement, TextRange>> stringUsages = new ArrayList<Pair<PsiElement, TextRange>>();
|
||||
collectAdditionalElementsToRename(stringUsages);
|
||||
return buildTemplateAndStart(refs, stringUsages, scope, containingFile);
|
||||
}
|
||||
|
||||
protected boolean notSameFile(@Nullable VirtualFile file, PsiFile containingFile) {
|
||||
return getTopLevelVirtualFile(containingFile.getViewProvider()) != file;
|
||||
}
|
||||
|
||||
protected SearchScope getReferencesSearchScope(VirtualFile file) {
|
||||
return file == null || ProjectRootManager.getInstance(myProject).getFileIndex().isInContent(file)
|
||||
? ProjectScope.getProjectScope(myElementToRename.getProject())
|
||||
: new LocalSearchScope(myElementToRename.getContainingFile());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected PsiElement checkLocalScope() {
|
||||
final SearchScope searchScope = PsiSearchHelper.SERVICE.getInstance(myElementToRename.getProject()).getUseScope(myElementToRename);
|
||||
if (searchScope instanceof LocalSearchScope) {
|
||||
final PsiElement[] elements = ((LocalSearchScope)searchScope).getScope();
|
||||
return PsiTreeUtil.findCommonParent(elements);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract void collectAdditionalElementsToRename(final List<Pair<PsiElement, TextRange>> stringUsages);
|
||||
|
||||
protected abstract boolean shouldSelectAll();
|
||||
|
||||
protected abstract LookupElement[] createLookupItems(LookupElement[] lookupItems, String name);
|
||||
|
||||
protected Collection<PsiReference> collectRefs(SearchScope referencesSearchScope) {
|
||||
return ReferencesSearch.search(myElementToRename, referencesSearchScope, false).findAll();
|
||||
}
|
||||
|
||||
protected boolean buildTemplateAndStart(final Collection<PsiReference> refs,
|
||||
final Collection<Pair<PsiElement, TextRange>> stringUsages,
|
||||
final PsiElement scope,
|
||||
final PsiFile containingFile) {
|
||||
final PsiElement context = containingFile.getContext();
|
||||
myScope = context != null ? context.getContainingFile() : scope;
|
||||
final TemplateBuilderImpl builder = new TemplateBuilderImpl(myScope);
|
||||
|
||||
PsiElement nameIdentifier = getNameIdentifier();
|
||||
int offset = myEditor.getCaretModel().getOffset();
|
||||
PsiElement selectedElement = getSelectedInEditorElement(nameIdentifier, refs, stringUsages, offset);
|
||||
|
||||
if (nameIdentifier != null) addVariable(nameIdentifier, selectedElement, builder);
|
||||
for (PsiReference ref : refs) {
|
||||
addVariable(ref, selectedElement, builder, offset);
|
||||
}
|
||||
for (Pair<PsiElement, TextRange> usage : stringUsages) {
|
||||
addVariable(usage.first, usage.second, selectedElement, builder);
|
||||
}
|
||||
addAdditionalVariables(builder);
|
||||
try {
|
||||
myMarkAction = startRename();
|
||||
}
|
||||
catch (final StartMarkAction.AlreadyStartedException e) {
|
||||
final Document oldDocument = e.getDocument();
|
||||
if (oldDocument != myEditor.getDocument()) {
|
||||
final int exitCode = Messages.showOkCancelDialog(myProject, e.getMessage(), getCommandName(),
|
||||
"Navigate to continue", "Cancel started", Messages.getErrorIcon());
|
||||
if (exitCode == -1) return true;
|
||||
navigateToAlreadyStarted(oldDocument, exitCode);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
revertState();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
beforeTemplateStart();
|
||||
|
||||
new WriteCommandAction(myProject, getCommandName()) {
|
||||
@Override
|
||||
protected void run(com.intellij.openapi.application.Result result) throws Throwable {
|
||||
startTemplate(builder);
|
||||
}
|
||||
}.execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void beforeTemplateStart();
|
||||
|
||||
private void startTemplate(final TemplateBuilderImpl builder) {
|
||||
|
||||
final DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject);
|
||||
|
||||
final boolean previousUpdate;
|
||||
if (daemonCodeAnalyzer != null) {
|
||||
previousUpdate = ((DaemonCodeAnalyzerImpl)daemonCodeAnalyzer).isUpdateByTimerEnabled();
|
||||
daemonCodeAnalyzer.setUpdateByTimerEnabled(false);
|
||||
}
|
||||
else {
|
||||
previousUpdate = false;
|
||||
}
|
||||
|
||||
final MyTemplateListener templateListener = new MyTemplateListener() {
|
||||
@Override
|
||||
protected void restoreDaemonUpdateState() {
|
||||
if (daemonCodeAnalyzer != null) {
|
||||
daemonCodeAnalyzer.setUpdateByTimerEnabled(previousUpdate);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final int offset = myEditor.getCaretModel().getOffset();
|
||||
|
||||
Template template = builder.buildInlineTemplate();
|
||||
template.setToShortenLongNames(false);
|
||||
TextRange range = myScope.getTextRange();
|
||||
assert range != null;
|
||||
myHighlighters = new ArrayList<RangeHighlighter>();
|
||||
Editor topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(myEditor);
|
||||
topLevelEditor.getCaretModel().moveToOffset(range.getStartOffset());
|
||||
|
||||
TemplateManager.getInstance(myProject).startTemplate(topLevelEditor, template, templateListener);
|
||||
restoreOldCaretPositionAndSelection(offset);
|
||||
highlightTemplateVariables(template, topLevelEditor);
|
||||
}
|
||||
|
||||
private void highlightTemplateVariables(Template template, Editor topLevelEditor) {
|
||||
//add highlights
|
||||
if (myHighlighters != null) { // can be null if finish is called during testing
|
||||
Map<TextRange, TextAttributes> rangesToHighlight = new HashMap<TextRange, TextAttributes>();
|
||||
final TemplateState templateState = TemplateManagerImpl.getTemplateState(topLevelEditor);
|
||||
if (templateState != null) {
|
||||
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
|
||||
for (int i = 0; i < templateState.getSegmentsCount(); i++) {
|
||||
final TextRange segmentOffset = templateState.getSegmentRange(i);
|
||||
final String name = template.getSegmentName(i);
|
||||
TextAttributes attributes = null;
|
||||
if (name.equals(PRIMARY_VARIABLE_NAME)) {
|
||||
attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES);
|
||||
}
|
||||
else if (name.equals(OTHER_VARIABLE_NAME)) {
|
||||
attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
|
||||
}
|
||||
if (attributes == null) continue;
|
||||
rangesToHighlight.put(segmentOffset, attributes);
|
||||
}
|
||||
}
|
||||
addHighlights(rangesToHighlight, topLevelEditor, myHighlighters, HighlightManager.getInstance(myProject));
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreOldCaretPositionAndSelection(final int offset) {
|
||||
//move to old offset
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
myEditor.getCaretModel().moveToOffset(restoreCaretOffset(offset));
|
||||
restoreSelection();
|
||||
}
|
||||
};
|
||||
|
||||
final LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(myEditor);
|
||||
if (lookup != null && lookup.getLookupStart() <= (restoreCaretOffset(offset))) {
|
||||
lookup.setFocused(false);
|
||||
lookup.performGuardedChange(runnable);
|
||||
}
|
||||
else {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected void restoreSelection() {
|
||||
}
|
||||
|
||||
protected int restoreCaretOffset(int offset) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
protected void navigateToAlreadyStarted(Document oldDocument, int exitCode) {
|
||||
final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(oldDocument);
|
||||
if (file != null) {
|
||||
final VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (virtualFile != null) {
|
||||
final FileEditor[] editors = FileEditorManager.getInstance(myProject).getEditors(virtualFile);
|
||||
for (FileEditor editor : editors) {
|
||||
if (editor instanceof TextEditor) {
|
||||
final Editor textEditor = ((TextEditor)editor).getEditor();
|
||||
final TemplateState templateState = TemplateManagerImpl.getTemplateState(textEditor);
|
||||
if (templateState != null) {
|
||||
if (exitCode == DialogWrapper.OK_EXIT_CODE) {
|
||||
final TextRange range = templateState.getVariableRange(PRIMARY_VARIABLE_NAME);
|
||||
if (range != null) {
|
||||
new OpenFileDescriptor(myProject, virtualFile, range.getStartOffset()).navigate(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
templateState.gotoEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected PsiElement getNameIdentifier() {
|
||||
return myElementToRename instanceof PsiNameIdentifierOwner ? ((PsiNameIdentifierOwner)myElementToRename).getNameIdentifier() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected StartMarkAction startRename() throws StartMarkAction.AlreadyStartedException {
|
||||
final StartMarkAction[] markAction = new StartMarkAction[1];
|
||||
final StartMarkAction.AlreadyStartedException[] ex = new StartMarkAction.AlreadyStartedException[1];
|
||||
CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
markAction[0] = StartMarkAction.start(myEditor, myProject, getCommandName());
|
||||
}
|
||||
catch (StartMarkAction.AlreadyStartedException e) {
|
||||
ex[0] = e;
|
||||
}
|
||||
}
|
||||
}, getCommandName(), null);
|
||||
if (ex[0] != null) throw ex[0];
|
||||
return markAction[0];
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected PsiNamedElement getVariable() {
|
||||
if (myElementToRename != null && myElementToRename.isValid()) return myElementToRename;
|
||||
final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument());
|
||||
if (psiFile != null) {
|
||||
return PsiTreeUtil.getParentOfType(psiFile.findElementAt(myRenameOffset.getStartOffset()), PsiNameIdentifierOwner.class);
|
||||
}
|
||||
return myElementToRename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the completion of the refactoring, either a successful or a failed one.
|
||||
*
|
||||
* @param success true if the refactoring was accepted, false if it was cancelled (by undo or Esc)
|
||||
*/
|
||||
protected void moveOffsetAfter(boolean success) {
|
||||
}
|
||||
|
||||
protected void addAdditionalVariables(TemplateBuilderImpl builder) {
|
||||
}
|
||||
|
||||
protected void addReferenceAtCaret(Collection<PsiReference> refs) {
|
||||
PsiFile myEditorFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument());
|
||||
// Note, that myEditorFile can be different from myElement.getContainingFile() e.g. in injections: myElement declaration in one
|
||||
// file / usage in another !
|
||||
final PsiReference reference = (myEditorFile != null ?
|
||||
myEditorFile : myElementToRename.getContainingFile())
|
||||
.findReferenceAt(myEditor.getCaretModel().getOffset());
|
||||
if (reference != null && !refs.contains(reference)) {
|
||||
refs.add(reference);
|
||||
}
|
||||
}
|
||||
|
||||
protected void showDialogAdvertisement(final String actionId) {
|
||||
final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
|
||||
final Shortcut[] shortcuts = keymap.getShortcuts(actionId);
|
||||
if (shortcuts.length > 0) {
|
||||
setAdvertisementText("Press " + KeymapUtil.getShortcutText(shortcuts[0]) + " to show dialog");
|
||||
}
|
||||
}
|
||||
|
||||
public String getInitialName() {
|
||||
if (myInitialName == null) {
|
||||
final PsiNamedElement variable = getVariable();
|
||||
if (variable != null) {
|
||||
return variable.getName();
|
||||
}
|
||||
}
|
||||
return myInitialName;
|
||||
}
|
||||
|
||||
protected void revertState() {
|
||||
CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
|
||||
public void run() {
|
||||
final Editor topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(myEditor);
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
final TemplateState state = TemplateManagerImpl.getTemplateState(topLevelEditor);
|
||||
assert state != null;
|
||||
final int segmentsCount = state.getSegmentsCount();
|
||||
final Document document = topLevelEditor.getDocument();
|
||||
for (int i = 0; i < segmentsCount; i++) {
|
||||
final TextRange segmentRange = state.getSegmentRange(i);
|
||||
document.replaceString(segmentRange.getStartOffset(), segmentRange.getEndOffset(), myOldName);
|
||||
}
|
||||
}
|
||||
});
|
||||
PsiDocumentManager.getInstance(myProject).commitDocument(topLevelEditor.getDocument());
|
||||
}
|
||||
}, getCommandName(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the command performed by the refactoring.
|
||||
*
|
||||
* @return command name
|
||||
*/
|
||||
protected abstract String getCommandName();
|
||||
|
||||
public void finish() {
|
||||
if (!ourRenamersStack.isEmpty() && ourRenamersStack.peek() == this) {
|
||||
ourRenamersStack.pop();
|
||||
}
|
||||
if (myHighlighters != null) {
|
||||
if (!myProject.isDisposed()) {
|
||||
final HighlightManager highlightManager = HighlightManager.getInstance(myProject);
|
||||
for (RangeHighlighter highlighter : myHighlighters) {
|
||||
highlightManager.removeSegmentHighlighter(myEditor, highlighter);
|
||||
}
|
||||
}
|
||||
|
||||
myHighlighters = null;
|
||||
myEditor.putUserData(INPLACE_RENAMER, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addHighlights(@NotNull Map<TextRange, TextAttributes> ranges,
|
||||
@NotNull Editor editor,
|
||||
@NotNull Collection<RangeHighlighter> highlighters,
|
||||
@NotNull HighlightManager highlightManager) {
|
||||
for (Map.Entry<TextRange, TextAttributes> entry : ranges.entrySet()) {
|
||||
TextRange range = entry.getKey();
|
||||
TextAttributes attributes = entry.getValue();
|
||||
highlightManager.addOccurrenceHighlight(editor, range.getStartOffset(), range.getEndOffset(), attributes, 0, highlighters, null);
|
||||
}
|
||||
|
||||
for (RangeHighlighter highlighter : highlighters) {
|
||||
highlighter.setGreedyToLeft(true);
|
||||
highlighter.setGreedyToRight(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean performRefactoring();
|
||||
|
||||
private void addVariable(final PsiReference reference,
|
||||
final PsiElement selectedElement,
|
||||
final TemplateBuilderImpl builder,
|
||||
int offset) {
|
||||
if (reference.getElement() == selectedElement &&
|
||||
contains(reference.getRangeInElement().shiftRight(selectedElement.getTextRange().getStartOffset()), offset)) {
|
||||
Expression expression = new MyExpression(getInitialName(), myNameSuggestions);
|
||||
builder.replaceElement(reference, PRIMARY_VARIABLE_NAME, expression, true);
|
||||
}
|
||||
else {
|
||||
builder.replaceElement(reference, OTHER_VARIABLE_NAME, PRIMARY_VARIABLE_NAME, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void addVariable(final PsiElement element,
|
||||
final PsiElement selectedElement,
|
||||
final TemplateBuilderImpl builder) {
|
||||
addVariable(element, null, selectedElement, builder);
|
||||
}
|
||||
|
||||
private void addVariable(final PsiElement element,
|
||||
@Nullable final TextRange textRange,
|
||||
final PsiElement selectedElement,
|
||||
final TemplateBuilderImpl builder) {
|
||||
if (element == selectedElement) {
|
||||
Expression expression = new MyExpression(getInitialName(), myNameSuggestions);
|
||||
builder.replaceElement(element, PRIMARY_VARIABLE_NAME, expression, true);
|
||||
}
|
||||
else if (textRange != null) {
|
||||
builder.replaceElement(element, textRange, OTHER_VARIABLE_NAME, PRIMARY_VARIABLE_NAME, false);
|
||||
}
|
||||
else {
|
||||
builder.replaceElement(element, OTHER_VARIABLE_NAME, PRIMARY_VARIABLE_NAME, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setElementToRename(PsiNamedElement elementToRename) {
|
||||
myElementToRename = elementToRename;
|
||||
}
|
||||
|
||||
protected static VirtualFile getTopLevelVirtualFile(final FileViewProvider fileViewProvider) {
|
||||
VirtualFile file = fileViewProvider.getVirtualFile();
|
||||
if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate();
|
||||
return file;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public static void checkCleared() {
|
||||
try {
|
||||
assert ourRenamersStack.isEmpty() : ourRenamersStack;
|
||||
}
|
||||
finally {
|
||||
ourRenamersStack.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiElement getSelectedInEditorElement(@Nullable PsiElement nameIdentifier,
|
||||
final Collection<PsiReference> refs,
|
||||
Collection<Pair<PsiElement, TextRange>> stringUsages,
|
||||
final int offset) {
|
||||
if (nameIdentifier != null) {
|
||||
final TextRange range = nameIdentifier.getTextRange();
|
||||
if (contains(range, offset)) return nameIdentifier;
|
||||
}
|
||||
|
||||
for (PsiReference ref : refs) {
|
||||
final PsiElement element = ref.getElement();
|
||||
if (contains(ref.getRangeInElement().shiftRight(element.getTextRange().getStartOffset()), offset)) return element;
|
||||
}
|
||||
|
||||
for (Pair<PsiElement, TextRange> stringUsage : stringUsages) {
|
||||
final PsiElement element = stringUsage.first;
|
||||
if (contains(stringUsage.second.shiftRight(element.getTextRange().getStartOffset()), offset)) return element;
|
||||
}
|
||||
|
||||
LOG.assertTrue(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean contains(final TextRange range, final int offset) {
|
||||
return range.getStartOffset() <= offset && offset <= range.getEndOffset();
|
||||
}
|
||||
|
||||
protected class MyExpression extends Expression {
|
||||
private final String myName;
|
||||
private final LookupElement[] myLookupItems;
|
||||
|
||||
protected MyExpression(String name, LinkedHashSet<String> names) {
|
||||
myName = name;
|
||||
if (names == null) {
|
||||
names = new LinkedHashSet<String>();
|
||||
for (NameSuggestionProvider provider : Extensions.getExtensions(NameSuggestionProvider.EP_NAME)) {
|
||||
provider.getSuggestedNames(myElementToRename, myElementToRename, names);
|
||||
}
|
||||
}
|
||||
myLookupItems = new LookupElement[names.size()];
|
||||
final Iterator<String> iterator = names.iterator();
|
||||
for (int i = 0; i < myLookupItems.length; i++) {
|
||||
final String suggestion = iterator.next();
|
||||
myLookupItems[i] = LookupElementBuilder.create(suggestion).setInsertHandler(new InsertHandler<LookupElement>() {
|
||||
@Override
|
||||
public void handleInsert(InsertionContext context, LookupElement item) {
|
||||
if (shouldSelectAll()) return;
|
||||
final Editor topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(myEditor);
|
||||
final TemplateState templateState = TemplateManagerImpl.getTemplateState(topLevelEditor);
|
||||
if (templateState != null) {
|
||||
final TextRange range = templateState.getCurrentVariableRange();
|
||||
if (range != null) {
|
||||
topLevelEditor.getDocument().replaceString(range.getStartOffset(), range.getEndOffset(), suggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context) {
|
||||
return createLookupItems(myLookupItems, myName);
|
||||
}
|
||||
|
||||
public Result calculateQuickResult(ExpressionContext context) {
|
||||
return calculateResult(context);
|
||||
}
|
||||
|
||||
public Result calculateResult(ExpressionContext context) {
|
||||
TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
|
||||
final TextResult insertedValue = templateState != null ? templateState.getVariableValue(PRIMARY_VARIABLE_NAME) : null;
|
||||
if (insertedValue != null) {
|
||||
if (!insertedValue.getText().isEmpty()) return insertedValue;
|
||||
}
|
||||
return new TextResult(myName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdvertisingText() {
|
||||
return myAdvertisementText;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class MyTemplateListener extends TemplateEditingAdapter {
|
||||
|
||||
protected abstract void restoreDaemonUpdateState();
|
||||
|
||||
public void beforeTemplateFinished(final TemplateState templateState, Template template) {
|
||||
try {
|
||||
final TextResult value = templateState.getVariableValue(PRIMARY_VARIABLE_NAME);
|
||||
myInsertedName = value != null ? value.toString() : null;
|
||||
|
||||
final int currentOffset = myEditor.getCaretModel().getOffset();
|
||||
myBeforeRevert =
|
||||
myRenameOffset != null && myRenameOffset.getEndOffset() >= currentOffset && myRenameOffset.getStartOffset() <= currentOffset
|
||||
? myEditor.getDocument().createRangeMarker(myRenameOffset.getStartOffset(), currentOffset)
|
||||
: null;
|
||||
if (myBeforeRevert != null) {
|
||||
myBeforeRevert.setGreedyToRight(true);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
finally {
|
||||
restoreDaemonUpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void templateFinished(Template template, final boolean brokenOff) {
|
||||
boolean bind = false;
|
||||
try {
|
||||
super.templateFinished(template, brokenOff);
|
||||
if (!brokenOff) {
|
||||
bind = performRefactoring();
|
||||
}
|
||||
moveOffsetAfter(!brokenOff);
|
||||
}
|
||||
finally {
|
||||
if (!bind) {
|
||||
FinishMarkAction.finish(myProject, myEditor, myMarkAction);
|
||||
if (myBeforeRevert != null) {
|
||||
myBeforeRevert.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void templateCancelled(Template template) {
|
||||
try {
|
||||
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
|
||||
documentManager.commitAllDocuments();
|
||||
finish();
|
||||
moveOffsetAfter(false);
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
documentManager.doPostponedOperationsAndUnblockDocument(myEditor.getDocument());
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
restoreDaemonUpdateState();
|
||||
}
|
||||
finally {
|
||||
FinishMarkAction.finish(myProject, myEditor, myMarkAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class MemberInplaceRenameHandler extends VariableInplaceRenameHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public VariableInplaceRenamer doRename(@NotNull final PsiElement elementToRename, final Editor editor, final DataContext dataContext) {
|
||||
public InplaceRefactoring doRename(@NotNull final PsiElement elementToRename, final Editor editor, final DataContext dataContext) {
|
||||
if (elementToRename instanceof PsiNameIdentifierOwner) {
|
||||
final RenamePsiElementProcessor processor = RenamePsiElementProcessor.forElement(elementToRename);
|
||||
if (processor.isInplaceRenameSupported()) {
|
||||
|
||||
+5
-10
@@ -138,7 +138,7 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean appendAdditionalElement(List<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
protected boolean appendAdditionalElement(Collection<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
boolean showChooser = super.appendAdditionalElement(stringUsages);
|
||||
PsiNamedElement variable = getVariable();
|
||||
if (variable != null) {
|
||||
@@ -157,7 +157,7 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer {
|
||||
return showChooser;
|
||||
}
|
||||
|
||||
private void appendAdditionalElement(List<Pair<PsiElement, TextRange>> stringUsages,
|
||||
private void appendAdditionalElement(Collection<Pair<PsiElement, TextRange>> stringUsages,
|
||||
PsiNamedElement variable,
|
||||
PsiElement element) {
|
||||
if (element != variable && element instanceof PsiNameIdentifierOwner &&
|
||||
@@ -170,7 +170,6 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer {
|
||||
}
|
||||
|
||||
protected void performRefactoringRename(final String newName,
|
||||
PsiElement context,
|
||||
final StartMarkAction markAction) {
|
||||
try {
|
||||
final PsiNamedElement variable = getVariable();
|
||||
@@ -216,20 +215,16 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collectAdditionalElementsToRename(boolean processTextOccurrences, List<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
protected void collectAdditionalElementsToRename(List<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
//do not highlight non-code usages in file
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void restoreStateBeforeTemplateIsFinished() {
|
||||
public void finish() {
|
||||
super.finish();
|
||||
revertState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNewName(String newName, ResolveSnapshotProvider.ResolveSnapshot snapshot) {
|
||||
return newName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement getSubstituted() {
|
||||
if (mySubstituted != null && mySubstituted.isValid()) return mySubstituted;
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.ui.popup.JBPopupAdapter;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
* Date: 1/11/12
|
||||
*/
|
||||
abstract class RenameChooser {
|
||||
@NonNls private static final String CODE_OCCURRENCES = "Rename code occurrences";
|
||||
@NonNls private static final String ALL_OCCURRENCES = "Rename all occurrences";
|
||||
private final Set<RangeHighlighter> myRangeHighlighters = new HashSet<RangeHighlighter>();
|
||||
private final Editor myEditor;
|
||||
private final TextAttributes myAttributes;
|
||||
|
||||
public RenameChooser(Editor editor) {
|
||||
myEditor = editor;
|
||||
myAttributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
|
||||
}
|
||||
|
||||
protected abstract void runRenameTemplate(Collection<Pair<PsiElement, TextRange>> stringUsages);
|
||||
|
||||
public void showChooser(final Collection<PsiReference> refs,
|
||||
final Collection<Pair<PsiElement, TextRange>> stringUsages) {
|
||||
|
||||
final DefaultListModel model = new DefaultListModel();
|
||||
model.addElement(CODE_OCCURRENCES);
|
||||
model.addElement(ALL_OCCURRENCES);
|
||||
final JList list = new JBList(model);
|
||||
|
||||
list.addListSelectionListener(new ListSelectionListener() {
|
||||
public void valueChanged(final ListSelectionEvent e) {
|
||||
final String selectedValue = (String)list.getSelectedValue();
|
||||
if (selectedValue == null) return;
|
||||
dropHighlighters();
|
||||
final MarkupModel markupModel = myEditor.getMarkupModel();
|
||||
|
||||
if (selectedValue == ALL_OCCURRENCES) {
|
||||
for (Pair<PsiElement, TextRange> pair : stringUsages) {
|
||||
final TextRange textRange = pair.second.shiftRight(pair.first.getTextOffset());
|
||||
final RangeHighlighter rangeHighlighter = markupModel.addRangeHighlighter(
|
||||
textRange.getStartOffset(), textRange.getEndOffset(), HighlighterLayer.SELECTION - 1, myAttributes,
|
||||
HighlighterTargetArea.EXACT_RANGE);
|
||||
myRangeHighlighters.add(rangeHighlighter);
|
||||
}
|
||||
}
|
||||
|
||||
for (PsiReference reference : refs) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element == null) continue;
|
||||
final TextRange textRange = element.getTextRange();
|
||||
final RangeHighlighter rangeHighlighter = markupModel.addRangeHighlighter(
|
||||
textRange.getStartOffset(), textRange.getEndOffset(), HighlighterLayer.SELECTION - 1, myAttributes,
|
||||
HighlighterTargetArea.EXACT_RANGE);
|
||||
myRangeHighlighters.add(rangeHighlighter);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JBPopupFactory.getInstance().createListPopupBuilder(list)
|
||||
.setTitle("String occurrences found")
|
||||
.setMovable(false)
|
||||
.setResizable(false)
|
||||
.setRequestFocus(true)
|
||||
.setItemChoosenCallback(new Runnable() {
|
||||
public void run() {
|
||||
runRenameTemplate(list.getSelectedValue() == ALL_OCCURRENCES ? stringUsages : new ArrayList<Pair<PsiElement, TextRange>>());
|
||||
}
|
||||
})
|
||||
.addListener(new JBPopupAdapter() {
|
||||
@Override
|
||||
public void onClosed(LightweightWindowEvent event) {
|
||||
dropHighlighters();
|
||||
}
|
||||
})
|
||||
.createPopup().showInBestPositionFor(myEditor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void dropHighlighters() {
|
||||
for (RangeHighlighter highlight : myRangeHighlighters) {
|
||||
highlight.dispose();
|
||||
}
|
||||
myRangeHighlighters.clear();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -107,7 +107,7 @@ public class VariableInplaceRenameHandler implements RenameHandler {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VariableInplaceRenamer doRename(final @NotNull PsiElement elementToRename, final Editor editor, final DataContext dataContext) {
|
||||
public InplaceRefactoring doRename(final @NotNull PsiElement elementToRename, final Editor editor, final DataContext dataContext) {
|
||||
VariableInplaceRenamer renamer = createRenamer(elementToRename, editor);
|
||||
boolean startedRename = renamer == null ? false : renamer.performInplaceRename();
|
||||
|
||||
|
||||
+108
-779
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ import com.intellij.psi.codeStyle.CodeStyleSchemes;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
|
||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
|
||||
import com.intellij.testFramework.exceptionCases.AbstractExceptionCase;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Function;
|
||||
@@ -195,7 +195,7 @@ public abstract class UsefulTestCase extends TestCase {
|
||||
myOldCodeStyleSettings = null;
|
||||
|
||||
try {
|
||||
VariableInplaceRenamer.checkCleared();
|
||||
InplaceRefactoring.checkCleared();
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
result.add(e);
|
||||
|
||||
+2
-2
@@ -40,8 +40,8 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler;
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -165,7 +165,7 @@ public class CodeInsightTestUtil {
|
||||
TemplateManagerImpl templateManager = (TemplateManagerImpl)TemplateManager.getInstance(project);
|
||||
try {
|
||||
templateManager.setTemplateTesting(true);
|
||||
VariableInplaceRenamer renamer = handler.doRename(elementAtCaret, editor, null);
|
||||
InplaceRefactoring renamer = handler.doRename(elementAtCaret, editor, null);
|
||||
if (editor instanceof EditorWindow) {
|
||||
editor = ((EditorWindow)editor).getDelegate();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user