[threading] IJPL-219144: Add flag for removing WI from input events

GitOrigin-RevId: 21a7fffeb69706345078b6002fb36a590572dbfc
This commit is contained in:
Konstantin Nisht
2025-11-19 20:20:06 +00:00
committed by intellij-monorepo-bot
parent 0cafcc0abd
commit b3cdfb87b0
4 changed files with 60 additions and 34 deletions
@@ -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
@@ -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) {
@@ -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) {
@@ -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
}