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.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.lookup.LookupEx;
27 import com.intellij.codeInsight.lookup.LookupManager;
28 import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
29 import com.intellij.codeInsight.template.impl.TemplateState;
30 import com.intellij.codeInspection.SuppressIntentionActionFromFix;
31 import com.intellij.featureStatistics.FeatureUsageTracker;
32 import com.intellij.featureStatistics.FeatureUsageTrackerImpl;
33 import com.intellij.injected.editor.EditorWindow;
34 import com.intellij.lang.injection.InjectedLanguageManager;
35 import com.intellij.openapi.application.ApplicationManager;
36 import com.intellij.openapi.command.CommandProcessor;
37 import com.intellij.openapi.diagnostic.Logger;
38 import com.intellij.openapi.editor.Editor;
39 import com.intellij.openapi.project.IndexNotReadyException;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.util.Pair;
42 import com.intellij.psi.PsiCodeFragment;
43 import com.intellij.psi.PsiDocumentManager;
44 import com.intellij.psi.PsiElement;
45 import com.intellij.psi.PsiFile;
46 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
47 import com.intellij.util.IncorrectOperationException;
48 import com.intellij.util.PairProcessor;
49 import com.intellij.util.ThreeState;
50 import org.jetbrains.annotations.NotNull;
51 import org.jetbrains.annotations.Nullable;
56 public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
57 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler");
60 public void invoke(@NotNull final Project project, @NotNull Editor editor, @NotNull PsiFile file) {
61 PsiDocumentManager.getInstance(project).commitAllDocuments();
62 if (editor instanceof EditorWindow) {
63 editor = ((EditorWindow)editor).getDelegate();
64 file = InjectedLanguageManager.getInstance(file.getProject()).getTopLevelFile(file);
67 final LookupEx lookup = LookupManager.getActiveLookup(editor);
69 lookup.showElementActions();
73 if (HintManagerImpl.getInstanceImpl().performCurrentQuestionAction()) return;
75 //intentions check isWritable before modification: if (!file.isWritable()) return;
76 if (file instanceof PsiCodeFragment) return;
78 TemplateState state = TemplateManagerImpl.getTemplateState(editor);
79 if (state != null && !state.isFinished()) {
83 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(project);
84 codeAnalyzer.autoImportReferenceAtCursor(editor, file); //let autoimport complete
86 ShowIntentionsPass.IntentionsInfo intentions = new ShowIntentionsPass.IntentionsInfo();
87 ShowIntentionsPass.getActionsToShow(editor, file, intentions, -1);
89 if (!intentions.isEmpty()) {
90 IntentionHintComponent.showIntentionHint(project, file, editor, intentions, true);
95 public boolean startInWriteAction() {
99 // returns editor,file where the action is available or null if there are none
100 public static boolean availableFor(@NotNull PsiFile file, @NotNull Editor editor, @NotNull IntentionAction action) {
101 if (!file.isValid()) return false;
103 int offset = editor.getCaretModel().getOffset();
104 PsiElement element = file.findElementAt(offset);
105 boolean inProject = file.getManager().isInProject(file);
106 return isAvailableHere(editor, file, element, inProject, action);
109 private static boolean isAvailableHere(Editor editor, PsiFile psiFile, PsiElement psiElement, boolean inProject, IntentionAction action) {
111 Project project = psiFile.getProject();
112 if (action instanceof SuppressIntentionActionFromFix) {
113 final ThreeState shouldBeAppliedToInjectionHost = ((SuppressIntentionActionFromFix)action).isShouldBeAppliedToInjectionHost();
114 if (editor instanceof EditorWindow && shouldBeAppliedToInjectionHost == ThreeState.YES) {
117 if (!(editor instanceof EditorWindow) && shouldBeAppliedToInjectionHost == ThreeState.NO) {
122 if (action instanceof PsiElementBaseIntentionAction) {
123 if (!inProject || psiElement == null || !((PsiElementBaseIntentionAction)action).isAvailable(project, editor, psiElement)) return false;
125 else if (!action.isAvailable(project, editor, psiFile)) {
129 catch (IndexNotReadyException e) {
136 public static Pair<PsiFile,Editor> chooseBetweenHostAndInjected(@NotNull PsiFile hostFile, @NotNull Editor hostEditor, @NotNull PairProcessor<PsiFile, Editor> predicate) {
137 Editor editorToApply = null;
138 PsiFile fileToApply = null;
140 int offset = hostEditor.getCaretModel().getOffset();
141 PsiFile injectedFile = InjectedLanguageUtil.findInjectedPsiNoCommit(hostFile, offset);
142 if (injectedFile != null) {
143 Editor injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, injectedFile);
144 if (predicate.process(injectedFile, injectedEditor)) {
145 editorToApply = injectedEditor;
146 fileToApply = injectedFile;
150 if (editorToApply == null && predicate.process(hostFile, hostEditor)) {
151 editorToApply = hostEditor;
152 fileToApply = hostFile;
154 if (editorToApply == null) return null;
155 return Pair.create(fileToApply, editorToApply);
158 public static boolean chooseActionAndInvoke(@NotNull PsiFile hostFile,
159 @NotNull final Editor hostEditor,
160 @NotNull final IntentionAction action,
161 @NotNull String text) {
162 if (!hostFile.isValid()) return false;
163 final Project project = hostFile.getProject();
164 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.quickFix");
165 ((FeatureUsageTrackerImpl)FeatureUsageTracker.getInstance()).getFixesStats().registerInvocation();
167 Pair<PsiFile, Editor> pair = chooseBetweenHostAndInjected(hostFile, hostEditor, new PairProcessor<PsiFile, Editor>() {
169 public boolean process(PsiFile psiFile, Editor editor) {
170 return availableFor(psiFile, editor, action);
173 if (pair == null) return false;
174 final Editor editorToApply = pair.second;
175 final PsiFile fileToApply = pair.first;
177 Runnable runnable = new Runnable() {
181 action.invoke(project, editorToApply, fileToApply);
183 catch (IncorrectOperationException e) {
186 DaemonCodeAnalyzer.getInstance(project).updateVisibleHighlighters(hostEditor);
190 if (action.startInWriteAction()) {
191 final Runnable _runnable = runnable;
192 runnable = new Runnable() {
195 ApplicationManager.getApplication().runWriteAction(_runnable);
200 CommandProcessor.getInstance().executeCommand(project, runnable, text, null);