From b3cdfb87b07ffbcbd4f8962fa0c0857b81263c2d Mon Sep 17 00:00:00 2001 From: Konstantin Nisht Date: Mon, 6 Oct 2025 14:48:12 +0200 Subject: [PATCH] [threading] IJPL-219144: Add flag for removing WI from input events GitOrigin-RevId: 21a7fffeb69706345078b6002fb36a590572dbfc --- .../application/ThreadingRuntimeFlags.kt | 12 +++- .../src/com/intellij/ide/IdeEventQueue.kt | 16 ++++- .../openapi/editor/impl/EditorImpl.java | 4 +- .../keymap/impl/IdeKeyEventDispatcher.kt | 62 ++++++++++--------- 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/application/ThreadingRuntimeFlags.kt b/platform/core-api/src/com/intellij/openapi/application/ThreadingRuntimeFlags.kt index f68e4b4aaa31..c8128cb5b809 100644 --- a/platform/core-api/src/com/intellij/openapi/application/ThreadingRuntimeFlags.kt +++ b/platform/core-api/src/com/intellij/openapi/application/ThreadingRuntimeFlags.kt @@ -11,14 +11,22 @@ import org.jetbrains.annotations.ApiStatus val useBackgroundWriteAction: Boolean = System.getProperty("idea.background.write.action.enabled", "true").toBoolean() /** - * - `true` means some high-level Swing code will not use write-intent lock defensively for execution of user's code - * - `false` means that write-intent lock will be inserted in more places + * - `true` means some high-level Swing code will use write-intent lock defensively for execution of user's code + * - `false` means that write-intent lock will not be inserted there * * See IJPL-199557 */ @ApiStatus.Internal val wrapHighLevelFunctionsInWriteIntent: Boolean = System.getProperty("idea.wrap.high.level.functions.in.write.intent", "false").toBoolean() +/** + * - `true` means some high-level Swing code will use write-intent lock defensively for execution of input events + * - `false` means that write-intent lock will not be inserted there + */ +@ApiStatus.Internal +val wrapHighLevelInputEventsInWriteIntentLock: Boolean = System.getProperty("idea.wrap.high.level.input.events.in.write.intent", "true").toBoolean() + + /** * - `false` means that [backgroundWriteAction] will block the thread during lock acquisition * - `true` means that [backgroundWriteAction] will suspend during lock acquisition diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.kt b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.kt index ac0531b85482..617c6e1f6dd6 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.kt +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.kt @@ -505,8 +505,20 @@ class IdeEventQueue private constructor() : EventQueue() { } when { - e is MouseEvent -> threadingSupport.runPreventiveWriteIntentReadAction { dispatchMouseEvent(e) } - e is KeyEvent -> threadingSupport.runPreventiveWriteIntentReadAction { dispatchKeyEvent(e) } + e is MouseEvent -> if (wrapHighLevelInputEventsInWriteIntentLock) { + threadingSupport.runPreventiveWriteIntentReadAction { + dispatchMouseEvent(e) + } + } else { + dispatchMouseEvent(e) + } + e is KeyEvent -> if (wrapHighLevelInputEventsInWriteIntentLock) { + threadingSupport.runPreventiveWriteIntentReadAction { + dispatchKeyEvent(e) + } + } else { + dispatchKeyEvent(e) + } appIsLoaded() -> { val app = ApplicationManagerEx.getApplicationEx() if (e is ComponentEvent) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 6156244dd164..06d3e9805408 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -1498,7 +1498,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi @ApiStatus.Internal public void processKeyTypedNormally(char c, @NotNull DataContext dataContext) { EditorActionManager.getInstance(); - TypedAction.getInstance().actionPerformed(this, c, dataContext); + WriteIntentReadAction.run((Runnable) () -> { + TypedAction.getInstance().actionPerformed(this, c, dataContext); + }); } private void fireFocusLost(@NotNull FocusEvent event) { diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.kt b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.kt index 2be042560708..89fde382da73 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.kt +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.impl.Utils import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.TransactionGuard import com.intellij.openapi.application.TransactionGuardImpl +import com.intellij.openapi.application.WriteIntentReadAction import com.intellij.openapi.client.ClientSystemInfo import com.intellij.openapi.components.serviceIfCreated import com.intellij.openapi.diagnostic.debug @@ -537,40 +538,43 @@ class IdeKeyEventDispatcher(private val queue: IdeEventQueue?) { fireBeforeShortcutTriggered(shortcut, actions, context) - val chosen = Utils.runUpdateSessionForInputEvent( - actions, e, wrappedContext, place, processor, presentationFactory - ) { rearranged, updater, events -> - doUpdateActionsInner(rearranged, updater, events, dumb, wouldBeEnabledIfNotDumb) - } - val doPerform = chosen != null && !this@IdeKeyEventDispatcher.context.secondStrokeActions.contains(chosen.action) + val chosen = WriteIntentReadAction.compute(Computable { + val chosen = Utils.runUpdateSessionForInputEvent( + actions, e, wrappedContext, place, processor, presentationFactory + ) { rearranged, updater, events -> + doUpdateActionsInner(rearranged, updater, events, dumb, wouldBeEnabledIfNotDumb) + } + val doPerform = chosen != null && !this@IdeKeyEventDispatcher.context.secondStrokeActions.contains(chosen.action) - LOG.trace { "updateResult: chosen=$chosen, doPerform=$doPerform" } - val hasSecondStroke = chosen != null && this.context.secondStrokeActions.contains(chosen.action) - if (e.id == KeyEvent.KEY_PRESSED && !hasSecondStroke && (chosen != null || !wouldBeEnabledIfNotDumb.isEmpty())) { - ignoreNextKeyTypedEvent = true - } + LOG.trace { "updateResult: chosen=$chosen, doPerform=$doPerform" } + val hasSecondStroke = chosen != null && this.context.secondStrokeActions.contains(chosen.action) + if (e.id == KeyEvent.KEY_PRESSED && !hasSecondStroke && (chosen != null || !wouldBeEnabledIfNotDumb.isEmpty())) { + ignoreNextKeyTypedEvent = true + } - if (doPerform) { - doPerformActionInner(e, processor, chosen.action, chosen.event) - logTimeMillis(chosen.startedAt, chosen.action) - } - else if (hasSecondStroke) { - waitSecondStroke(chosen.action, chosen.event.presentation) - } - else if (!wouldBeEnabledIfNotDumb.isEmpty()) { - val actionManager = ActionManager.getInstance() - showDumbModeBalloonLater(project = project, - message = getActionUnavailableMessage(wouldBeEnabledIfNotDumb), - expired = { e.isConsumed }, - actionIds = actions.mapNotNull { action -> actionManager.getId(action) }) { - // invokeLater to make sure correct dataContext is taken from focus - ApplicationManager.getApplication().invokeLater { - DataManager.getInstance().dataContextFromFocusAsync.onSuccess { dataContext -> - processAction(e, place, dataContext, actions, processor, presentationFactory, shortcut) + if (doPerform) { + doPerformActionInner(e, processor, chosen.action, chosen.event) + logTimeMillis(chosen.startedAt, chosen.action) + } + else if (hasSecondStroke) { + waitSecondStroke(chosen.action, chosen.event.presentation) + } + else if (!wouldBeEnabledIfNotDumb.isEmpty()) { + val actionManager = ActionManager.getInstance() + showDumbModeBalloonLater(project = project, + message = getActionUnavailableMessage(wouldBeEnabledIfNotDumb), + expired = { e.isConsumed }, + actionIds = actions.mapNotNull { action -> actionManager.getId(action) }) { + // invokeLater to make sure correct dataContext is taken from focus + ApplicationManager.getApplication().invokeLater { + DataManager.getInstance().dataContextFromFocusAsync.onSuccess { dataContext -> + processAction(e, place, dataContext, actions, processor, presentationFactory, shortcut) + } } } } - } + chosen + }) return chosen != null }