IDEADEV-40832
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / intention / impl / IntentionListStep.java
blob7d25cc3b1d59c5136d27a84dd6638a8af2426f7e
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.intellij.codeInsight.intention.impl;
19 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
20 import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass;
21 import com.intellij.codeInsight.hint.HintManager;
22 import com.intellij.codeInsight.intention.EmptyIntentionAction;
23 import com.intellij.codeInsight.intention.IntentionAction;
24 import com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings;
25 import com.intellij.codeInspection.ex.QuickFixWrapper;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.editor.Editor;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.ui.popup.*;
30 import com.intellij.openapi.util.Comparing;
31 import com.intellij.openapi.util.Iconable;
32 import com.intellij.openapi.util.Pair;
33 import com.intellij.psi.*;
34 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
35 import com.intellij.psi.util.PsiUtilBase;
36 import gnu.trove.THashSet;
37 import gnu.trove.TObjectHashingStrategy;
38 import org.jetbrains.annotations.NotNull;
40 import javax.swing.*;
41 import java.util.*;
43 /**
44 * @author cdr
46 class IntentionListStep implements ListPopupStep<IntentionActionWithTextCaching>, SpeedSearchFilter<IntentionActionWithTextCaching> {
47 private final Set<IntentionActionWithTextCaching> myCachedIntentions = new THashSet<IntentionActionWithTextCaching>(ACTION_TEXT_AND_CLASS_EQUALS);
48 private final Set<IntentionActionWithTextCaching> myCachedErrorFixes = new THashSet<IntentionActionWithTextCaching>(ACTION_TEXT_AND_CLASS_EQUALS);
49 private final Set<IntentionActionWithTextCaching> myCachedInspectionFixes = new THashSet<IntentionActionWithTextCaching>(ACTION_TEXT_AND_CLASS_EQUALS);
50 private final Set<IntentionActionWithTextCaching> myCachedGutters = new THashSet<IntentionActionWithTextCaching>(ACTION_TEXT_AND_CLASS_EQUALS);
51 private final IntentionManagerSettings mySettings;
52 private final IntentionHintComponent myIntentionHintComponent;
53 private final Editor myEditor;
54 private final PsiFile myFile;
55 private final Project myProject;
56 private static final TObjectHashingStrategy<IntentionActionWithTextCaching> ACTION_TEXT_AND_CLASS_EQUALS = new TObjectHashingStrategy<IntentionActionWithTextCaching>() {
57 public int computeHashCode(final IntentionActionWithTextCaching object) {
58 return object.getText().hashCode();
61 public boolean equals(final IntentionActionWithTextCaching o1, final IntentionActionWithTextCaching o2) {
62 return o1.getAction().getClass() == o2.getAction().getClass() && o1.getText().equals(o2.getText());
66 IntentionListStep(IntentionHintComponent intentionHintComponent, ShowIntentionsPass.IntentionsInfo intentions, Editor editor, PsiFile file,
67 Project project) {
68 myIntentionHintComponent = intentionHintComponent;
69 myEditor = editor;
70 myFile = file;
71 myProject = project;
72 mySettings = IntentionManagerSettings.getInstance();
73 updateActions(intentions);
76 //true if something changed
77 boolean updateActions(ShowIntentionsPass.IntentionsInfo intentions) {
78 boolean result = wrapActionsTo(intentions.errorFixesToShow, myCachedErrorFixes);
79 result &= wrapActionsTo(intentions.inspectionFixesToShow, myCachedInspectionFixes);
80 result &= wrapActionsTo(intentions.intentionsToShow, myCachedIntentions);
81 result &= wrapActionsTo(intentions.guttersToShow, myCachedGutters);
82 return !result;
85 private boolean wrapActionsTo(final List<HighlightInfo.IntentionActionDescriptor> descriptors, final Set<IntentionActionWithTextCaching> cachedActions) {
86 boolean result = true;
87 final int caretOffset = myEditor.getCaretModel().getOffset();
88 final int fileOffset = caretOffset > 0 && caretOffset == myFile.getTextLength() ? caretOffset - 1 : caretOffset;
89 PsiElement element;
90 if (myFile instanceof PsiCompiledElement) {
91 element = myFile;
93 else if (PsiDocumentManager.getInstance(myProject).isUncommited(myEditor.getDocument())) {
94 //???
95 FileViewProvider viewProvider = myFile.getViewProvider();
96 element = viewProvider.findElementAt(fileOffset, viewProvider.getBaseLanguage());
98 else {
99 element = InjectedLanguageUtil.findElementAtNoCommit(myFile, fileOffset);
101 if (!descriptors.isEmpty()) {
103 for (HighlightInfo.IntentionActionDescriptor descriptor : descriptors) {
104 IntentionAction action = descriptor.getAction();
105 IntentionActionWithTextCaching cachedAction = new IntentionActionWithTextCaching(action, descriptor.getDisplayName(), descriptor.getIcon());
106 result &= !cachedActions.add(cachedAction);
107 final List<IntentionAction> options;
108 if (element != null && (options = descriptor.getOptions(element)) != null) {
109 for (IntentionAction option : options) {
110 boolean isErrorFix = myCachedErrorFixes.contains(new IntentionActionWithTextCaching(option, option.getText()));
111 if (isErrorFix) {
112 cachedAction.addErrorFix(option);
114 boolean isInspectionFix = myCachedInspectionFixes.contains(new IntentionActionWithTextCaching(option, option.getText()));
115 if (isInspectionFix) {
116 cachedAction.addInspectionFix(option);
118 else {
119 cachedAction.addIntention(option);
125 result &= removeInvalidActions(cachedActions, element);
126 return result;
129 private boolean removeInvalidActions(final Collection<IntentionActionWithTextCaching> cachedActions, final PsiElement element) {
130 boolean result = true;
131 Iterator<IntentionActionWithTextCaching> iterator = cachedActions.iterator();
132 while (iterator.hasNext()) {
133 IntentionActionWithTextCaching cachedAction = iterator.next();
134 IntentionAction action = cachedAction.getAction();
135 Pair<PsiFile,Editor> place = ShowIntentionActionsHandler.availableFor(myFile, myEditor, action, element);
136 if (place == null) {
137 iterator.remove();
138 result = false;
141 return result;
144 public String getTitle() {
145 return null;
148 public boolean isSelectable(final IntentionActionWithTextCaching action) {
149 return true;
152 public PopupStep onChosen(final IntentionActionWithTextCaching action, final boolean finalChoice) {
153 if (finalChoice && !(action.getAction() instanceof EmptyIntentionAction)) {
154 applyAction(action);
155 return FINAL_CHOICE;
158 if (hasSubstep(action)) {
159 return getSubStep(action);
162 return FINAL_CHOICE;
165 private void applyAction(final IntentionActionWithTextCaching cachedAction) {
166 ApplicationManager.getApplication().invokeLater(new Runnable() {
167 public void run() {
168 HintManager.getInstance().hideAllHints();
169 ApplicationManager.getApplication().invokeLater(new Runnable() {
170 public void run() {
171 if (myProject.isDisposed()) return;
172 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
173 final PsiFile file = PsiUtilBase.getPsiFileInEditor(myEditor, myProject);
174 if (file == null) {
175 return;
178 ShowIntentionActionsHandler.chooseActionAndInvoke(file, myEditor, cachedAction.getAction(), cachedAction.getText());
185 private PopupStep getSubStep(final IntentionActionWithTextCaching action) {
186 ShowIntentionsPass.IntentionsInfo intentions = new ShowIntentionsPass.IntentionsInfo();
187 for (final IntentionAction optionIntention : action.getOptionIntentions()) {
188 intentions.intentionsToShow.add(new HighlightInfo.IntentionActionDescriptor(optionIntention, null));
190 for (final IntentionAction optionFix : action.getOptionErrorFixes()) {
191 intentions.errorFixesToShow.add(new HighlightInfo.IntentionActionDescriptor(optionFix, null));
193 for (final IntentionAction optionFix : action.getOptionInspectionFixes()) {
194 intentions.inspectionFixesToShow.add(new HighlightInfo.IntentionActionDescriptor(optionFix, null));
197 return new IntentionListStep(myIntentionHintComponent, intentions,myEditor, myFile, myProject){
198 public String getTitle() {
199 return action.getToolName();
204 public boolean hasSubstep(final IntentionActionWithTextCaching action) {
205 return action.getOptionIntentions().size() + action.getOptionErrorFixes().size() > 0;
208 @NotNull
209 public List<IntentionActionWithTextCaching> getValues() {
210 List<IntentionActionWithTextCaching> result = new ArrayList<IntentionActionWithTextCaching>(myCachedErrorFixes);
211 result.addAll(myCachedInspectionFixes);
212 result.addAll(myCachedIntentions);
213 result.addAll(myCachedGutters);
214 Collections.sort(result, new Comparator<IntentionActionWithTextCaching>() {
215 public int compare(final IntentionActionWithTextCaching o1, final IntentionActionWithTextCaching o2) {
216 final IntentionAction action1 = o1.getAction();
217 final IntentionAction action2 = o2.getAction();
218 if (action1 instanceof EmptyIntentionAction && !(action2 instanceof EmptyIntentionAction)) return 1;
219 if (action2 instanceof EmptyIntentionAction && !(action1 instanceof EmptyIntentionAction)) return -1;
220 int weight1 = myCachedErrorFixes.contains(o1) ? 2 : myCachedInspectionFixes.contains(o1) ? 1 : 0;
221 int weight2 = myCachedErrorFixes.contains(o2) ? 2 : myCachedInspectionFixes.contains(o2) ? 1 : 0;
222 if (weight1 != weight2) {
223 return weight2 - weight1;
225 return Comparing.compare(o1.getText(), o2.getText());
228 return result;
231 @NotNull
232 public String getTextFor(final IntentionActionWithTextCaching action) {
233 return action.getAction().getText();
236 public Icon getIconFor(final IntentionActionWithTextCaching value) {
237 if (value.getIcon() != null) {
238 return value.getIcon();
241 final IntentionAction action = value.getAction();
243 //custom icon
244 if (action instanceof QuickFixWrapper) {
245 final QuickFixWrapper quickFix = (QuickFixWrapper)action;
246 if (quickFix.getFix() instanceof Iconable) {
247 final Icon icon = ((Iconable)quickFix.getFix()).getIcon(0);
248 if (icon != null) {
249 return icon;
254 if (mySettings.isShowLightBulb(action)) {
255 if (myCachedErrorFixes.contains(value)) {
256 return IntentionHintComponent.ourQuickFixIcon;
257 } else if (myCachedInspectionFixes.contains(value)) {
258 return IntentionHintComponent.ourBulbIcon;
260 else {
261 return IntentionHintComponent.ourIntentionIcon;
264 else {
265 if (myCachedErrorFixes.contains(value)) {
266 return IntentionHintComponent.ourQuickFixOffIcon;
268 else {
269 return IntentionHintComponent.ourIntentionOffIcon;
274 public void canceled() {
275 myIntentionHintComponent.canceled(this);
278 public int getDefaultOptionIndex() { return 0; }
279 public ListSeparator getSeparatorAbove(final IntentionActionWithTextCaching value) {
280 List<IntentionActionWithTextCaching> values = getValues();
281 int index = values.indexOf(value);
282 if (index == 0) return null;
283 IntentionActionWithTextCaching prev = values.get(index - 1);
285 if (myCachedErrorFixes.contains(value) != myCachedErrorFixes.contains(prev)
286 || myCachedInspectionFixes.contains(value) != myCachedInspectionFixes.contains(prev)
287 || myCachedIntentions.contains(value) != myCachedIntentions.contains(prev)
288 || value.getAction() instanceof EmptyIntentionAction != prev.getAction() instanceof EmptyIntentionAction) {
289 return new ListSeparator();
291 return null;
293 public boolean isMnemonicsNavigationEnabled() { return false; }
294 public MnemonicNavigationFilter<IntentionActionWithTextCaching> getMnemonicNavigationFilter() { return null; }
295 public boolean isSpeedSearchEnabled() { return true; }
296 public boolean isAutoSelectionEnabled() { return false; }
297 public SpeedSearchFilter<IntentionActionWithTextCaching> getSpeedSearchFilter() { return this; }
299 //speed search filter
300 public boolean canBeHidden(final IntentionActionWithTextCaching value) { return true;}
301 public String getIndexedString(final IntentionActionWithTextCaching value) { return getTextFor(value);}