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
;
39 import org
.jetbrains
.annotations
.Nullable
;
47 class IntentionListStep
implements ListPopupStep
<IntentionActionWithTextCaching
>, SpeedSearchFilter
<IntentionActionWithTextCaching
> {
48 private final Set
<IntentionActionWithTextCaching
> myCachedIntentions
= new THashSet
<IntentionActionWithTextCaching
>(ACTION_TEXT_AND_CLASS_EQUALS
);
49 private final Set
<IntentionActionWithTextCaching
> myCachedErrorFixes
= new THashSet
<IntentionActionWithTextCaching
>(ACTION_TEXT_AND_CLASS_EQUALS
);
50 private final Set
<IntentionActionWithTextCaching
> myCachedInspectionFixes
= new THashSet
<IntentionActionWithTextCaching
>(ACTION_TEXT_AND_CLASS_EQUALS
);
51 private final Set
<IntentionActionWithTextCaching
> myCachedGutters
= new THashSet
<IntentionActionWithTextCaching
>(ACTION_TEXT_AND_CLASS_EQUALS
);
52 private final IntentionManagerSettings mySettings
;
54 private final IntentionHintComponent myIntentionHintComponent
;
55 private final Editor myEditor
;
56 private final PsiFile myFile
;
57 private final Project myProject
;
58 private static final TObjectHashingStrategy
<IntentionActionWithTextCaching
> ACTION_TEXT_AND_CLASS_EQUALS
= new TObjectHashingStrategy
<IntentionActionWithTextCaching
>() {
59 public int computeHashCode(final IntentionActionWithTextCaching object
) {
60 return object
.getText().hashCode();
63 public boolean equals(final IntentionActionWithTextCaching o1
, final IntentionActionWithTextCaching o2
) {
64 return o1
.getAction().getClass() == o2
.getAction().getClass() && o1
.getText().equals(o2
.getText());
67 private Runnable myFinalRunnable
;
69 IntentionListStep(@Nullable IntentionHintComponent intentionHintComponent
, ShowIntentionsPass
.IntentionsInfo intentions
, Editor editor
, PsiFile file
,
71 myIntentionHintComponent
= intentionHintComponent
;
75 mySettings
= IntentionManagerSettings
.getInstance();
76 updateActions(intentions
);
79 //true if something changed
80 boolean updateActions(ShowIntentionsPass
.IntentionsInfo intentions
) {
81 boolean result
= wrapActionsTo(intentions
.errorFixesToShow
, myCachedErrorFixes
);
82 result
&= wrapActionsTo(intentions
.inspectionFixesToShow
, myCachedInspectionFixes
);
83 result
&= wrapActionsTo(intentions
.intentionsToShow
, myCachedIntentions
);
84 result
&= wrapActionsTo(intentions
.guttersToShow
, myCachedGutters
);
88 private boolean wrapActionsTo(final List
<HighlightInfo
.IntentionActionDescriptor
> descriptors
, final Set
<IntentionActionWithTextCaching
> cachedActions
) {
89 final int caretOffset
= myEditor
.getCaretModel().getOffset();
90 final int fileOffset
= caretOffset
> 0 && caretOffset
== myFile
.getTextLength() ? caretOffset
- 1 : caretOffset
;
92 if (myFile
instanceof PsiCompiledElement
) {
95 else if (PsiDocumentManager
.getInstance(myProject
).isUncommited(myEditor
.getDocument())) {
97 FileViewProvider viewProvider
= myFile
.getViewProvider();
98 element
= viewProvider
.findElementAt(fileOffset
, viewProvider
.getBaseLanguage());
101 element
= InjectedLanguageUtil
.findElementAtNoCommit(myFile
, fileOffset
);
103 boolean result
= removeInvalidActions(cachedActions
, element
);
104 for (HighlightInfo
.IntentionActionDescriptor descriptor
: descriptors
) {
105 IntentionAction action
= descriptor
.getAction();
106 if (!isAvailable(action
, element
)) continue;
107 if (element
== null) continue;
108 IntentionActionWithTextCaching cachedAction
= wrapAction(element
, descriptor
);
109 result
&= !cachedActions
.add(cachedAction
);
114 IntentionActionWithTextCaching
wrapAction(PsiElement element
, HighlightInfo
.IntentionActionDescriptor descriptor
) {
115 IntentionActionWithTextCaching cachedAction
= new IntentionActionWithTextCaching(descriptor
);
116 final List
<IntentionAction
> options
= descriptor
.getOptions(element
);
117 if (options
!= null) {
118 for (IntentionAction option
: options
) {
119 if (!option
.isAvailable(myProject
, myEditor
, element
.getContainingFile())) continue;
120 IntentionActionWithTextCaching textCaching
= new IntentionActionWithTextCaching(option
);
121 boolean isErrorFix
= myCachedErrorFixes
.contains(textCaching
);
123 cachedAction
.addErrorFix(option
);
125 boolean isInspectionFix
= myCachedInspectionFixes
.contains(textCaching
);
126 if (isInspectionFix
) {
127 cachedAction
.addInspectionFix(option
);
130 cachedAction
.addIntention(option
);
137 private boolean removeInvalidActions(final Collection
<IntentionActionWithTextCaching
> cachedActions
, final PsiElement element
) {
138 boolean result
= true;
139 Iterator
<IntentionActionWithTextCaching
> iterator
= cachedActions
.iterator();
140 while (iterator
.hasNext()) {
141 IntentionActionWithTextCaching cachedAction
= iterator
.next();
142 IntentionAction action
= cachedAction
.getAction();
143 if (!isAvailable(action
, element
)) {
151 private boolean isAvailable(IntentionAction action
, PsiElement element
) {
152 return ShowIntentionActionsHandler
.availableFor(myFile
, myEditor
, action
, element
) != null;
155 public String
getTitle() {
159 public boolean isSelectable(final IntentionActionWithTextCaching action
) {
163 public PopupStep
onChosen(final IntentionActionWithTextCaching action
, final boolean finalChoice
) {
164 if (finalChoice
&& !(action
.getAction() instanceof EmptyIntentionAction
)) {
169 if (hasSubstep(action
)) {
170 return getSubStep(action
, action
.getToolName());
176 public Runnable
getFinalRunnable() {
177 return myFinalRunnable
;
180 private void applyAction(final IntentionActionWithTextCaching cachedAction
) {
181 myFinalRunnable
= new Runnable() {
183 HintManager
.getInstance().hideAllHints();
184 ApplicationManager
.getApplication().invokeLater(new Runnable() {
186 if (myProject
.isDisposed()) return;
187 PsiDocumentManager
.getInstance(myProject
).commitAllDocuments();
188 final PsiFile file
= PsiUtilBase
.getPsiFileInEditor(myEditor
, myProject
);
193 ShowIntentionActionsHandler
.chooseActionAndInvoke(file
, myEditor
, cachedAction
.getAction(), cachedAction
.getText());
200 IntentionListStep
getSubStep(final IntentionActionWithTextCaching action
, final String title
) {
201 ShowIntentionsPass
.IntentionsInfo intentions
= new ShowIntentionsPass
.IntentionsInfo();
202 for (final IntentionAction optionIntention
: action
.getOptionIntentions()) {
203 intentions
.intentionsToShow
.add(new HighlightInfo
.IntentionActionDescriptor(optionIntention
, null));
205 for (final IntentionAction optionFix
: action
.getOptionErrorFixes()) {
206 intentions
.errorFixesToShow
.add(new HighlightInfo
.IntentionActionDescriptor(optionFix
, null));
208 for (final IntentionAction optionFix
: action
.getOptionInspectionFixes()) {
209 intentions
.inspectionFixesToShow
.add(new HighlightInfo
.IntentionActionDescriptor(optionFix
, null));
212 return new IntentionListStep(myIntentionHintComponent
, intentions
,myEditor
, myFile
, myProject
){
213 public String
getTitle() {
219 public boolean hasSubstep(final IntentionActionWithTextCaching action
) {
220 return action
.getOptionIntentions().size() + action
.getOptionErrorFixes().size() > 0;
224 public List
<IntentionActionWithTextCaching
> getValues() {
225 List
<IntentionActionWithTextCaching
> result
= new ArrayList
<IntentionActionWithTextCaching
>(myCachedErrorFixes
);
226 result
.addAll(myCachedInspectionFixes
);
227 result
.addAll(myCachedIntentions
);
228 result
.addAll(myCachedGutters
);
229 Collections
.sort(result
, new Comparator
<IntentionActionWithTextCaching
>() {
230 public int compare(final IntentionActionWithTextCaching o1
, final IntentionActionWithTextCaching o2
) {
231 int weight1
= getWeight(o1
);
232 int weight2
= getWeight(o2
);
233 if (weight1
!= weight2
) {
234 return weight2
- weight1
;
236 return Comparing
.compare(o1
.getText(), o2
.getText());
242 private int getWeight(IntentionActionWithTextCaching action
) {
243 if (action
.getAction() instanceof PreferredAction
) {
246 else if (myCachedErrorFixes
.contains(action
)) {
249 else if (myCachedInspectionFixes
.contains(action
)) {
252 else if (action
.getAction() instanceof EmptyIntentionAction
) {
261 public String
getTextFor(final IntentionActionWithTextCaching action
) {
262 return action
.getAction().getText();
265 public Icon
getIconFor(final IntentionActionWithTextCaching value
) {
266 if (value
.getIcon() != null) {
267 return value
.getIcon();
270 final IntentionAction action
= value
.getAction();
273 if (action
instanceof QuickFixWrapper
) {
274 final QuickFixWrapper quickFix
= (QuickFixWrapper
)action
;
275 if (quickFix
.getFix() instanceof Iconable
) {
276 final Icon icon
= ((Iconable
)quickFix
.getFix()).getIcon(0);
283 if (mySettings
.isShowLightBulb(action
)) {
284 return myCachedErrorFixes
.contains(value
) ? IntentionHintComponent
.ourQuickFixIcon
285 : myCachedInspectionFixes
.contains(value
) ? IntentionHintComponent
.ourBulbIcon
:
286 IntentionHintComponent
.ourIntentionIcon
;
289 return myCachedErrorFixes
.contains(value
) ? IntentionHintComponent
.ourQuickFixOffIcon
: IntentionHintComponent
.ourIntentionOffIcon
;
293 public void canceled() {
294 if (myIntentionHintComponent
!= null) {
295 myIntentionHintComponent
.canceled(this);
299 public int getDefaultOptionIndex() { return 0; }
300 public ListSeparator
getSeparatorAbove(final IntentionActionWithTextCaching value
) {
301 List
<IntentionActionWithTextCaching
> values
= getValues();
302 int index
= values
.indexOf(value
);
303 if (index
== 0) return null;
304 IntentionActionWithTextCaching prev
= values
.get(index
- 1);
306 if (getWeight(value
) != getWeight(prev
)) {
307 return new ListSeparator();
311 public boolean isMnemonicsNavigationEnabled() { return false; }
312 public MnemonicNavigationFilter
<IntentionActionWithTextCaching
> getMnemonicNavigationFilter() { return null; }
313 public boolean isSpeedSearchEnabled() { return true; }
314 public boolean isAutoSelectionEnabled() { return false; }
315 public SpeedSearchFilter
<IntentionActionWithTextCaching
> getSpeedSearchFilter() { return this; }
317 //speed search filter
318 public boolean canBeHidden(final IntentionActionWithTextCaching value
) { return true;}
319 public String
getIndexedString(final IntentionActionWithTextCaching value
) { return getTextFor(value
);}