diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/IncreaseLanguageLevelFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/IncreaseLanguageLevelFix.java index fe5f6ea7ad37..d2720899a142 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/IncreaseLanguageLevelFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/IncreaseLanguageLevelFix.java @@ -73,6 +73,11 @@ public class IncreaseLanguageLevelFix implements IntentionAction { JavaProjectModelModificationService.getInstance(project).changeLanguageLevel(module, myLevel); } + @Override + public boolean shouldMakeCurrentFileWritable() { + return false; + } + @Override public boolean startInWriteAction() { return true; diff --git a/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicConstantInspection.java b/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicConstantInspection.java index 6cc93d4cd165..cbe7edc14d84 100644 --- a/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicConstantInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/magicConstant/MagicConstantInspection.java @@ -22,7 +22,6 @@ import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInspection.*; import com.intellij.ide.util.treeView.AbstractTreeNode; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModificator; @@ -209,13 +208,16 @@ public class MagicConstantInspection extends BaseJavaLocalInspectionTool { return "Attach annotations"; } + @Override + public boolean shouldMakeCurrentFileWritable() { + return false; + } + @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - ApplicationManager.getApplication().runWriteAction(() -> { - SdkModificator modificator = finalJdk.getSdkModificator(); - JavaSdkImpl.attachJdkAnnotations(modificator); - modificator.commitChanges(); - }); + SdkModificator modificator = finalJdk.getSdkModificator(); + JavaSdkImpl.attachJdkAnnotations(modificator); + modificator.commitChanges(); } }); } diff --git a/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionAction.java b/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionAction.java index ba47b7f5e045..bec79d488ec3 100644 --- a/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionAction.java +++ b/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionAction.java @@ -16,7 +16,6 @@ package com.intellij.codeInsight.intention; import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.WriteActionAware; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; @@ -40,7 +39,7 @@ import org.jetbrains.annotations.NotNull; * * @see IntentionManager#registerIntentionAndMetaData(IntentionAction, String...) */ -public interface IntentionAction extends WriteActionAware { +public interface IntentionAction extends IntentionLike { IntentionAction[] EMPTY_ARRAY = new IntentionAction[0]; /** * Returns text to be shown in the list of available actions, if this action diff --git a/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionLike.java b/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionLike.java new file mode 100644 index 000000000000..3408ae0d84a7 --- /dev/null +++ b/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionLike.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.intention; + +import com.intellij.openapi.application.WriteActionAware; + +/** + * An interface that {@link IntentionAction} and {@link com.intellij.codeInspection.LocalQuickFix} share. + * + * @since 171.* + * @author peter + */ +public interface IntentionLike extends WriteActionAware { + + /** + * Controls whether this intention/fix is going to modify the current editor file. + * If {@code true}, and the file is read-only, + * it will be made writable (honoring version control integration) before the intention/fix is invoked.

