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
.PreferredAction
;
25 import com
.intellij
.codeInsight
.intention
.impl
.config
.IntentionManagerSettings
;
26 import com
.intellij
.codeInspection
.ex
.QuickFixWrapper
;
27 import com
.intellij
.openapi
.application
.ApplicationManager
;
28 import com
.intellij
.openapi
.editor
.Editor
;
29 import com
.intellij
.openapi
.project
.Project
;
30 import com
.intellij
.openapi
.ui
.popup
.*;
31 import com
.intellij
.openapi
.util
.Comparing
;
32 import com
.intellij
.openapi
.util
.Iconable
;
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
;
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());
65 private Runnable myFinalRunnable
;
67 IntentionListStep(IntentionHintComponent intentionHintComponent
, ShowIntentionsPass
.IntentionsInfo intentions
, Editor editor
, PsiFile file
,
69 myIntentionHintComponent
= intentionHintComponent
;
73 mySettings
= IntentionManagerSettings
.getInstance();
74 updateActions(intentions
);
77 //true if something changed
78 boolean updateActions(ShowIntentionsPass
.IntentionsInfo intentions
) {
79 boolean result
= wrapActionsTo(intentions
.errorFixesToShow
, myCachedErrorFixes
);
80 result
&= wrapActionsTo(intentions
.inspectionFixesToShow
, myCachedInspectionFixes
);
81 result
&= wrapActionsTo(intentions
.intentionsToShow
, myCachedIntentions
);
82 result
&= wrapActionsTo(intentions
.guttersToShow
, myCachedGutters
);
86 private boolean wrapActionsTo(final List
<HighlightInfo
.IntentionActionDescriptor
> descriptors
, final Set
<IntentionActionWithTextCaching
> cachedActions
) {
87 final int caretOffset
= myEditor
.getCaretModel().getOffset();
88 final int fileOffset
= caretOffset
> 0 && caretOffset
== myFile
.getTextLength() ? caretOffset
- 1 : caretOffset
;
90 if (myFile
instanceof PsiCompiledElement
) {
93 else if (PsiDocumentManager
.getInstance(myProject
).isUncommited(myEditor
.getDocument())) {
95 FileViewProvider viewProvider
= myFile
.getViewProvider();
96 element
= viewProvider
.findElementAt(fileOffset
, viewProvider
.getBaseLanguage());
99 element
= InjectedLanguageUtil
.findElementAtNoCommit(myFile
, fileOffset
);
101 boolean result
= removeInvalidActions(cachedActions
, element
);
102 for (HighlightInfo
.IntentionActionDescriptor descriptor
: descriptors
) {
103 IntentionAction action
= descriptor
.getAction();
104 if (!isAvailable(action
, element
)) continue;
105 IntentionActionWithTextCaching cachedAction
= new IntentionActionWithTextCaching(action
, descriptor
.getDisplayName(), descriptor
.getIcon());
106 result
&= !cachedActions
.add(cachedAction
);
107 if (element
== null) continue;
108 final List
<IntentionAction
> options
= descriptor
.getOptions(element
);
109 if (options
!= null) {
110 for (IntentionAction option
: options
) {
111 if (!option
.isAvailable(myProject
, myEditor
, element
.getContainingFile())) continue;
112 IntentionActionWithTextCaching textCaching
= new IntentionActionWithTextCaching(option
, option
.getText());
113 boolean isErrorFix
= myCachedErrorFixes
.contains(textCaching
);
115 cachedAction
.addErrorFix(option
);
117 boolean isInspectionFix
= myCachedInspectionFixes
.contains(textCaching
);
118 if (isInspectionFix
) {
119 cachedAction
.addInspectionFix(option
);
122 cachedAction
.addIntention(option
);
130 private boolean removeInvalidActions(final Collection
<IntentionActionWithTextCaching
> cachedActions
, final PsiElement element
) {
131 boolean result
= true;
132 Iterator
<IntentionActionWithTextCaching
> iterator
= cachedActions
.iterator();
133 while (iterator
.hasNext()) {
134 IntentionActionWithTextCaching cachedAction
= iterator
.next();
135 IntentionAction action
= cachedAction
.getAction();
136 if (!isAvailable(action
, element
)) {
144 private boolean isAvailable(IntentionAction action
, PsiElement element
) {
145 return ShowIntentionActionsHandler
.availableFor(myFile
, myEditor
, action
, element
) != null;
148 public String
getTitle() {
152 public boolean isSelectable(final IntentionActionWithTextCaching action
) {
156 public PopupStep
onChosen(final IntentionActionWithTextCaching action
, final boolean finalChoice
) {
157 if (finalChoice
&& !(action
.getAction() instanceof EmptyIntentionAction
)) {
162 if (hasSubstep(action
)) {
163 return getSubStep(action
);
169 public Runnable
getFinalRunnable() {
170 return myFinalRunnable
;
173 private void applyAction(final IntentionActionWithTextCaching cachedAction
) {
174 myFinalRunnable
= new Runnable() {
176 HintManager
.getInstance().hideAllHints();
177 ApplicationManager
.getApplication().invokeLater(new Runnable() {
179 if (myProject
.isDisposed()) return;
180 PsiDocumentManager
.getInstance(myProject
).commitAllDocuments();
181 final PsiFile file
= PsiUtilBase
.getPsiFileInEditor(myEditor
, myProject
);
186 ShowIntentionActionsHandler
.chooseActionAndInvoke(file
, myEditor
, cachedAction
.getAction(), cachedAction
.getText());
193 private PopupStep
getSubStep(final IntentionActionWithTextCaching action
) {
194 ShowIntentionsPass
.IntentionsInfo intentions
= new ShowIntentionsPass
.IntentionsInfo();
195 for (final IntentionAction optionIntention
: action
.getOptionIntentions()) {
196 intentions
.intentionsToShow
.add(new HighlightInfo
.IntentionActionDescriptor(optionIntention
, null));
198 for (final IntentionAction optionFix
: action
.getOptionErrorFixes()) {
199 intentions
.errorFixesToShow
.add(new HighlightInfo
.IntentionActionDescriptor(optionFix
, null));
201 for (final IntentionAction optionFix
: action
.getOptionInspectionFixes()) {
202 intentions
.inspectionFixesToShow
.add(new HighlightInfo
.IntentionActionDescriptor(optionFix
, null));
205 return new IntentionListStep(myIntentionHintComponent
, intentions
,myEditor
, myFile
, myProject
){
206 public String
getTitle() {
207 return action
.getToolName();
212 public boolean hasSubstep(final IntentionActionWithTextCaching action
) {
213 return action
.getOptionIntentions().size() + action
.getOptionErrorFixes().size() > 0;
217 public List
<IntentionActionWithTextCaching
> getValues() {
218 List
<IntentionActionWithTextCaching
> result
= new ArrayList
<IntentionActionWithTextCaching
>(myCachedErrorFixes
);
219 result
.addAll(myCachedInspectionFixes
);
220 result
.addAll(myCachedIntentions
);
221 result
.addAll(myCachedGutters
);
222 Collections
.sort(result
, new Comparator
<IntentionActionWithTextCaching
>() {
223 public int compare(final IntentionActionWithTextCaching o1
, final IntentionActionWithTextCaching o2
) {
224 int weight1
= getWeight(o1
);
225 int weight2
= getWeight(o2
);
226 if (weight1
!= weight2
) {
227 return weight2
- weight1
;
229 return Comparing
.compare(o1
.getText(), o2
.getText());
235 private int getWeight(IntentionActionWithTextCaching action
) {
236 if (action
.getAction() instanceof PreferredAction
) {
239 else if (myCachedErrorFixes
.contains(action
)) {
242 else if (myCachedInspectionFixes
.contains(action
)) {
245 else if (action
.getAction() instanceof EmptyIntentionAction
) {
254 public String
getTextFor(final IntentionActionWithTextCaching action
) {
255 return action
.getAction().getText();
258 public Icon
getIconFor(final IntentionActionWithTextCaching value
) {
259 if (value
.getIcon() != null) {
260 return value
.getIcon();
263 final IntentionAction action
= value
.getAction();
266 if (action
instanceof QuickFixWrapper
) {
267 final QuickFixWrapper quickFix
= (QuickFixWrapper
)action
;
268 if (quickFix
.getFix() instanceof Iconable
) {
269 final Icon icon
= ((Iconable
)quickFix
.getFix()).getIcon(0);
276 if (mySettings
.isShowLightBulb(action
)) {
277 return myCachedErrorFixes
.contains(value
) ? IntentionHintComponent
.ourQuickFixIcon
278 : myCachedInspectionFixes
.contains(value
) ? IntentionHintComponent
.ourBulbIcon
:
279 IntentionHintComponent
.ourIntentionIcon
;
282 return myCachedErrorFixes
.contains(value
) ? IntentionHintComponent
.ourQuickFixOffIcon
: IntentionHintComponent
.ourIntentionOffIcon
;
286 public void canceled() {
287 myIntentionHintComponent
.canceled(this);
290 public int getDefaultOptionIndex() { return 0; }
291 public ListSeparator
getSeparatorAbove(final IntentionActionWithTextCaching value
) {
292 List
<IntentionActionWithTextCaching
> values
= getValues();
293 int index
= values
.indexOf(value
);
294 if (index
== 0) return null;
295 IntentionActionWithTextCaching prev
= values
.get(index
- 1);
297 if (getWeight(value
) != getWeight(prev
)) {
298 return new ListSeparator();
302 public boolean isMnemonicsNavigationEnabled() { return false; }
303 public MnemonicNavigationFilter
<IntentionActionWithTextCaching
> getMnemonicNavigationFilter() { return null; }
304 public boolean isSpeedSearchEnabled() { return true; }
305 public boolean isAutoSelectionEnabled() { return false; }
306 public SpeedSearchFilter
<IntentionActionWithTextCaching
> getSpeedSearchFilter() { return this; }
308 //speed search filter
309 public boolean canBeHidden(final IntentionActionWithTextCaching value
) { return true;}
310 public String
getIndexedString(final IntentionActionWithTextCaching value
) { return getTextFor(value
);}