inplace rename: no need to revert to continue editing non-identifier; no need to...
[idea/community.git] / platform / lang-impl / src / com / intellij / refactoring / introduce / inplace / InplaceVariableIntroducer.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.refactoring.introduce.inplace;
17
18 import com.intellij.codeInsight.lookup.LookupElement;
19 import com.intellij.codeInsight.lookup.LookupElementBuilder;
20 import com.intellij.codeInsight.template.TextResult;
21 import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
22 import com.intellij.codeInsight.template.impl.TemplateState;
23 import com.intellij.openapi.Disposable;
24 import com.intellij.openapi.application.ApplicationManager;
25 import com.intellij.openapi.command.impl.StartMarkAction;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.editor.RangeMarker;
28 import com.intellij.openapi.extensions.Extensions;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.ui.popup.Balloon;
31 import com.intellij.openapi.ui.popup.BalloonBuilder;
32 import com.intellij.openapi.ui.popup.JBPopupFactory;
33 import com.intellij.openapi.util.*;
34 import com.intellij.psi.PsiElement;
35 import com.intellij.psi.PsiNamedElement;
36 import com.intellij.refactoring.rename.NameSuggestionProvider;
37 import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
38 import com.intellij.ui.awt.RelativePoint;
39 import com.intellij.util.ui.PositionTracker;
40 import org.jetbrains.annotations.Nullable;
41
42 import javax.swing.*;
43 import java.awt.*;
44 import java.util.ArrayList;
45 import java.util.Iterator;
46 import java.util.LinkedHashSet;
47 import java.util.List;
48
49 /**
50  * User: anna
51  * Date: 3/15/11
52  */
53 public abstract class InplaceVariableIntroducer<E extends PsiElement> extends InplaceRefactoring {
54   public static final Key<Boolean> INTRODUCE_RESTART = Key.create("INTRODUCE_RESTART");
55
56   protected E myExpr;
57   protected RangeMarker myExprMarker;
58
59   protected E[] myOccurrences;
60   protected List<RangeMarker> myOccurrenceMarkers;
61
62   protected Balloon myBalloon;
63   protected String myTitle;
64   protected RelativePoint myTarget;
65  
66
67   public InplaceVariableIntroducer(PsiNamedElement elementToRename,
68                                    Editor editor,
69                                    Project project,
70                                    String title, E[] occurrences, 
71                                    @Nullable E expr) {
72     super(editor, elementToRename, project);
73     myTitle = title;
74     myOccurrences = occurrences;
75     myExpr = expr;
76     myExprMarker = myExpr != null && myExpr.isPhysical() ? createMarker(myExpr) : null;
77     initOccurrencesMarkers();
78   }
79
80   @Deprecated
81   public boolean performInplaceRename() {
82     return performInplaceRefactoring(null);
83   }
84   
85   @Override
86   protected boolean shouldSelectAll() {
87     return true;
88   }
89
90   @Override
91   protected StartMarkAction startRename() throws StartMarkAction.AlreadyStartedException {
92     return null;
93   }
94
95   @Nullable
96   protected JComponent getComponent() {
97     return null;
98   }
99
100   public void setOccurrenceMarkers(List<RangeMarker> occurrenceMarkers) {
101     myOccurrenceMarkers = occurrenceMarkers;
102   }
103
104   public void setExprMarker(RangeMarker exprMarker) {
105     myExprMarker = exprMarker;
106   }
107
108   public E getExpr() {
109     return myExpr != null && myExpr.isValid() && myExpr.isPhysical() ? myExpr : null;
110   }
111
112   public E[] getOccurrences() {
113     return myOccurrences;
114   }
115
116   public List<RangeMarker> getOccurrenceMarkers() {
117     if (myOccurrenceMarkers == null) {
118       initOccurrencesMarkers();
119     }
120     return myOccurrenceMarkers;
121   }
122
123   protected void initOccurrencesMarkers() {
124     if (myOccurrenceMarkers != null) return;
125     myOccurrenceMarkers = new ArrayList<RangeMarker>();
126     for (E occurrence : myOccurrences) {
127       myOccurrenceMarkers.add(createMarker(occurrence));
128     }
129   }
130
131   protected RangeMarker createMarker(PsiElement element) {
132     return myEditor.getDocument().createRangeMarker(element.getTextRange());
133   }
134
135
136   public RangeMarker getExprMarker() {
137     return myExprMarker;
138   }
139
140   @Override
141   protected boolean performRefactoring() {
142     return false;
143   }
144
145   @Override
146   protected void collectAdditionalElementsToRename(List<Pair<PsiElement, TextRange>> stringUsages) {
147   }
148
149   @Override
150   protected String getCommandName() {
151     return myTitle;
152   }
153
154   @Override
155   public boolean performInplaceRefactoring(LinkedHashSet<String> nameSuggestions) {
156     final boolean result = super.performInplaceRefactoring(nameSuggestions);
157     if (result) {
158       if (myBalloon == null) {
159         showBalloon();
160       }
161     }
162     return result;
163   }
164
165   @Override
166   protected void moveOffsetAfter(boolean success) {
167     super.moveOffsetAfter(success);
168     if (myOccurrenceMarkers != null) {
169       for (RangeMarker marker : myOccurrenceMarkers) {
170         marker.dispose();
171       }
172     }
173     if (myExprMarker != null && !isRestart()) {
174       myExprMarker.dispose();
175     }
176   }
177
178   protected boolean isRestart() {
179     final Boolean isRestart = myEditor.getUserData(INTRODUCE_RESTART);
180     return isRestart != null && isRestart;
181   }
182   
183   protected void releaseResources() {
184   }
185
186   protected void showBalloon() {
187     final JComponent component = getComponent();
188     if (component == null) return;
189     if (ApplicationManager.getApplication().isHeadlessEnvironment()) return;
190     final BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createDialogBalloonBuilder(component, null).setSmallVariant(true);
191     myBalloon = balloonBuilder.createBalloon();
192     Disposer.register(myProject, myBalloon);
193     Disposer.register(myBalloon, new Disposable() {
194       @Override
195       public void dispose() {
196         releaseIfNotRestart();
197       }
198     });
199     myBalloon.show(new PositionTracker<Balloon>(myEditor.getContentComponent()) {
200       @Override
201       public RelativePoint recalculateLocation(Balloon object) {
202         final RelativePoint target = JBPopupFactory.getInstance().guessBestPopupLocation(myEditor);
203         final Point screenPoint = target.getScreenPoint();
204         int y = screenPoint.y;
205         if (target.getPoint().getY() > myEditor.getLineHeight() + myBalloon.getPreferredSize().getHeight()) {
206           y -= myEditor.getLineHeight();
207         }
208         myTarget = new RelativePoint(new Point(screenPoint.x, y));
209         return myTarget;
210       }
211     }, Balloon.Position.above);
212   }
213
214   protected void releaseIfNotRestart() {
215     final Boolean isRestart = myEditor.getUserData(INTRODUCE_RESTART);
216     if (isRestart == null || !isRestart.booleanValue()) {
217       releaseResources();
218     }
219   }
220
221   @Override
222   public void finish(boolean success) {
223     super.finish(success);
224     if (myBalloon != null) {
225       final Boolean isRestart = myEditor.getUserData(INTRODUCE_RESTART);
226       if (isRestart == null || !isRestart.booleanValue()) {
227         myBalloon.hide();
228       }
229     }
230   }
231
232   @Override
233   protected LookupElement[] createLookupItems(LookupElement[] lookupItems, String name) {
234     TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
235     final PsiNamedElement psiVariable = getVariable();
236     if (psiVariable != null) {
237       final TextResult insertedValue =
238         templateState != null ? templateState.getVariableValue(PRIMARY_VARIABLE_NAME) : null;
239       if (insertedValue != null) {
240         final String text = insertedValue.getText();
241         if (!text.isEmpty() && !Comparing.strEqual(text, name)) {
242           final LinkedHashSet<String> names = new LinkedHashSet<String>();
243           names.add(text);
244           for (NameSuggestionProvider provider : Extensions.getExtensions(NameSuggestionProvider.EP_NAME)) {
245             provider.getSuggestedNames(psiVariable, psiVariable, names);
246           }
247           final LookupElement[] items = new LookupElement[names.size()];
248           final Iterator<String> iterator = names.iterator();
249           for (int i = 0; i < items.length; i++) {
250             items[i] = LookupElementBuilder.create(iterator.next());
251           }
252           return items;
253         }
254       }
255     }
256     return lookupItems;
257   }
258
259 }