diff --git a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java
index 54841737ca3f..7291d411e241 100644
--- a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java
+++ b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java
@@ -53,11 +53,12 @@ import org.jetbrains.annotations.NotNull;
* {@link #submitMergeableTransaction(TransactionKind, Runnable)} call.
*
* Q: I've got "Nested transactions are not allowed" exception, what do I do?
- * A: First, find the place in the stack where the outer transaction is started. Then, see if there is any Swing event pumping
- * in between two transactions (e.g. a dialog is shown). If not, one of two transactions is superfluous, remove it. If there
- * is event pumping, check if the client code (e.g. the one showing the dialog) is prepared to the nested model modifications
- * of the specified kinds. For example, refactoring dialogs might be prepared to {@link TransactionKind#TEXT_EDITING} kind
- * (for text field editing inside the dialogs) but not
+ * A: First, identify the place in the stack where the outer transaction is started (the exception attachment should contain it).
+ * Then, see if there is any Swing event pumping
+ * in between two transactions (e.g. a dialog is shown). If not, one of the two involved transactions is superfluous, remove it. If there
+ * is event pumping present, check if the client code (e.g. the one showing the dialog) is prepared to such nested model modifications.
+ * For example, refactoring dialogs might be prepared to {@link TransactionKind#TEXT_EDITING} kind
+ * (for text field editing inside the dialogs) but not to
* other model changes, e.g. root changes. The outer transaction code might then specify which kinds it's prepared to (by using
* {@link #acceptNestedTransactions(TransactionKind...)}), and the inner transaction code should have the very same transaction kind
* (by using {@link #submitMergeableTransaction(TransactionKind, Runnable)} or {@link #startSynchronousTransaction(TransactionKind)}).
diff --git a/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java b/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java
index 711dc54680e4..b42f547b326f 100644
--- a/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java
+++ b/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java
@@ -3,7 +3,7 @@ package com.intellij.openapi.application;
import java.lang.annotation.*;
/**
- * Add this annotation to actions (AnAction inheritors) to make them run inside a transaction.
+ * Add this annotation to actions (AnAction inheritors) to make them run inside a synchronous transaction.
*
* @see TransactionGuard
* @since 146.*
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 3d627e9956f0..70a54e4886a0 100644
--- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java
+++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java
@@ -15,8 +15,10 @@
*/
package com.intellij.openapi.application;
+import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.psi.impl.DebugUtil;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -33,7 +35,7 @@ public class TransactionGuardImpl extends TransactionGuard {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.application.TransactionGuardImpl");
private final Queue myQueue = new LinkedBlockingQueue();
private final Set myMergeableKinds = ContainerUtil.newHashSet();
- private boolean myInsideTransaction;
+ private String myTransactionStartTrace;
@Override
@NotNull
@@ -42,15 +44,16 @@ public class TransactionGuardImpl extends TransactionGuard {
if (kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) {
return AccessToken.EMPTY_ACCESS_TOKEN;
}
- if (myInsideTransaction) {
- LOG.error("Nested transactions are not allowed");
+ if (myTransactionStartTrace != null) {
+ LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment.",
+ new Attachment("trace.txt", myTransactionStartTrace));
//throw new IllegalStateException("Nested transactions are not allowed");
}
- myInsideTransaction = true;
+ myTransactionStartTrace = DebugUtil.currentStackTrace();
return new AccessToken() {
@Override
public void finish() {
- myInsideTransaction = false;
+ myTransactionStartTrace = null;
if (!myQueue.isEmpty()) {
pollQueueLater();
}
@@ -64,7 +67,7 @@ public class TransactionGuardImpl extends TransactionGuard {
app.invokeLater(new Runnable() {
@Override
public void run() {
- if (myInsideTransaction) return;
+ if (isInsideTransaction()) return;
Runnable next = myQueue.poll();
if (next != null) {
@@ -87,7 +90,7 @@ public class TransactionGuardImpl extends TransactionGuard {
@Override
public boolean isInsideTransaction() {
ApplicationManager.getApplication().assertIsDispatchThread();
- return myInsideTransaction;
+ return myTransactionStartTrace != null;
}
@Override
@@ -95,7 +98,7 @@ public class TransactionGuardImpl extends TransactionGuard {
Runnable runnable = new Runnable() {
@Override
public void run() {
- if (!myInsideTransaction || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) {
+ if (!isInsideTransaction() || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) {
runSyncTransaction(kind, transaction);
}
else {