mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -19,6 +19,7 @@ import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.util.ThrowableRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
* and process UI events in other ways: it's guaranteed that no one will be able to sneak in with an unexpected model change using
|
||||
* {@link javax.swing.SwingUtilities#invokeLater(Runnable)} or analogs.<p/>
|
||||
*
|
||||
* Transactions are run on UI thread. They have read access by default. All write actions should be performed inside a transaction.<p/>
|
||||
* Transactions are run on UI thread. They have read access by default. All write actions that don't happen as the result of direct user input
|
||||
* should be performed inside a transaction. No write action should be performed from within an {@code invokeLater}-like call
|
||||
* unless wrapped into a transaction.<p/>
|
||||
*
|
||||
* The recommended way to perform a transaction is to invoke {@link #submitTransaction(Runnable)}. It either runs the transaction immediately
|
||||
* (if on UI thread and there's no other transaction running) or queues it to invoke at some later moment, when it becomes possible.<p/>
|
||||
@@ -192,4 +195,13 @@ public abstract class TransactionGuard {
|
||||
* @param errorMessage the message that will be logged if current transaction status differs from the expected one
|
||||
*/
|
||||
public abstract void assertInsideTransaction(boolean transactionRequired, @NotNull String errorMessage);
|
||||
|
||||
/**
|
||||
* Executes the given code and marks it as a user activity, to allow write actions to be run without requiring transactions.
|
||||
* Please note that only direct keyboard/mouse event processing can be wrapped in such calls. If you wish to invoke some actionPerformed,
|
||||
* please consider using {@code ActionManager.tryToExecute()} instead, or ensure in some other way that the action is enabled
|
||||
* and can be invoked in the current modality state.
|
||||
*/
|
||||
public abstract <T extends Throwable> void performUserActivity(ThrowableRunnable<T> activity) throws T;
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ 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.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.impl.DebugUtil;
|
||||
import com.intellij.util.ThrowableRunnable;
|
||||
import com.intellij.util.concurrency.Semaphore;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -37,13 +39,14 @@ public class TransactionGuardImpl extends TransactionGuard {
|
||||
private final Set<TransactionKind> myMergeableKinds = ContainerUtil.newHashSet();
|
||||
private String myTransactionStartTrace;
|
||||
private ModalityState myTransactionModality;
|
||||
private boolean myUserActivity;
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public AccessToken startSynchronousTransaction(@NotNull TransactionKind kind) throws IllegalStateException {
|
||||
ModalityState modality = ModalityState.current();
|
||||
if (isInsideTransaction()) {
|
||||
if (modality.equals(myTransactionModality)) {
|
||||
if (modality.equals(myTransactionModality) || myUserActivity) {
|
||||
return AccessToken.EMPTY_ACCESS_TOKEN;
|
||||
}
|
||||
|
||||
@@ -200,4 +203,39 @@ public class TransactionGuardImpl extends TransactionGuard {
|
||||
throw new RuntimeException(exception[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Throwable> void performUserActivity(ThrowableRunnable<T> activity) throws T {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
AccessToken token = startActivity(true);
|
||||
try {
|
||||
activity.run();
|
||||
}
|
||||
finally {
|
||||
token.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An absolutely guru method, only intended to be used from Swing event processing. Please consult Peter if you think you need to invoke this.
|
||||
*/
|
||||
@NotNull
|
||||
public AccessToken startActivity(boolean userActivity) {
|
||||
if (myUserActivity == userActivity) {
|
||||
return AccessToken.EMPTY_ACCESS_TOKEN;
|
||||
}
|
||||
|
||||
final boolean prev = myUserActivity;
|
||||
myUserActivity = userActivity;
|
||||
return new AccessToken() {
|
||||
@Override
|
||||
public void finish() {
|
||||
myUserActivity = prev;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean isWriteActionAllowed() {
|
||||
return !Registry.is("ide.require.transaction.for.model.changes", false) || isInsideTransaction() || myUserActivity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.intellij.openapi.actionSystem.ex.ActionUtil;
|
||||
import com.intellij.openapi.actionSystem.ex.QuickList;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.TransactionGuard;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.keymap.Keymap;
|
||||
import com.intellij.openapi.keymap.KeymapManager;
|
||||
@@ -250,10 +251,9 @@ public class GotoActionAction extends GotoActionBase implements DumbAware {
|
||||
public static void performAction(Object element, @Nullable final Component component, @Nullable final AnActionEvent e) {
|
||||
// element could be AnAction (SearchEverywhere)
|
||||
final AnAction action = element instanceof AnAction ? (AnAction)element : ((GotoActionModel.ActionWrapper)element).getAction();
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (component == null) return;
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (component == null) return;
|
||||
TransactionGuard.getInstance().performUserActivity(() -> {
|
||||
DataManager instance = DataManager.getInstance();
|
||||
DataContext context = instance != null ? instance.getDataContext(component) : DataContext.EMPTY_CONTEXT;
|
||||
InputEvent inputEvent = e == null ? null : e.getInputEvent();
|
||||
@@ -270,12 +270,12 @@ public class GotoActionAction extends GotoActionBase implements DumbAware {
|
||||
else {
|
||||
popup.showInFocusCenter();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ActionUtil.performActionDumbAware(action, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, ModalityState.NON_MODAL);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@ import com.intellij.ide.plugins.PluginManager;
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.idea.IdeaApplication;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.*;
|
||||
import com.intellij.openapi.application.ex.ApplicationEx;
|
||||
import com.intellij.openapi.application.impl.LaterInvocator;
|
||||
import com.intellij.openapi.diagnostic.FrequentEventDetector;
|
||||
@@ -385,6 +383,7 @@ public class IdeEventQueue extends EventQueue {
|
||||
}
|
||||
AWTEvent oldEvent = myCurrentEvent;
|
||||
myCurrentEvent = e;
|
||||
AccessToken token = ((TransactionGuardImpl)TransactionGuard.getInstance()).startActivity(myIsInInputEvent);
|
||||
|
||||
try {
|
||||
_dispatchEvent(e, false);
|
||||
@@ -393,6 +392,7 @@ public class IdeEventQueue extends EventQueue {
|
||||
processException(t);
|
||||
}
|
||||
finally {
|
||||
token.finish();
|
||||
myIsInInputEvent = wasInputEvent;
|
||||
myCurrentEvent = oldEvent;
|
||||
|
||||
|
||||
+5
-9
@@ -29,10 +29,7 @@ import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil;
|
||||
import com.intellij.openapi.actionSystem.ex.AnActionListener;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationActivationListener;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.*;
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
@@ -1298,15 +1295,14 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
|
||||
private void tryToExecuteNow(final AnAction action, final InputEvent inputEvent, final Component contextComponent, final String place, final ActionCallback result) {
|
||||
final Presentation presentation = action.getTemplatePresentation().clone();
|
||||
|
||||
IdeFocusManager.findInstanceByContext(getContextBy(contextComponent)).doWhenFocusSettlesDown(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
IdeFocusManager.findInstanceByContext(getContextBy(contextComponent)).doWhenFocusSettlesDown(
|
||||
() -> TransactionGuard.getInstance().performUserActivity(() -> {
|
||||
final DataContext context = getContextBy(contextComponent);
|
||||
|
||||
AnActionEvent event = new AnActionEvent(
|
||||
inputEvent, context,
|
||||
place != null ? place : ActionPlaces.UNKNOWN,
|
||||
presentation, ActionManagerImpl.this,
|
||||
presentation, this,
|
||||
inputEvent.getModifiersEx()
|
||||
);
|
||||
|
||||
@@ -1346,7 +1342,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
|
||||
result.setDone();
|
||||
queueActionPerformedEvent(action, context, event);
|
||||
}
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
private class MyTimer extends Timer implements ActionListener {
|
||||
|
||||
+3
-3
@@ -1229,9 +1229,9 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
private void startWrite(/*@NotNull*/ Class clazz) {
|
||||
assertIsDispatchThread(getStatus(), "Write access is allowed from event dispatch thread only");
|
||||
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing(); // let non-cancellable read actions complete faster, if present
|
||||
if (Registry.is("ide.require.transaction.for.model.changes", false)) {
|
||||
TransactionGuard.getInstance().assertInsideTransaction(
|
||||
true, "Write access is allowed from model transactions only, see TransactionGuard documentation for details");
|
||||
if (!((TransactionGuardImpl)TransactionGuard.getInstance()).isWriteActionAllowed()) {
|
||||
// please assign exceptions here to Peter
|
||||
LOG.error("Write access is allowed from model transactions only, see TransactionGuard documentation for details");
|
||||
}
|
||||
boolean writeActionPending = myWriteActionPending;
|
||||
if (gatherWriteActionStatistics && myWriteActionsStack.isEmpty() && !writeActionPending) {
|
||||
|
||||
+3
-5
@@ -868,17 +868,15 @@ public final class IdeKeyEventDispatcher implements Disposable {
|
||||
}
|
||||
|
||||
private static void invokeAction(@NotNull final AnAction action, final DataContext ctx) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> TransactionGuard.getInstance().performUserActivity(() -> {
|
||||
final AnActionEvent event =
|
||||
new AnActionEvent(null, ctx, ActionPlaces.UNKNOWN, action.getTemplatePresentation().clone(),
|
||||
ActionManager.getInstance(), 0);
|
||||
if (ActionUtil.lastUpdateAndCheckDumb(action, event, true)) {
|
||||
ActionUtil.performActionDumbAware(action, event);
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.DataProvider;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.TransactionGuard;
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
@@ -1392,7 +1393,7 @@ public class AbstractPopup implements JBPopup {
|
||||
//noinspection SSBasedInspection
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (ModalityState.current().equals(modalityState)) {
|
||||
finalRunnable.run();
|
||||
TransactionGuard.getInstance().performUserActivity(finalRunnable::run);
|
||||
}
|
||||
// Otherwise the UI has changed unexpectedly and the action is likely not applicable.
|
||||
// And we don't want finalRunnable to perform potentially destructive actions
|
||||
|
||||
Reference in New Issue
Block a user