import popup fixed
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / intention / impl / ShowIntentionActionsHandler.java
1 /*
2  * Copyright 2000-2009 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
17 package com.intellij.codeInsight.intention.impl;
18
19 import com.intellij.codeInsight.CodeInsightActionHandler;
20 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
21 import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
22 import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass;
23 import com.intellij.codeInsight.hint.HintManagerImpl;
24 import com.intellij.codeInsight.intention.IntentionAction;
25 import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
26 import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
27 import com.intellij.codeInsight.template.impl.TemplateState;
28 import com.intellij.openapi.application.ApplicationManager;
29 import com.intellij.openapi.command.CommandProcessor;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.editor.Editor;
32 import com.intellij.openapi.project.IndexNotReadyException;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.util.Pair;
35 import com.intellij.psi.PsiCodeFragment;
36 import com.intellij.psi.PsiDocumentManager;
37 import com.intellij.psi.PsiElement;
38 import com.intellij.psi.PsiFile;
39 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
40 import com.intellij.util.IncorrectOperationException;
41 import com.intellij.util.PairProcessor;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44
45 /**
46  * @author mike
47  */
48 public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
49   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler");
50
51   public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
52     PsiDocumentManager.getInstance(project).commitAllDocuments();
53
54     if (HintManagerImpl.getInstanceImpl().performCurrentQuestionAction()) return;
55
56     //intentions check isWritable before modification: if (!file.isWritable()) return;
57     if (file instanceof PsiCodeFragment) return;
58
59     TemplateState state = TemplateManagerImpl.getTemplateState(editor);
60     if (state != null && !state.isFinished()) {
61       return;
62     }
63
64     final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(project);
65     codeAnalyzer.autoImportReferenceAtCursor(editor, file); //let autoimport complete
66
67     ShowIntentionsPass.IntentionsInfo intentions = new ShowIntentionsPass.IntentionsInfo();
68     ShowIntentionsPass.getActionsToShow(editor, file, intentions, -1);
69     
70     if (!intentions.isEmpty()) {
71       IntentionHintComponent.showIntentionHint(project, file, editor, intentions, true);
72     }
73   }
74
75   public boolean startInWriteAction() {
76     return false;
77   }
78
79   // returns editor,file where the action is available or null if there are none
80   public static boolean availableFor(@NotNull PsiFile file, @NotNull Editor editor, @NotNull IntentionAction action) {
81     if (!file.isValid()) return false;
82
83     int offset = editor.getCaretModel().getOffset();
84     PsiElement element = file.findElementAt(offset);
85     boolean inProject = file.getManager().isInProject(file);
86     return isAvailableHere(editor, file, element, inProject, action);
87   }
88   
89   private static boolean isAvailableHere(Editor editor, PsiFile psiFile, PsiElement psiElement, boolean inProject, IntentionAction action) {
90     try {
91       Project project = psiFile.getProject();
92       if (action instanceof PsiElementBaseIntentionAction) {
93         if (!inProject || psiElement == null || !((PsiElementBaseIntentionAction)action).isAvailable(project, editor, psiElement)) return false;
94       }
95       else if (!action.isAvailable(project, editor, psiFile)) {
96         return false;
97       }
98     }
99     catch (IndexNotReadyException e) {
100       return false;
101     }
102     return true;
103   }
104
105   @Nullable
106   public static Pair<PsiFile,Editor> chooseBetweenHostAndInjected(PsiFile hostFile, Editor hostEditor, PairProcessor<PsiFile, Editor> predicate) {
107     Editor editorToApply = null;
108     PsiFile fileToApply = null;
109
110     int offset = hostEditor.getCaretModel().getOffset();
111     PsiFile injectedFile = InjectedLanguageUtil.findInjectedPsiNoCommit(hostFile, offset);
112     if (injectedFile != null) {
113       Editor injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, injectedFile);
114       if (predicate.process(injectedFile, injectedEditor)) {
115         editorToApply = injectedEditor;
116         fileToApply = injectedFile;
117       }
118     }
119
120     if (editorToApply == null && predicate.process(hostFile, hostEditor)) {
121       editorToApply = hostEditor;
122       fileToApply = hostFile;
123     }
124     if (editorToApply == null) return null;
125     return Pair.create(fileToApply, editorToApply);
126   }
127   
128   public static boolean chooseActionAndInvoke(PsiFile hostFile, final Editor hostEditor, final IntentionAction action, final String text) {
129     final Project project = hostFile.getProject();
130
131     Pair<PsiFile, Editor> pair = chooseBetweenHostAndInjected(hostFile, hostEditor, new PairProcessor<PsiFile, Editor>() {
132       public boolean process(PsiFile psiFile, Editor editor) {
133         return availableFor(psiFile, editor, action);
134       }
135     });
136     if (pair == null) return false;
137     final Editor editorToApply = pair.second;
138     final PsiFile fileToApply = pair.first;
139
140     Runnable runnable = new Runnable() {
141       public void run() {
142         try {
143           action.invoke(project, editorToApply, fileToApply);
144         }
145         catch (IncorrectOperationException e) {
146           LOG.error(e);
147         }
148         DaemonCodeAnalyzer.getInstance(project).updateVisibleHighlighters(hostEditor);
149       }
150     };
151
152     if (action.startInWriteAction()) {
153       final Runnable _runnable = runnable;
154       runnable = new Runnable() {
155         public void run() {
156           ApplicationManager.getApplication().runWriteAction(_runnable);
157         }
158       };
159     }
160
161     CommandProcessor.getInstance().executeCommand(project, runnable, text, null);
162     return true;
163   }
164 }