2 * Copyright 2000-2015 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.TransactionGuard;
36 import com.intellij.openapi.application.WriteAction;
37 import com.intellij.openapi.command.CommandProcessor;
38 import com.intellij.openapi.diagnostic.Logger;
39 import com.intellij.openapi.editor.Editor;
40 import com.intellij.openapi.project.IndexNotReadyException;
41 import com.intellij.openapi.project.Project;
42 import com.intellij.openapi.util.Pair;
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 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(project);
74 letAutoImportComplete(editor, file, codeAnalyzer);
76 ShowIntentionsPass.IntentionsInfo intentions = new ShowIntentionsPass.IntentionsInfo();
77 ShowIntentionsPass.getActionsToShow(editor, file, intentions, -1);
78 IntentionHintComponent hintComponent = codeAnalyzer.getLastIntentionHint();
79 if (hintComponent != null) {
80 IntentionHintComponent.PopupUpdateResult result = hintComponent.isForEditor(editor)
81 ? hintComponent.updateActions(intentions)
82 : IntentionHintComponent.PopupUpdateResult.HIDE_AND_RECREATE;
83 if (result == IntentionHintComponent.PopupUpdateResult.HIDE_AND_RECREATE) {
88 if (HintManagerImpl.getInstanceImpl().performCurrentQuestionAction()) return;
90 //intentions check isWritable before modification: if (!file.isWritable()) return;
92 TemplateState state = TemplateManagerImpl.getTemplateState(editor);
93 if (state != null && !state.isFinished()) {
97 if (!intentions.isEmpty()) {
98 IntentionHintComponent.showIntentionHint(project, file, editor, intentions, true);
102 private static void letAutoImportComplete(@NotNull Editor editor, @NotNull PsiFile file, DaemonCodeAnalyzerImpl codeAnalyzer) {
103 CommandProcessor.getInstance().runUndoTransparentAction(() -> codeAnalyzer.autoImportReferenceAtCursor(editor, file));
107 public boolean startInWriteAction() {
111 public static boolean availableFor(@NotNull PsiFile psiFile, @NotNull Editor editor, @NotNull IntentionAction action) {
112 if (!psiFile.isValid()) return false;
114 int offset = editor.getCaretModel().getOffset();
115 PsiElement psiElement = psiFile.findElementAt(offset);
116 boolean inProject = psiFile.getManager().isInProject(psiFile);
118 Project project = psiFile.getProject();
119 if (action instanceof SuppressIntentionActionFromFix) {
120 final ThreeState shouldBeAppliedToInjectionHost = ((SuppressIntentionActionFromFix)action).isShouldBeAppliedToInjectionHost();
121 if (editor instanceof EditorWindow && shouldBeAppliedToInjectionHost == ThreeState.YES) {
124 if (!(editor instanceof EditorWindow) && shouldBeAppliedToInjectionHost == ThreeState.NO) {
129 if (action instanceof PsiElementBaseIntentionAction) {
130 if (!inProject || psiElement == null || !((PsiElementBaseIntentionAction)action).isAvailable(project, editor, psiElement)) return false;
132 else if (!action.isAvailable(project, editor, psiFile)) {
136 catch (IndexNotReadyException e) {
143 public static Pair<PsiFile,Editor> chooseBetweenHostAndInjected(@NotNull PsiFile hostFile, @NotNull Editor hostEditor, @NotNull PairProcessor<PsiFile, Editor> predicate) {
144 Editor editorToApply = null;
145 PsiFile fileToApply = null;
147 int offset = hostEditor.getCaretModel().getOffset();
148 PsiFile injectedFile = InjectedLanguageUtil.findInjectedPsiNoCommit(hostFile, offset);
149 if (injectedFile != null) {
150 Editor injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, injectedFile);
151 if (predicate.process(injectedFile, injectedEditor)) {
152 editorToApply = injectedEditor;
153 fileToApply = injectedFile;
157 if (editorToApply == null && predicate.process(hostFile, hostEditor)) {
158 editorToApply = hostEditor;
159 fileToApply = hostFile;
161 if (editorToApply == null) return null;
162 return Pair.create(fileToApply, editorToApply);
165 public static boolean chooseActionAndInvoke(@NotNull PsiFile hostFile,
166 @NotNull final Editor hostEditor,
167 @NotNull final IntentionAction action,
168 @NotNull String text) {
169 final Project project = hostFile.getProject();
170 return chooseActionAndInvoke(hostFile, hostEditor, action, text, project);
173 static boolean chooseActionAndInvoke(@NotNull PsiFile hostFile,
174 @Nullable final Editor hostEditor,
175 @NotNull final IntentionAction action,
176 @NotNull String text,
177 @NotNull final Project project) {
178 FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.quickFix");
179 ((FeatureUsageTrackerImpl)FeatureUsageTracker.getInstance()).getFixesStats().registerInvocation();
181 final Pair<PsiFile, Editor> pair = hostEditor != null ? chooseBetweenHostAndInjected(hostFile, hostEditor,
182 (psiFile, editor) -> availableFor(psiFile, editor, action)) : Pair.<PsiFile, Editor>create(hostFile, null);
183 if (pair == null) return false;
185 CommandProcessor.getInstance().executeCommand(project, () -> TransactionGuard.getInstance().submitTransactionAndWait(() -> {
186 Runnable r = () -> action.invoke(project, pair.second, pair.first);
188 if (action.startInWriteAction()) {
189 WriteAction.run(r::run);
194 catch (IncorrectOperationException e) {
197 if (hostEditor != null) {
198 DaemonCodeAnalyzer.getInstance(project).updateVisibleHighlighters(hostEditor);