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
;
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
,
68 myIntentionHintComponent
= intentionHintComponent
;
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
);
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
;
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 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()));
112 cachedAction
.addErrorFix(option
);
114 boolean isInspectionFix
= myCachedInspectionFixes
.contains(new IntentionActionWithTextCaching(option
, option
.getText()));
115 if (isInspectionFix
) {
116 cachedAction
.addInspectionFix(option
);
119 cachedAction
.addIntention(option
);
125 result
&= removeInvalidActions(cachedActions
, element
);
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
);
144 public String
getTitle() {
148 public boolean isSelectable(final IntentionActionWithTextCaching action
) {
152 public PopupStep
onChosen(final IntentionActionWithTextCaching action
, final boolean finalChoice
) {
153 if (finalChoice
&& !(action
.getAction() instanceof EmptyIntentionAction
)) {
158 if (hasSubstep(action
)) {
159 return getSubStep(action
);
165 private void applyAction(final IntentionActionWithTextCaching cachedAction
) {
166 ApplicationManager
.getApplication().invokeLater(new Runnable() {
168 HintManager
.getInstance().hideAllHints();
169 ApplicationManager
.getApplication().invokeLater(new Runnable() {
171 if (myProject
.isDisposed()) return;
172 PsiDocumentManager
.getInstance(myProject
).commitAllDocuments();
173 final PsiFile file
= PsiUtilBase
.getPsiFileInEditor(myEditor
, myProject
);
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;
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());
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();
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);
254 if (mySettings
.isShowLightBulb(action
)) {
255 if (myCachedErrorFixes
.contains(value
)) {
256 return IntentionHintComponent
.ourQuickFixIcon
;
257 } else if (myCachedInspectionFixes
.contains(value
)) {
258 return IntentionHintComponent
.ourBulbIcon
;
261 return IntentionHintComponent
.ourIntentionIcon
;
265 if (myCachedErrorFixes
.contains(value
)) {
266 return IntentionHintComponent
.ourQuickFixOffIcon
;
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();
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
);}