IJPL-158385 ToolboxUpdateActions - use coroutine scope, avoid Disposable

GitOrigin-RevId: a2e45785e0324d654c6f9747816ae2609d8fc171
This commit is contained in:
Vladimir Krivosheev
2024-07-17 16:01:08 +02:00
committed by intellij-monorepo-bot
parent cfaad7d6fe
commit 8fa4a1fc9a
5 changed files with 96 additions and 26 deletions

View File

@@ -283,9 +283,7 @@ f:org.jetbrains.ide.ToolboxIdeExitHandler$ExitParameters
f:org.jetbrains.ide.ToolboxRestServiceKt
- sf:getToolboxHandlerEP():com.intellij.openapi.extensions.ExtensionPointName
f:org.jetbrains.ide.ToolboxSettingsActionRegistry
- com.intellij.openapi.Disposable
- <init>():V
- dispose():V
- <init>(kotlinx.coroutines.CoroutineScope):V
- f:getActions():java.util.List
- f:isNewAction(java.lang.String):Z
- f:markAsRead(java.lang.String):V

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.ide
import com.intellij.ide.actions.SettingsEntryPointAction
@@ -7,24 +7,38 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.EDT
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.util.Disposer
import com.intellij.util.Alarm
import com.intellij.util.messages.Topic
import com.intellij.util.ui.update.MergingUpdateQueue
import com.intellij.util.ui.update.Update
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import org.jetbrains.annotations.Nls
import java.util.*
@OptIn(FlowPreview::class)
@Service(Service.Level.APP)
class ToolboxSettingsActionRegistry : Disposable {
class ToolboxSettingsActionRegistry(coroutineScope: CoroutineScope) {
private val readActions = Collections.synchronizedSet(HashSet<String>())
private val pendingActions = Collections.synchronizedList(LinkedList<ToolboxUpdateAction>())
private val alarm = MergingUpdateQueue("toolbox-updates", 500, true, null, this, null, Alarm.ThreadToUse.SWING_THREAD).usePassThroughInUnitTestMode()
private val updateRequests = MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override fun dispose() = Unit
init {
coroutineScope.launch {
updateRequests
.debounce(500)
.collectLatest {
withContext(Dispatchers.EDT) {
SettingsEntryPointAction.updateState()
}
}
}
}
fun isNewAction(actionId: String) = actionId !in readActions
@@ -33,11 +47,12 @@ class ToolboxSettingsActionRegistry : Disposable {
}
fun scheduleUpdate() {
alarm.queue(object: Update(this){
override fun run() {
SettingsEntryPointAction.updateState()
}
})
if (ApplicationManager.getApplication().isUnitTestMode) {
SettingsEntryPointAction.updateState()
}
else {
check(updateRequests.tryEmit(Unit))
}
}
internal fun registerUpdateAction(action: ToolboxUpdateAction) {

View File

@@ -3196,6 +3196,8 @@ c:com.intellij.util.ui.update.MergingUpdateQueue
- <init>(java.lang.String,I,Z,javax.swing.JComponent,com.intellij.openapi.Disposable):V
- <init>(java.lang.String,I,Z,javax.swing.JComponent,com.intellij.openapi.Disposable,javax.swing.JComponent):V
- <init>(java.lang.String,I,Z,javax.swing.JComponent,com.intellij.openapi.Disposable,javax.swing.JComponent,com.intellij.util.Alarm$ThreadToUse):V
- <init>(java.lang.String,I,Z,javax.swing.JComponent,com.intellij.openapi.Disposable,javax.swing.JComponent,com.intellij.util.Alarm$ThreadToUse,kotlinx.coroutines.CoroutineScope):V
- b:<init>(java.lang.String,I,Z,javax.swing.JComponent,com.intellij.openapi.Disposable,javax.swing.JComponent,com.intellij.util.Alarm$ThreadToUse,kotlinx.coroutines.CoroutineScope,I,kotlin.jvm.internal.DefaultConstructorMarker):V
- <init>(java.lang.String,I,Z,javax.swing.JComponent,com.intellij.openapi.Disposable,javax.swing.JComponent,Z):V
- b:<init>(java.lang.String,I,Z,javax.swing.JComponent,com.intellij.openapi.Disposable,javax.swing.JComponent,Z,I,kotlin.jvm.internal.DefaultConstructorMarker):V
- f:activate():V

View File

@@ -18,7 +18,7 @@ import com.intellij.util.Alarm
import com.intellij.util.SystemProperties
import com.intellij.util.ui.EdtInvocationManager
import com.intellij.util.ui.update.UiNotifyConnector.Companion.installOn
import org.jetbrains.annotations.ApiStatus
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.annotations.ApiStatus.Internal
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.TestOnly
@@ -50,14 +50,15 @@ import kotlin.concurrent.Volatile
* @param activationComponent if not `null` the tasks will be processing only when the given component is showing
* @param thread specifies on which thread the tasks are executed
*/
open class MergingUpdateQueue(
open class MergingUpdateQueue @JvmOverloads constructor(
private val name: @NonNls String,
private var mergingTimeSpan: Int,
isActive: Boolean,
private var modalityStateComponent: JComponent?,
parent: Disposable?,
activationComponent: JComponent?,
thread: Alarm.ThreadToUse
thread: Alarm.ThreadToUse,
coroutineScope: CoroutineScope? = null,
) : Disposable, Activatable {
@Volatile
var isSuspended: Boolean = false
@@ -111,6 +112,7 @@ open class MergingUpdateQueue(
parent = parent,
activationComponent = activationComponent,
thread = if (executeInDispatchThread) Alarm.ThreadToUse.SWING_THREAD else Alarm.ThreadToUse.POOLED_THREAD,
coroutineScope = null,
)
init {
@@ -119,7 +121,17 @@ open class MergingUpdateQueue(
Disposer.register(parent, this)
}
waiterForMerge = if (executeInDispatchThread) Alarm(threadToUse = thread) else Alarm(threadToUse = thread, parentDisposable = this)
waiterForMerge = if (coroutineScope == null) {
if (executeInDispatchThread) {
Alarm(threadToUse = thread)
}
else {
Alarm(threadToUse = thread, parentDisposable = this)
}
}
else {
Alarm(threadToUse = thread, coroutineScope = coroutineScope)
}
if (isActive) {
showNotify()
@@ -139,6 +151,42 @@ open class MergingUpdateQueue(
@JvmField
val ANY_COMPONENT: JComponent = object : JComponent() {}
@Internal
fun edtMergingUpdateQueue(
name: String,
mergingTimeSpan: Int,
coroutineScope: CoroutineScope,
): MergingUpdateQueue {
return MergingUpdateQueue(
name = name,
mergingTimeSpan = mergingTimeSpan,
isActive = true,
modalityStateComponent = null,
parent = null,
activationComponent = null,
thread = Alarm.ThreadToUse.SWING_THREAD,
coroutineScope = coroutineScope,
)
}
@Internal
fun mergingUpdateQueue(
name: String,
mergingTimeSpan: Int,
coroutineScope: CoroutineScope,
): MergingUpdateQueue {
return MergingUpdateQueue(
name = name,
mergingTimeSpan = mergingTimeSpan,
isActive = true,
modalityStateComponent = null,
parent = null,
activationComponent = null,
thread = Alarm.ThreadToUse.POOLED_THREAD,
coroutineScope = coroutineScope,
)
}
private val queues: MutableSet<MergingUpdateQueue>? = if (SystemProperties.getBooleanProperty("intellij.MergingUpdateQueue.enable.global.flusher", false)) {
ConcurrentCollectionFactory.createConcurrentSet()
}
@@ -146,7 +194,7 @@ open class MergingUpdateQueue(
null
}
@ApiStatus.Internal
@Internal
fun flushAllQueues() {
if (queues != null) {
for (queue in queues) {
@@ -185,7 +233,7 @@ open class MergingUpdateQueue(
* It is needed to support some old tests, which expect such behaviour.
* @return this instance for the sequential creation (the Builder pattern)
*/
@ApiStatus.Internal
@Internal
@Deprecated(
"""use {@link #waitForAllExecuted(long, TimeUnit)} instead in tests
""")
@@ -240,8 +288,8 @@ open class MergingUpdateQueue(
restart(mergingTimeSpan)
}
@ApiStatus.Internal
open protected fun getFlushTask(): Runnable = flushTask
@Internal
protected open fun getFlushTask(): Runnable = flushTask
private fun restart(mergingTimeSpanMillis: Int) {
if (!isActive) {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.wm.impl.welcomeScreen.recentProjects
import com.intellij.ide.RecentProjectsManager
@@ -61,8 +61,15 @@ internal object RecentProjectPanelComponentFactory {
}
})
val updateQueue = MergingUpdateQueue("Welcome screen UI updater", UPDATE_INTERVAL, true, null,
parentDisposable, tree, Alarm.ThreadToUse.SWING_THREAD)
val updateQueue = MergingUpdateQueue(
name = "Welcome screen UI updater",
mergingTimeSpan = UPDATE_INTERVAL,
isActive = true,
modalityStateComponent = null,
parent = parentDisposable,
activationComponent = tree,
thread = Alarm.ThreadToUse.SWING_THREAD,
)
updateQueue.queue(Update.create(filteringTree, Runnable { repaintProgressBars(updateQueue, filteringTree) }))
return filteringTree