diff --git a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java index 2c40b8549527..2ba93aaca322 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -36,24 +36,40 @@ public class TransactionGuardImpl extends TransactionGuard { private final Queue myQueue = new LinkedBlockingQueue(); private final Set myMergeableKinds = ContainerUtil.newHashSet(); private String myTransactionStartTrace; + private ModalityState myTransactionModality; @Override @NotNull public AccessToken startSynchronousTransaction(@NotNull TransactionKind kind) throws IllegalStateException { - ApplicationManager.getApplication().assertIsDispatchThread(); - if (myTransactionStartTrace != null) { - if (!myMergeableKinds.contains(kind) && !ApplicationManager.getApplication().isUnitTestMode()) { - // please assign exceptions that occur here to Peter - LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment. Kind is " + kind, - new Attachment("trace.txt", myTransactionStartTrace)); + ModalityState modality = ModalityState.current(); + if (isInsideTransaction()) { + if (myTransactionModality == modality) { + return AccessToken.EMPTY_ACCESS_TOKEN; } + + if (myMergeableKinds.contains(kind)) { + final ModalityState prev = myTransactionModality; + myTransactionModality = modality; + return new AccessToken() { + @Override + public void finish() { + myTransactionModality = prev; + } + }; + } + + // please assign exceptions that occur here to Peter + LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment. Kind is " + kind, + new Attachment("trace.txt", myTransactionStartTrace)); return AccessToken.EMPTY_ACCESS_TOKEN; } + myTransactionModality = modality; myTransactionStartTrace = DebugUtil.currentStackTrace(); return new AccessToken() { @Override public void finish() { myTransactionStartTrace = null; + myTransactionModality = null; if (!myQueue.isEmpty()) { pollQueueLater(); } @@ -148,7 +164,7 @@ public class TransactionGuardImpl extends TransactionGuard { public void submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull final Runnable transaction) throws ProcessCanceledException { Application app = ApplicationManager.getApplication(); if (app.isDispatchThread()) { - if (!canRunTransactionNow(kind)) { + if (!canRunTransactionNow(kind) && myTransactionModality != ModalityState.current()) { throw new AssertionError("Cannot run submitTransactionAndWait from another transaction, kind " + kind + " is not allowed"); } runSyncTransaction(kind, transaction); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EndHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EndHandler.java index d43e47dfb3cb..72a64b2b49c4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EndHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EndHandler.java @@ -94,9 +94,7 @@ public class EndHandler extends EditorActionHandler { // here just as a boolean value holder due to requirement to declare variable used from inner class as final. final AtomicBoolean stopProcessing = new AtomicBoolean(true); - TransactionGuard guard = TransactionGuard.getInstance(); - // sometimes this handler is invoked from other actions, then we're already inside a transaction - try (AccessToken ignore = guard.isInsideTransaction() ? null : guard.startSynchronousTransaction(TransactionKind.TEXT_EDITING)) { + try (AccessToken ignore = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.TEXT_EDITING)) { PsiDocumentManager.getInstance(project).commitAllDocuments(); ApplicationManager.getApplication().runWriteAction(() -> { CodeStyleManager styleManager = CodeStyleManager.getInstance(project);