+ * + * By default, as a heuristic, returns the same as {@link #startInWriteAction()}.

+ * + * If the action is going to modify multiple files, or the set of the files is unknown in advance, please + * don't bother overriding this method, return {@code false} from {@link #startInWriteAction()}, and call {@link com.intellij.codeInsight.FileModificationService} methods in the implementation, and take write actions yourself as needed. + */ + default boolean shouldMakeCurrentFileWritable() { + return startInWriteAction(); + } + +} diff --git a/platform/analysis-api/src/com/intellij/codeInsight/intention/impl/PriorityActionWrapper.java b/platform/analysis-api/src/com/intellij/codeInsight/intention/impl/PriorityActionWrapper.java index a3fe520fde2e..3585489432ca 100644 --- a/platform/analysis-api/src/com/intellij/codeInsight/intention/impl/PriorityActionWrapper.java +++ b/platform/analysis-api/src/com/intellij/codeInsight/intention/impl/PriorityActionWrapper.java @@ -61,6 +61,11 @@ public abstract class PriorityActionWrapper extends LocalQuickFixAndIntentionAct return fix.startInWriteAction(); } + @Override + public boolean shouldMakeCurrentFileWritable() { + return fix.shouldMakeCurrentFileWritable(); + } + @NotNull @Override public String getText() { diff --git a/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java b/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java index 585e89de8f38..8904d1475c61 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java @@ -15,8 +15,10 @@ */ package com.intellij.codeInspection; +import com.intellij.codeInsight.intention.IntentionLike; + /** - * QuickFix based on {@link com.intellij.codeInspection.ProblemDescriptor ProblemDescriptor} + * QuickFix based on {@link ProblemDescriptor ProblemDescriptor} *

* N.B. Please DO NOT store PSI elements inside the LocalQuickFix instance, to avoid holding too much PSI files during inspection. * Instead, use the {@link ProblemDescriptor#getPsiElement()} @@ -32,9 +34,9 @@ package com.intellij.codeInspection; * * @author max * @see LocalQuickFixBase - * @see com.intellij.codeInspection.ProblemDescriptor + * @see ProblemDescriptor * @see com.intellij.openapi.util.Iconable */ -public interface LocalQuickFix extends QuickFix { +public interface LocalQuickFix extends QuickFix, IntentionLike { LocalQuickFix[] EMPTY_ARRAY = new LocalQuickFix[0]; } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ExternalAnnotatorInspectionVisitor.java b/platform/analysis-impl/src/com/intellij/codeInspection/ExternalAnnotatorInspectionVisitor.java index 2dfa17da3ab8..4c3a69791191 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ExternalAnnotatorInspectionVisitor.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ExternalAnnotatorInspectionVisitor.java @@ -169,6 +169,11 @@ public class ExternalAnnotatorInspectionVisitor extends PsiElementVisitor { myAction.invoke(project, null, getPsiFile(descriptor)); } + @Override + public boolean shouldMakeCurrentFileWritable() { + return myAction.shouldMakeCurrentFileWritable(); + } + @Nullable private static PsiFile getPsiFile(@NotNull ProblemDescriptor descriptor) { PsiElement startElement = descriptor.getStartElement(); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/QuickFixWrapper.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/QuickFixWrapper.java index 774ee7101b93..5c6d17c4c560 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/QuickFixWrapper.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/QuickFixWrapper.java @@ -95,6 +95,11 @@ public class QuickFixWrapper implements IntentionAction { return getFix().startInWriteAction(); } + @Override + public boolean shouldMakeCurrentFileWritable() { + return getFix().shouldMakeCurrentFileWritable(); + } + public LocalQuickFix getFix() { return (LocalQuickFix)myDescriptor.getFixes()[myFixNumber]; } diff --git a/platform/lang-api/src/com/intellij/codeInsight/intention/impl/PriorityIntentionActionWrapper.java b/platform/lang-api/src/com/intellij/codeInsight/intention/impl/PriorityIntentionActionWrapper.java index dfeb5a00e13c..3a8ea981c469 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/intention/impl/PriorityIntentionActionWrapper.java +++ b/platform/lang-api/src/com/intellij/codeInsight/intention/impl/PriorityIntentionActionWrapper.java @@ -56,6 +56,11 @@ public abstract class PriorityIntentionActionWrapper implements IntentionAction action.invoke(project, editor, file); } + @Override + public boolean shouldMakeCurrentFileWritable() { + return action.shouldMakeCurrentFileWritable(); + } + @Override public boolean startInWriteAction() { return action.startInWriteAction(); diff --git a/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java b/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java index 7453e518bbc7..8c2511e8f42f 100644 --- a/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java +++ b/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java @@ -69,6 +69,11 @@ public class IntentionWrapper implements LocalQuickFix, IntentionAction, ActionC myAction.invoke(project, editor, file); } + @Override + public final boolean shouldMakeCurrentFileWritable() { + return myAction.shouldMakeCurrentFileWritable(); + } + @Override public boolean startInWriteAction() { return myAction.startInWriteAction(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/quickFix/CreateFileFix.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/quickFix/CreateFileFix.java index 7da35aed0061..fae9f85394e3 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/quickFix/CreateFileFix.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/quickFix/CreateFileFix.java @@ -18,8 +18,6 @@ package com.intellij.codeInsight.daemon.quickFix; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.Result; -import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -96,20 +94,19 @@ public class CreateFileFix extends LocalQuickFixAndIntentionActionOnPsiElement { return CodeInsightBundle.message("create.file.family"); } + @Override + public boolean shouldMakeCurrentFileWritable() { + return false; + } + @Override public void invoke(@NotNull final Project project, @NotNull PsiFile file, Editor editor, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { - final PsiDirectory myDirectory = (PsiDirectory)startElement; if (isAvailable(project, null, file)) { - new WriteCommandAction(project) { - @Override - protected void run(@NotNull Result result) throws Throwable { - invoke(project, myDirectory); - } - }.execute(); + invoke(project, (PsiDirectory)startElement); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java index 9924badcc318..56dd5ef5f0a8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.intention.impl; import com.intellij.codeInsight.CodeInsightActionHandler; +import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass; @@ -35,7 +36,6 @@ import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; @@ -44,7 +44,6 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; -import com.intellij.util.IncorrectOperationException; import com.intellij.util.PairProcessor; import com.intellij.util.ThreeState; import org.jetbrains.annotations.NotNull; @@ -54,7 +53,6 @@ import org.jetbrains.annotations.Nullable; * @author mike */ public class ShowIntentionActionsHandler implements CodeInsightActionHandler { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler"); @Override public void invoke(@NotNull final Project project, @NotNull Editor editor, @NotNull PsiFile file) { @@ -178,28 +176,31 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler { FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.quickFix"); ((FeatureUsageTrackerImpl)FeatureUsageTracker.getInstance()).getFixesStats().registerInvocation(); - final Pair pair = chooseFileForAction(hostFile, hostEditor, action); + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + Pair pair = chooseFileForAction(hostFile, hostEditor, action); if (pair == null) return false; - CommandProcessor.getInstance().executeCommand(project, () -> TransactionGuard.getInstance().submitTransactionAndWait(() -> { - Runnable r = () -> action.invoke(project, pair.second, pair.first); - try { - if (action.startInWriteAction()) { - WriteAction.run(r::run); - } else { - r.run(); - } - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - if (hostEditor != null) { - DaemonCodeAnalyzer.getInstance(project).updateVisibleHighlighters(hostEditor); - } - }), text, null); + CommandProcessor.getInstance().executeCommand(project, () -> + TransactionGuard.getInstance().submitTransactionAndWait( + () -> invokeIntention(action, pair.second, pair.first)), text, null); return true; } + private static void invokeIntention(@NotNull IntentionAction action, @NotNull Editor editor, @NotNull PsiFile file) { + if (action.shouldMakeCurrentFileWritable() && + !FileModificationService.getInstance().preparePsiElementsForWrite(file)) { + return; + } + + Runnable r = () -> action.invoke(file.getProject(), editor, file); + if (action.startInWriteAction()) { + WriteAction.run(r::run); + } else { + r.run(); + } + } + static Pair chooseFileForAction(@NotNull PsiFile hostFile, @Nullable Editor hostEditor, diff --git a/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/SwitchToHtml5Action.java b/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/SwitchToHtml5Action.java index d62c4947c951..afe471c35f2b 100644 --- a/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/SwitchToHtml5Action.java +++ b/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/SwitchToHtml5Action.java @@ -65,6 +65,11 @@ public class SwitchToHtml5Action implements LocalQuickFix, IntentionAction { applyFix(project); } + @Override + public boolean shouldMakeCurrentFileWritable() { + return false; + } + private static void applyFix(Project project) { ExternalResourceManagerEx.getInstanceEx().setDefaultHtmlDoctype(Html5SchemaProvider.getHtml5SchemaLocation(), project); DaemonCodeAnalyzer.getInstance(project).restart();