From 3ba50c2ca4a1405d2d8b88c4c28ed2aaa0e16f2c Mon Sep 17 00:00:00 2001 From: anna Date: Thu, 14 Oct 2010 20:57:39 +0400 Subject: [PATCH] inplace introduce variable --- .../introduceVariable/InputValidator.java | 2 +- .../IntroduceVariableBase.java | 218 ++++++++++++------ .../IntroduceVariableDialog.java | 2 +- .../IntroduceVariableHandler.java | 30 ++- .../introduceVariable/OccurrencesChooser.java | 161 +++++++++++++ .../rename/inplace/JavaResolveSnapshot.java | 2 + ...ameInPlaceQualifyFieldReference_after.java | 2 +- .../refactoring/IntroduceVariableTest.java | 21 +- .../MockIntroduceVariableHandler.java | 21 +- .../intellij/refactoring/RenameLocalTest.java | 2 +- .../inplace/VariableInplaceRenameHandler.java | 10 +- .../inplace/VariableInplaceRenamer.java | 7 +- 12 files changed, 369 insertions(+), 109 deletions(-) create mode 100644 java/java-impl/src/com/intellij/refactoring/introduceVariable/OccurrencesChooser.java diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/InputValidator.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/InputValidator.java index 1b5cbe2bc591..183c755123a5 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/InputValidator.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/InputValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 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. diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 94079ab30c4d..7a5eaf5ca99e 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -24,29 +24,35 @@ package com.intellij.refactoring.introduceVariable; import com.intellij.codeInsight.CodeInsightUtil; +import com.intellij.codeInsight.completion.JavaCompletionUtil; +import com.intellij.codeInsight.highlighting.HighlightManager; import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.ide.DataManager; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.colors.EditorColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pass; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.*; import com.intellij.psi.impl.source.tree.java.ReplaceExpressionUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.refactoring.IntroduceHandlerBase; -import com.intellij.refactoring.IntroduceTargetChooser; -import com.intellij.refactoring.RefactoringActionHandler; -import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.*; import com.intellij.refactoring.introduceField.ElementToWorkOn; +import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler; +import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer; import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.FieldConflictsResolver; @@ -55,13 +61,12 @@ import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.occurences.ExpressionOccurenceManager; import com.intellij.refactoring.util.occurences.NotInSuperCallOccurenceFilter; import com.intellij.util.IncorrectOperationException; -import com.intellij.util.containers.MultiMap; +import com.intellij.util.containers.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public abstract class IntroduceVariableBase extends IntroduceHandlerBase implements RefactoringActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceVariable.IntroduceVariableBase"); @@ -69,6 +74,15 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme protected static String REFACTORING_NAME = RefactoringBundle.message("introduce.variable.title"); + public static SuggestedNameInfo getSuggestedName(PsiType type, final PsiExpression expression) { + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(expression.getProject()); + final SuggestedNameInfo nameInfo = codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expression, type); + final String[] strings = JavaCompletionUtil + .completeVariableNameForRefactoring(codeStyleManager, type, VariableKind.LOCAL_VARIABLE, nameInfo); + final SuggestedNameInfo.Delegate delegate = new SuggestedNameInfo.Delegate(strings, nameInfo); + return codeStyleManager.suggestUniqueVariableName(delegate, expression, true); + } + public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) { final SelectionModel selectionModel = editor.getSelectionModel(); if (!selectionModel.hasSelection()) { @@ -351,10 +365,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme return false; } - final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); - - PsiType originalType = RefactoringUtil.getTypeByExpressionWithExpectedType(expr); + final PsiType originalType = RefactoringUtil.getTypeByExpressionWithExpectedType(expr); if (originalType == null) { String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("unknown.expression.type")); showErrorMessage(project, editor, message); @@ -370,7 +382,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme final PsiElement physicalElement = expr.getUserData(ElementToWorkOn.PARENT); - PsiElement anchorStatement = RefactoringUtil.getParentStatement(physicalElement != null ? physicalElement : expr, false); + final PsiElement anchorStatement = RefactoringUtil.getParentStatement(physicalElement != null ? physicalElement : expr, false); if (anchorStatement == null) { return parentStatementNotFound(project, editor); @@ -388,7 +400,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme } } - PsiElement tempContainer = anchorStatement.getParent(); + final PsiElement tempContainer = anchorStatement.getParent(); if (!(tempContainer instanceof PsiCodeBlock) && !isLoopOrIf(tempContainer)) { String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME); @@ -418,38 +430,66 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme } } - ExpressionOccurenceManager occurenceManager = new ExpressionOccurenceManager(expr, lastScope, + final ExpressionOccurenceManager occurenceManager = new ExpressionOccurenceManager(expr, lastScope, NotInSuperCallOccurenceFilter.INSTANCE); final PsiExpression[] occurrences = occurenceManager.getOccurences(); final PsiElement anchorStatementIfAll = occurenceManager.getAnchorStatementForAll(); - boolean declareFinalIfAll = occurenceManager.isInFinalContext(); + final LinkedHashMap occurrencesMap = + new LinkedHashMap(); - boolean anyAssignmentLHS = false; - for (PsiExpression occurrence : occurrences) { - if (RefactoringUtil.isAssignmentLHS(occurrence)) { - anyAssignmentLHS = true; - break; + final boolean hasWriteAccess = OccurrencesChooser.fillChoices(expr, occurrences, occurrencesMap); + + final boolean isInplaceAvailableOnDataContext = + new VariableInplaceRenameHandler().isAvailableOnDataContext(DataManager.getInstance().getDataContext()) && + !ApplicationManager.getApplication().isUnitTestMode(); + final boolean inFinalContext = occurenceManager.isInFinalContext(); + final InputValidator validator = new InputValidator(this, project, anchorStatementIfAll, anchorStatement, occurenceManager); + final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, originalType, expr, occurrences); + + final Pass callback = new Pass() { + @Override + public void pass(OccurrencesChooser.ReplaceChoice choice) { + final Ref> variable = new Ref>(); + final IntroduceVariableSettings settings = + getSettings(project, editor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, choice); + final Runnable runnable = + introduce(project, expr, editor, anchorStatement, tempContainer, occurrences, anchorStatementIfAll, settings, variable); + CommandProcessor.getInstance().executeCommand( + project, + new Runnable() { + public void run() { + ApplicationManager.getApplication().runWriteAction(runnable); + if (isInplaceAvailableOnDataContext) { + PsiVariable elementToRename = variable.get().getElement(); + if (elementToRename != null) { + new VariableInplaceRenamer(elementToRename, editor).performInplaceRename(false); + } + } + } + }, REFACTORING_NAME, null); } + }; + + if (!isInplaceAvailableOnDataContext) { + callback.pass(null); } - - - IntroduceVariableSettings settings = getSettings(project, editor, expr, occurrences, anyAssignmentLHS, declareFinalIfAll, - originalType, - new TypeSelectorManagerImpl(project, originalType, expr, occurrences), - new InputValidator(this, project, anchorStatementIfAll, anchorStatement, occurenceManager)); - - if (!settings.isOK()) { - return false; + else { + new OccurrencesChooser(editor).showChooser(callback, occurrencesMap); } + return true; + } - final String variableName = settings.getEnteredName(); - - final PsiType type = settings.getSelectedType(); - final boolean replaceAll = settings.isReplaceAllOccurrences(); - final boolean replaceWrite = settings.isReplaceLValues(); - final boolean declareFinal = replaceAll && declareFinalIfAll || settings.isDeclareFinal(); - if (replaceAll) { + private static Runnable introduce(final Project project, + final PsiExpression expr, + final Editor editor, + PsiElement anchorStatement, + PsiElement tempContainer, + final PsiExpression[] occurrences, + PsiElement anchorStatementIfAll, + final IntroduceVariableSettings settings, + final Ref> variable) { + if (settings.isReplaceAllOccurrences()) { anchorStatement = anchorStatementIfAll; tempContainer = anchorStatement.getParent(); } @@ -463,7 +503,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme final PsiElement anchor = child == null ? anchorStatement : child; boolean tempDeleteSelf = false; - final boolean replaceSelf = replaceWrite || !RefactoringUtil.isAssignmentLHS(expr); + final boolean replaceSelf = settings.isReplaceLValues() || !RefactoringUtil.isAssignmentLHS(expr); if (!isLoopOrIf(container)) { if (expr.getParent() instanceof PsiExpressionStatement && anchor.equals(anchorStatement)) { PsiStatement statement = (PsiStatement) expr.getParent(); @@ -489,10 +529,9 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme } final PsiCodeBlock newDeclarationScope = PsiTreeUtil.getParentOfType(container, PsiCodeBlock.class, false); - final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(variableName, newDeclarationScope); - + final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(settings.getEnteredName(), newDeclarationScope); final PsiElement finalAnchorStatement = anchorStatement; - final Runnable runnable = new Runnable() { + return new Runnable() { public void run() { try { PsiStatement statement = null; @@ -500,6 +539,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme if (!isInsideLoop && deleteSelf) { statement = (PsiStatement) expr.getParent(); } + final PsiExpression expr1 = fieldConflictsResolver.fixInitializer(expr); PsiExpression initializer = RefactoringUtil.unparenthesizeExpression(expr1); if (expr1 instanceof PsiNewExpression) { @@ -508,7 +548,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme initializer = newExpression.getArrayInitializer(); } } - PsiDeclarationStatement declaration = factory.createVariableDeclarationStatement(variableName, type, initializer); + PsiDeclarationStatement declaration = JavaPsiFacade.getInstance(project).getElementFactory() + .createVariableDeclarationStatement(settings.getEnteredName(), settings.getSelectedType(), initializer); if (!isInsideLoop) { declaration = (PsiDeclarationStatement) container.addBefore(declaration, anchor); LOG.assertTrue(expr1.isValid()); @@ -528,8 +569,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme } } - PsiExpression ref = factory.createExpressionFromText(variableName, null); - if (replaceAll) { + PsiExpression ref = JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(settings.getEnteredName(), null); + if (settings.isReplaceAllOccurrences()) { ArrayList array = new ArrayList(); for (PsiExpression occurrence : occurrences) { if (deleteSelf && occurrence.equals(expr)) continue; @@ -539,7 +580,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme if (occurrence != null) { occurrence = RefactoringUtil.outermostParenthesizedExpression(occurrence); } - if (replaceWrite || !RefactoringUtil.isAssignmentLHS(occurrence)) { + if (settings.isReplaceLValues() || !RefactoringUtil.isAssignmentLHS(occurrence)) { array.add(replace(occurrence, ref, project)); } } @@ -557,23 +598,14 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme declaration = (PsiDeclarationStatement) putStatementInLoopBody(declaration, container, finalAnchorStatement); declaration = (PsiDeclarationStatement)JavaCodeStyleManager.getInstance(project).shortenClassReferences(declaration); PsiVariable var = (PsiVariable) declaration.getDeclaredElements()[0]; - PsiUtil.setModifierProperty(var, PsiModifier.FINAL, declareFinal); - + PsiUtil.setModifierProperty(var, PsiModifier.FINAL, settings.isDeclareFinal()); + variable.set(SmartPointerManager.getInstance(project).createLazyPointer(var)); fieldConflictsResolver.fix(); } catch (IncorrectOperationException e) { LOG.error(e); } } }; - - CommandProcessor.getInstance().executeCommand( - project, - new Runnable() { - public void run() { - ApplicationManager.getApplication().runWriteAction(runnable); - } - }, REFACTORING_NAME, null); - return true; } public static PsiElement replace(final PsiExpression expr1, final PsiExpression ref, final Project project) @@ -662,11 +694,15 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme return child; } - protected abstract void highlightReplacedOccurences(Project project, Editor editor, PsiElement[] replacedOccurences); - - protected abstract IntroduceVariableSettings getSettings(Project project, Editor editor, PsiExpression expr, final PsiElement[] occurrences, - boolean anyAssignmentLHS, final boolean declareFinalIfAll, final PsiType type, - TypeSelectorManagerImpl typeSelectorManager, InputValidator validator); + protected static void highlightReplacedOccurences(Project project, Editor editor, PsiElement[] replacedOccurences){ + if (editor == null) return; + if (ApplicationManager.getApplication().isUnitTestMode()) return; + HighlightManager highlightManager = HighlightManager.getInstance(project); + EditorColorsManager colorsManager = EditorColorsManager.getInstance(); + TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); + highlightManager.addOccurrenceHighlights(editor, replacedOccurences, attributes, true, null); + WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting")); + } protected abstract void showErrorMessage(Project project, Editor editor, String message); @@ -695,13 +731,65 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme return element instanceof PsiLoopStatement || element instanceof PsiIfStatement; } + protected boolean reportConflicts(MultiMap conflicts, Project project, IntroduceVariableSettings settings){ + return false; + } + + public IntroduceVariableSettings getSettings(Project project, Editor editor, + PsiExpression expr, PsiExpression[] occurrences, + final TypeSelectorManagerImpl typeSelectorManager, + boolean declareFinalIfAll, + boolean anyAssignmentLHS, + final InputValidator validator, + final OccurrencesChooser.ReplaceChoice replaceChoice) { + final SuggestedNameInfo suggestedName = getSuggestedName(typeSelectorManager.getDefaultType(), expr); + final String variableName = suggestedName.names[0]; + final Boolean generateFinals = JavaRefactoringSettings.getInstance().INTRODUCE_LOCAL_CREATE_FINALS; + final boolean replaceAll = + replaceChoice == OccurrencesChooser.ReplaceChoice.ALL || replaceChoice == OccurrencesChooser.ReplaceChoice.NO_WRITE; + final boolean declareFinal = + !anyAssignmentLHS && (replaceAll && + declareFinalIfAll || generateFinals == null ? + CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS : + generateFinals.booleanValue()); + final boolean replaceWrite = anyAssignmentLHS && replaceChoice == OccurrencesChooser.ReplaceChoice.ALL; + return new IntroduceVariableSettings() { + @Override + public String getEnteredName() { + return variableName; + } + + @Override + public boolean isReplaceAllOccurrences() { + return replaceAll; + } + + @Override + public boolean isDeclareFinal() { + return declareFinal; + } + + @Override + public boolean isReplaceLValues() { + return replaceWrite; + } + + @Override + public PsiType getSelectedType() { + return typeSelectorManager.getDefaultType(); + } + + @Override + public boolean isOK() { + return true; + } + }; + } + public interface Validator { boolean isOK(IntroduceVariableSettings dialog); } - protected abstract boolean reportConflicts(MultiMap conflicts, final Project project, IntroduceVariableSettings dialog); - - public static void checkInLoopCondition(PsiExpression occurence, MultiMap conflicts) { final PsiElement loopForLoopCondition = RefactoringUtil.getLoopForLoopCondition(occurence); if (loopForLoopCondition == null) return; @@ -721,6 +809,4 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme conflicts.putValue(occurence, RefactoringBundle.message("introducing.variable.may.break.code.logic")); } } - - } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableDialog.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableDialog.java index 8d696308e502..d281d4a9c073 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 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. diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableHandler.java index b0140133829e..da561e3af899 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableHandler.java @@ -22,11 +22,10 @@ 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.wm.WindowManager; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; -import com.intellij.psi.PsiType; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.ui.ConflictsDialog; @@ -38,11 +37,19 @@ import java.util.ArrayList; public class IntroduceVariableHandler extends IntroduceVariableBase { - protected IntroduceVariableSettings getSettings(final Project project, Editor editor, PsiExpression expr, - PsiElement[] occurrences, boolean anyAssignmentLHS, - boolean declareFinalIfAll, PsiType type, - TypeSelectorManagerImpl typeSelectorManager, - InputValidator validator) { + + @Override + public IntroduceVariableSettings getSettings(Project project, Editor editor, + PsiExpression expr, PsiExpression[] occurrences, + TypeSelectorManagerImpl typeSelectorManager, + boolean declareFinalIfAll, + boolean anyAssignmentLHS, + final InputValidator validator, + final OccurrencesChooser.ReplaceChoice replaceChoice) { + if (replaceChoice != null) { + return super.getSettings(project, editor, expr, occurrences, typeSelectorManager, declareFinalIfAll, anyAssignmentLHS, validator, + replaceChoice); + } ArrayList highlighters = new ArrayList(); HighlightManager highlightManager = null; if (editor != null) { @@ -78,15 +85,6 @@ public class IntroduceVariableHandler extends IntroduceVariableBase { CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE); } - protected void highlightReplacedOccurences(final Project project, Editor editor, final PsiElement[] replacedOccurences) { - if (editor == null) return; - HighlightManager highlightManager = HighlightManager.getInstance(project); - EditorColorsManager colorsManager = EditorColorsManager.getInstance(); - TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); - highlightManager.addOccurrenceHighlights(editor, replacedOccurences, attributes, true, null); - WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting")); - } - protected boolean reportConflicts(final MultiMap conflicts, final Project project, IntroduceVariableSettings dialog) { ConflictsDialog conflictsDialog = new ConflictsDialog(project, conflicts); conflictsDialog.show(); diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/OccurrencesChooser.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/OccurrencesChooser.java new file mode 100644 index 000000000000..97b2ca3c09af --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/OccurrencesChooser.java @@ -0,0 +1,161 @@ +/* + * Copyright 2000-2010 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.introduceVariable; + +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.Pass; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiExpression; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.ui.components.JBList; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.util.*; +import java.util.List; + +/** + * User: anna + * Date: 10/14/10 + */ +public class OccurrencesChooser { + public static enum ReplaceChoice { + NO("Do not replace"), NO_WRITE("Replace all but write"), ALL("Replace all occurrences"); + + private String myDescription; + + ReplaceChoice(String description) { + myDescription = description; + } + + public String getDescription() { + return myDescription; + } + } + + + private final Set myRangeHighlighters = new HashSet(); + private final Editor myEditor; + private final TextAttributes myAttributes; + + public OccurrencesChooser(Editor editor) { + myEditor = editor; + myAttributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); + } + + public void showChooser(final Pass callback, + final Map occurrencesMap) { + if (occurrencesMap.size() == 1) { + callback.pass(occurrencesMap.keySet().iterator().next()); + return; + } + final DefaultListModel model = new DefaultListModel(); + for (ReplaceChoice choice : occurrencesMap.keySet()) { + model.addElement(choice); + } + final JList list = new JBList(model); + list.setCellRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(final JList list, + final Object value, + final int index, + final boolean isSelected, + final boolean cellHasFocus) { + final Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + final ReplaceChoice choices = (ReplaceChoice)value; + if (choices != null) { + setText(choices.getDescription()); + } + return rendererComponent; + } + }); + list.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(final ListSelectionEvent e) { + final ReplaceChoice value = (ReplaceChoice)list.getSelectedValue(); + if (value == null) return; + dropHighlighters(); + final MarkupModel markupModel = myEditor.getMarkupModel(); + final PsiExpression[] psiExpressions = occurrencesMap.get(value); + for (PsiExpression psiExpression : psiExpressions) { + final TextRange textRange = psiExpression.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("Multiple occurrences found") + .setMovable(false) + .setResizable(false) + .setRequestFocus(true) + .setItemChoosenCallback(new Runnable() { + public void run() { + callback.pass((ReplaceChoice)list.getSelectedValue()); + } + }) + .addListener(new JBPopupAdapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + dropHighlighters(); + } + }) + .createPopup().showInBestPositionFor(myEditor); + } + + /** + * @return true if write usages found + */ + static boolean fillChoices(final PsiExpression expr, + final PsiExpression[] occurrences, + final LinkedHashMap occurrencesMap) { + occurrencesMap.put(ReplaceChoice.NO, new PsiExpression[]{expr}); + + final List nonWrite = new ArrayList(); + for (PsiExpression occurrence : occurrences) { + if (!RefactoringUtil.isAssignmentLHS(occurrence)) { + nonWrite.add(occurrence); + } + } + final boolean hasWriteAccess = occurrences.length > nonWrite.size() && occurrences.length > 1; + if (hasWriteAccess) { + occurrencesMap.put(ReplaceChoice.NO_WRITE, nonWrite.toArray(new PsiExpression[nonWrite.size()])); + } + + if (occurrences.length > 1) { + occurrencesMap.put(ReplaceChoice.ALL, occurrences); + } + return hasWriteAccess; + } + + private void dropHighlighters() { + final MarkupModel markupModel = myEditor.getMarkupModel(); + for (RangeHighlighter highlight : myRangeHighlighters) { + markupModel.removeHighlighter(highlight); + } + myRangeHighlighters.clear(); + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/rename/inplace/JavaResolveSnapshot.java b/java/java-impl/src/com/intellij/refactoring/rename/inplace/JavaResolveSnapshot.java index 68d69cf97a10..9441efe1bc08 100644 --- a/java/java-impl/src/com/intellij/refactoring/rename/inplace/JavaResolveSnapshot.java +++ b/java/java-impl/src/com/intellij/refactoring/rename/inplace/JavaResolveSnapshot.java @@ -71,6 +71,8 @@ class JavaResolveSnapshot extends ResolveSnapshotProvider.ResolveSnapshot { if (referent instanceof PsiReferenceExpression && referee instanceof PsiField) { PsiReferenceExpression ref = ((PsiReferenceExpression) referent); if (!ref.isQualified() && hidingLocalName.equals(ref.getReferenceName())) { + final PsiElement newlyResolved = ref.resolve(); + if (referee.getManager().areElementsEquivalent(newlyResolved, referee)) return; PsiClass refereeClass = ((PsiField) referee).getContainingClass(); PsiClass referentClass = PsiTreeUtil.getParentOfType(referent, PsiClass.class); if (refereeClass != null && referentClass != null && diff --git a/java/java-tests/testData/refactoring/renameLocal/RenameInPlaceQualifyFieldReference_after.java b/java/java-tests/testData/refactoring/renameLocal/RenameInPlaceQualifyFieldReference_after.java index 8afd91e9d431..589248fa75ab 100644 --- a/java/java-tests/testData/refactoring/renameLocal/RenameInPlaceQualifyFieldReference_after.java +++ b/java/java-tests/testData/refactoring/renameLocal/RenameInPlaceQualifyFieldReference_after.java @@ -1,6 +1,6 @@ class Test { int myI; void foo(int i){ - this.myI = i; + myI = i; } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java index 6d10533e1aeb..2a19c88e5d7b 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java @@ -11,6 +11,7 @@ import com.intellij.psi.PsiType; import com.intellij.refactoring.introduceVariable.InputValidator; import com.intellij.refactoring.introduceVariable.IntroduceVariableBase; import com.intellij.refactoring.introduceVariable.IntroduceVariableSettings; +import com.intellij.refactoring.introduceVariable.OccurrencesChooser; import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import com.intellij.testFramework.LightCodeInsightTestCase; import com.intellij.util.containers.MultiMap; @@ -225,17 +226,17 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase { public void testSiblingInnerClassType() throws Exception { doTest(new MockIntroduceVariableHandler("vari", true, false, false, "A.B"){ @Override - protected IntroduceVariableSettings getSettings(Project project, - Editor editor, - PsiExpression expr, - PsiElement[] occurrences, - boolean anyAssignmentLHS, - boolean declareFinalIfAll, - PsiType type, - TypeSelectorManagerImpl typeSelectorManager, - InputValidator validator) { + public IntroduceVariableSettings getSettings(Project project, Editor editor, + PsiExpression expr, PsiExpression[] occurrences, + TypeSelectorManagerImpl typeSelectorManager, + boolean declareFinalIfAll, + boolean anyAssignmentLHS, + InputValidator validator, + final OccurrencesChooser.ReplaceChoice replaceChoice) { + final PsiType type = typeSelectorManager.getDefaultType(); Assert.assertTrue(type.getPresentableText(), type.getPresentableText().equals("B")); - return super.getSettings(project, editor, expr, occurrences, anyAssignmentLHS, declareFinalIfAll, type, typeSelectorManager, validator); + return super.getSettings(project, editor, expr, occurrences, typeSelectorManager, declareFinalIfAll, anyAssignmentLHS, + validator, replaceChoice); } }); } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/MockIntroduceVariableHandler.java b/java/java-tests/testSrc/com/intellij/refactoring/MockIntroduceVariableHandler.java index c661cfbc5e12..620934fa1c41 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/MockIntroduceVariableHandler.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/MockIntroduceVariableHandler.java @@ -2,12 +2,11 @@ package com.intellij.refactoring; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiExpression; -import com.intellij.psi.PsiType; +import com.intellij.psi.*; import com.intellij.refactoring.introduceVariable.InputValidator; import com.intellij.refactoring.introduceVariable.IntroduceVariableBase; import com.intellij.refactoring.introduceVariable.IntroduceVariableSettings; +import com.intellij.refactoring.introduceVariable.OccurrencesChooser; import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import com.intellij.util.containers.MultiMap; import junit.framework.Assert; @@ -36,9 +35,14 @@ class MockIntroduceVariableHandler extends IntroduceVariableBase { @Override - protected IntroduceVariableSettings getSettings(Project project, Editor editor, PsiExpression expr, final PsiElement[] occurrences, - boolean anyAssignmentLHS, final boolean declareFinalIfAll, final PsiType type, - TypeSelectorManagerImpl typeSelectorManager, InputValidator validator) { + public IntroduceVariableSettings getSettings(Project project, Editor editor, + PsiExpression expr, final PsiExpression[] occurrences, + TypeSelectorManagerImpl typeSelectorManager, + final boolean declareFinalIfAll, + boolean anyAssignmentLHS, + InputValidator validator, + final OccurrencesChooser.ReplaceChoice replaceChoice) { + final PsiType type = typeSelectorManager.getDefaultType(); Assert.assertTrue(type.getCanonicalText(), type.equalsToText(myExpectedTypeCanonicalName)); IntroduceVariableSettings introduceVariableSettings = new IntroduceVariableSettings() { @Override @@ -85,11 +89,6 @@ class MockIntroduceVariableHandler extends IntroduceVariableBase { throw new RuntimeException("Error message:" + message); } - @Override - protected void highlightReplacedOccurences(final Project project, Editor editor, final PsiElement[] replacedOccurences) { - - } - @Override protected boolean reportConflicts(final MultiMap conflicts, final Project project, IntroduceVariableSettings dialog) { return false; diff --git a/java/java-tests/testSrc/com/intellij/refactoring/RenameLocalTest.java b/java/java-tests/testSrc/com/intellij/refactoring/RenameLocalTest.java index 34c57290a771..2a7a8ea20f70 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/RenameLocalTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/RenameLocalTest.java @@ -73,7 +73,7 @@ public class RenameLocalTest extends LightCodeInsightTestCase { ResolveSnapshotProvider resolveSnapshotProvider = VariableInplaceRenamer.INSTANCE.forLanguage(getFile().getLanguage()); assertNotNull(resolveSnapshotProvider); - final ResolveSnapshotProvider.ResolveSnapshot snapshot = resolveSnapshotProvider.createSnapshot(methodScope); + final ResolveSnapshotProvider.ResolveSnapshot snapshot = resolveSnapshotProvider.createSnapshot(methodScope.getBody()); assertNotNull(snapshot); final int offset = element.getTextOffset(); diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenameHandler.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenameHandler.java index 3714808801a3..5250c26ab0ba 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenameHandler.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenameHandler.java @@ -97,9 +97,17 @@ public class VariableInplaceRenameHandler implements RenameHandler { @Nullable public VariableInplaceRenamer doRename(final PsiElement elementToRename, final Editor editor, final DataContext dataContext) { + return doRename(elementToRename, editor, dataContext, true); + } + + @Nullable + public VariableInplaceRenamer doRename(final PsiElement elementToRename, + final Editor editor, + final DataContext dataContext, + boolean processTextOccurrences) { VariableInplaceRenamer renamer = createRenamer(elementToRename, editor); - boolean startedRename = renamer == null ? false : renamer.performInplaceRename(); + boolean startedRename = renamer == null ? false : renamer.performInplaceRename(processTextOccurrences); if (!startedRename) { try { diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java index a783606dd7cf..b9b758cfa053 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java @@ -94,6 +94,10 @@ public class VariableInplaceRenamer { } public boolean performInplaceRename() { + return performInplaceRename(true); + } + + public boolean performInplaceRename(boolean processTextOccurrences) { if (InjectedLanguageUtil.isInInjectedLanguagePrefixSuffix(myElementToRename)) { return false; } @@ -146,7 +150,8 @@ public class VariableInplaceRenamer { } String stringToSearch = myElementToRename.getName(); - if (stringToSearch != null && + if (processTextOccurrences && + stringToSearch != null && !TextOccurrencesUtil.processUsagesInStringsAndComments(myElementToRename, stringToSearch, true, new PairProcessor() { public boolean process(PsiElement psiElement, TextRange textRange) { return false;