From 35f17aa0348af19cb143f944433f6ed85d89b378 Mon Sep 17 00:00:00 2001 From: Kate Botsman Date: Mon, 19 Aug 2024 16:48:43 +0200 Subject: [PATCH] RDCT-1444 Add `TaskInfoEntity` This entity is going to represent running progress in Rhizome DB. The data is mostly copied from `PlatformTaskSupport.ProgressStartedEvent` structure. Few differences here: - No `CoroutineContext` in the entity It's not possible to put the actual coroutine context to `SharedEntity` because it's not serializable. The context is used mostly for cancellation of a job. The possibility to cancel a job will be provided through a different API. - New `TaskStatus` field The status represents the actual status of a task, whether it's running, paused or canceled. Also: - Added utils which should make working with `TasksInfoEntity` easier The utils are queries, which allow avoiding writing the same `asQuery().getOne` in every place where tasks info needs to be retrieved. - Marked classes used in `TaskInfoEntity` as `@Serializable` All data used in `SharedEntities` have to be serializable, otherwise it won't be possible to put entity to shared DB - Add `kotlinx/serialization/KSerializer` to `ideaProjectStructure/exposed-third-party` With Rhizome `KSerializer` is going to be exposed in more places. Therefore, it makes sense to add this class to default `exposed-third-party-api`, so the tests won't fail because of the added `@Serializable` annotation. GitOrigin-RevId: 1a1b979b7660d41879a40a4d28b6487b3205e743 --- platform/ide-core/exposed-third-party-api.txt | 1 - .../platform-impl/exposed-third-party-api.txt | 1 - platform/progress/shared/api-dump.txt | 7 ++ .../shared/intellij.platform.ide.progress.iml | 24 ++++++ .../shared/src/CancellableTaskCancellation.kt | 2 + .../src/NonCancellableTaskCancellation.kt | 2 + .../progress/shared/src/TaskCancellation.kt | 4 + platform/progress/shared/src/TaskStatus.kt | 34 +++++++++ platform/progress/shared/src/entities.kt | 74 +++++++++++++++++++ platform/progress/shared/src/utils.kt | 57 ++++++++++++++ platform/util/progress/api-dump.txt | 3 + .../intellij.platform.util.progress.iml | 8 ++ platform/util/progress/src/ProgressState.kt | 2 + platform/util/progress/src/StepState.kt | 2 + platform/util/progress/src/impl/impl.kt | 2 +- 15 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 platform/progress/shared/src/TaskStatus.kt create mode 100644 platform/progress/shared/src/entities.kt create mode 100644 platform/progress/shared/src/utils.kt diff --git a/platform/ide-core/exposed-third-party-api.txt b/platform/ide-core/exposed-third-party-api.txt index 8eaf57aa443d..cc731d91b6e0 100644 --- a/platform/ide-core/exposed-third-party-api.txt +++ b/platform/ide-core/exposed-third-party-api.txt @@ -1,5 +1,4 @@ kotlin/jvm/internal/DefaultConstructorMarker -kotlinx/serialization/KSerializer kotlinx/serialization/descriptors/SerialDescriptor kotlinx/serialization/encoding/Decoder kotlinx/serialization/encoding/Encoder diff --git a/platform/platform-impl/exposed-third-party-api.txt b/platform/platform-impl/exposed-third-party-api.txt index c056eb0b4feb..89c490059ce5 100644 --- a/platform/platform-impl/exposed-third-party-api.txt +++ b/platform/platform-impl/exposed-third-party-api.txt @@ -37,7 +37,6 @@ javax/xml/stream/XMLStreamReader kotlin/jvm/internal/DefaultConstructorMarker kotlin/reflect/KMutableProperty0 kotlin/reflect/KProperty -kotlinx/serialization/KSerializer kotlinx/serialization/descriptors/SerialDescriptor kotlinx/serialization/encoding/Decoder kotlinx/serialization/encoding/Encoder diff --git a/platform/progress/shared/api-dump.txt b/platform/progress/shared/api-dump.txt index b1b3054d34cb..be0b934ea7d3 100644 --- a/platform/progress/shared/api-dump.txt +++ b/platform/progress/shared/api-dump.txt @@ -8,13 +8,20 @@ com.intellij.platform.ide.progress.TaskCancellation - s:nonCancellable():com.intellij.platform.ide.progress.TaskCancellation$NonCancellable com.intellij.platform.ide.progress.TaskCancellation$Cancellable - com.intellij.platform.ide.progress.TaskCancellation +- sf:Companion:com.intellij.platform.ide.progress.TaskCancellation$Cancellable$Companion - a:withButtonText(java.lang.String):com.intellij.platform.ide.progress.TaskCancellation$Cancellable - a:withTooltipText(java.lang.String):com.intellij.platform.ide.progress.TaskCancellation$Cancellable +f:com.intellij.platform.ide.progress.TaskCancellation$Cancellable$Companion +- f:serializer():kotlinx.serialization.KSerializer f:com.intellij.platform.ide.progress.TaskCancellation$Companion - f:cancellable():com.intellij.platform.ide.progress.TaskCancellation$Cancellable - f:nonCancellable():com.intellij.platform.ide.progress.TaskCancellation$NonCancellable +- f:serializer():kotlinx.serialization.KSerializer com.intellij.platform.ide.progress.TaskCancellation$NonCancellable - com.intellij.platform.ide.progress.TaskCancellation +- sf:Companion:com.intellij.platform.ide.progress.TaskCancellation$NonCancellable$Companion +f:com.intellij.platform.ide.progress.TaskCancellation$NonCancellable$Companion +- f:serializer():kotlinx.serialization.KSerializer f:com.intellij.platform.ide.progress.TasksKt - sf:runWithModalProgressBlocking(com.intellij.openapi.project.Project,java.lang.String,kotlin.jvm.functions.Function2):java.lang.Object - sf:runWithModalProgressBlocking(com.intellij.platform.ide.progress.ModalTaskOwner,java.lang.String,com.intellij.platform.ide.progress.TaskCancellation,kotlin.jvm.functions.Function2):java.lang.Object diff --git a/platform/progress/shared/intellij.platform.ide.progress.iml b/platform/progress/shared/intellij.platform.ide.progress.iml index 5434954b0a24..b4774e9ca4b4 100644 --- a/platform/progress/shared/intellij.platform.ide.progress.iml +++ b/platform/progress/shared/intellij.platform.ide.progress.iml @@ -1,5 +1,26 @@ + + + + + + + + + + + + + + $KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar + + + + + + @@ -13,5 +34,8 @@ + + + \ No newline at end of file diff --git a/platform/progress/shared/src/CancellableTaskCancellation.kt b/platform/progress/shared/src/CancellableTaskCancellation.kt index e8fb62887538..bf8de089f257 100644 --- a/platform/progress/shared/src/CancellableTaskCancellation.kt +++ b/platform/progress/shared/src/CancellableTaskCancellation.kt @@ -2,9 +2,11 @@ package com.intellij.platform.ide.progress import com.intellij.openapi.util.NlsContexts +import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus @ApiStatus.Internal +@Serializable class CancellableTaskCancellation private constructor( val buttonText: @NlsContexts.Button String?, val tooltipText: @NlsContexts.Tooltip String? diff --git a/platform/progress/shared/src/NonCancellableTaskCancellation.kt b/platform/progress/shared/src/NonCancellableTaskCancellation.kt index 3c0ee71e1f81..e3a421513bae 100644 --- a/platform/progress/shared/src/NonCancellableTaskCancellation.kt +++ b/platform/progress/shared/src/NonCancellableTaskCancellation.kt @@ -1,7 +1,9 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.platform.ide.progress +import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus @ApiStatus.Internal +@Serializable data object NonCancellableTaskCancellation: TaskCancellation.NonCancellable diff --git a/platform/progress/shared/src/TaskCancellation.kt b/platform/progress/shared/src/TaskCancellation.kt index bd9f7da945a8..a6e458209bd1 100644 --- a/platform/progress/shared/src/TaskCancellation.kt +++ b/platform/progress/shared/src/TaskCancellation.kt @@ -2,12 +2,16 @@ package com.intellij.platform.ide.progress import com.intellij.openapi.util.NlsContexts +import kotlinx.serialization.Serializable import org.jetbrains.annotations.Contract +@Serializable sealed interface TaskCancellation { + @Serializable sealed interface NonCancellable : TaskCancellation + @Serializable sealed interface Cancellable : TaskCancellation { @Contract(value = "_ -> new", pure = true) fun withButtonText(buttonText: @NlsContexts.Button String): Cancellable diff --git a/platform/progress/shared/src/TaskStatus.kt b/platform/progress/shared/src/TaskStatus.kt new file mode 100644 index 000000000000..4d3c9feb5c41 --- /dev/null +++ b/platform/progress/shared/src/TaskStatus.kt @@ -0,0 +1,34 @@ +// 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.platform.ide.progress + +import kotlinx.serialization.Serializable +import org.jetbrains.annotations.ApiStatus + +/** + * Represents the status of a task + */ +@ApiStatus.Internal +@Serializable +enum class TaskStatus { + /** + * Indicates that a task is currently in progress. + * The task can be suspended ([PAUSED]), canceled ([CANCELED]) or finish without status change + */ + RUNNING, + + /** + * Indicates that a task has been temporarily paused. + * + * A task in this state may be resumed ([RUNNING]) or canceled ([CANCELED]). + * It implies that the task has been running before suspension. + */ + PAUSED, + + /** + * Indicates that a task has been requested to stop. + * + * A canceled task does not change its status anymore. + * Although the status is [CANCELED], the task might still be running until it can be safely aborted. + */ + CANCELED +} diff --git a/platform/progress/shared/src/entities.kt b/platform/progress/shared/src/entities.kt new file mode 100644 index 000000000000..ce2d967a7440 --- /dev/null +++ b/platform/progress/shared/src/entities.kt @@ -0,0 +1,74 @@ +// 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.platform.ide.progress + +import com.intellij.platform.util.progress.ProgressState +import com.jetbrains.rhizomedb.EID +import com.jetbrains.rhizomedb.Entity +import fleet.kernel.DurableEntityType +import kotlinx.serialization.builtins.serializer +import org.jetbrains.annotations.ApiStatus + +/** + * Represents the information of a task in the system. + */ +@ApiStatus.Internal +data class TaskInfoEntity(override val eid: EID) : Entity { + /** + * Human-readable title of a task, which is used to display the task in UI + */ + val title: String by Title + + /** + * Specifies whether the task can be canceled. + * + * Possible values are: + * + * - [TaskCancellation.NonCancellable]: The task cannot be canceled, and no cancel button should be displayed in the UI. + * - [TaskCancellation.Cancellable]: The task can be canceled, and an optional cancel button can be displayed with + * customizable button and tooltip text. + */ + val cancellation: TaskCancellation by TaskCancellationType + + /** + * Represents the current progress state of the task. + * + * This state includes: + * - [ProgressState.fraction]: The fraction of the task that has been completed, where `1.0` means 100% complete. + * - [ProgressState.text]: The primary text associated with the progress state (e.g., a brief description or title). + * - [ProgressState.details]: The detailed text associated with the progress state (e.g., additional information or steps). + * + * To subscribe on the progress updates use [updates] + * Initially, the progress state may be `null`. + */ + val progressState: ProgressState? by ProgressStateType + + /** + * Indicates the current status of the task. + * + * The task can have one of the following statuses defined by [TaskStatus]: + * - [TaskStatus.RUNNING]: The task is currently in progress. + * - [TaskStatus.PAUSED]: The task has been temporarily paused. + * - [TaskStatus.CANCELED]: The task has been requested to stop, though it might still be running until it can be safely aborted. + * + * The status can be changed based on certain conditions: + * - A running task can be suspended or canceled. + * - A suspended task can be resumed or canceled. + * - A canceled task does not change its status anymore. + * + * The status is managed by user through [TaskManager] + * The status updates can be subscribed using [statuses] + */ + var taskStatus: TaskStatus by TaskStatusType + + companion object : DurableEntityType( + TaskInfoEntity::class.java.name, + "com.intellij.platform.ide.progress", + ::TaskInfoEntity + ) { + var Title = requiredValue("title", String.serializer()) + var TaskCancellationType = requiredValue("taskCancellation", TaskCancellation.serializer()) + var ProgressStateType = optionalValue("progressState", ProgressState.serializer()) + var TaskStatusType = requiredValue("taskStatus", TaskStatus.serializer()) + } +} + diff --git a/platform/progress/shared/src/utils.kt b/platform/progress/shared/src/utils.kt new file mode 100644 index 000000000000..b7e1fedec4ee --- /dev/null +++ b/platform/progress/shared/src/utils.kt @@ -0,0 +1,57 @@ +// 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.platform.ide.progress + +import com.intellij.platform.util.progress.ProgressState +import fleet.kernel.onDispose +import fleet.kernel.rete.* +import kotlinx.coroutines.flow.* +import org.jetbrains.annotations.ApiStatus + +/** + * Represents a query for retrieving all active tasks in the system. + * To receive task updates, use [updates] + * + * @see TaskSupport for more info about how tasks are started + */ +val activeTasks: Query + @ApiStatus.Internal + get() = TaskInfoEntity.each() + +/** + * Returns a query that provides updates to the progress state of a task. + * For more info about progress state see [TaskInfoEntity.progressState] + * + * For a finite flow use [asFiniteFlow] extension + */ +val TaskInfoEntity.updates: Query + @ApiStatus.Internal + get() = asQuery()[TaskInfoEntity.ProgressStateType] + +/** + * Returns a query to retrieve the statuses of a task. + * For more info about task status see [TaskInfoEntity.taskStatus] + * + * For a finite flow use [asFiniteFlow] extension + */ +val TaskInfoEntity.statuses: Query + @ApiStatus.Internal + get() = asQuery()[TaskInfoEntity.TaskStatusType] + +/** + * Converts a query result into a finite flow that emits results as long as the specified entity is alive. + * The flow will terminate when the entity is disposed + * + * @param T the type of the query result. + * @param entity the task information entity associated with the query. It will determine the lifespan of the flow. + * @return a finite flow of query results that will complete when the entity is disposed. + */ +@ApiStatus.Internal +fun Query.asFiniteFlow(entity: TaskInfoEntity, rete: Rete): Flow { + val entityIsAlive = MutableStateFlow(true) + entity.onDispose(rete) { entityIsAlive.value = false } + + return this.matchesFlow() + .combine(entityIsAlive) { state, isAlive -> state.takeIf { isAlive } } + .takeWhile { it != null } + .mapNotNull { it?.value } +} diff --git a/platform/util/progress/api-dump.txt b/platform/util/progress/api-dump.txt index 91873d921d8c..86230a77fb57 100644 --- a/platform/util/progress/api-dump.txt +++ b/platform/util/progress/api-dump.txt @@ -16,9 +16,12 @@ com.intellij.platform.util.progress.ProgressReporter - a:sizedStep(I,java.lang.String,kotlin.jvm.functions.Function2,kotlin.coroutines.Continuation):java.lang.Object - bs:sizedStep$default(com.intellij.platform.util.progress.ProgressReporter,I,java.lang.String,kotlin.jvm.functions.Function2,kotlin.coroutines.Continuation,I,java.lang.Object):java.lang.Object com.intellij.platform.util.progress.ProgressState +- sf:Companion:com.intellij.platform.util.progress.ProgressState$Companion - a:getDetails():java.lang.String - a:getFraction():java.lang.Double - a:getText():java.lang.String +f:com.intellij.platform.util.progress.ProgressState$Companion +- f:serializer():kotlinx.serialization.KSerializer *:com.intellij.platform.util.progress.RawProgressReporter - details(java.lang.String):V - fraction(java.lang.Double):V diff --git a/platform/util/progress/intellij.platform.util.progress.iml b/platform/util/progress/intellij.platform.util.progress.iml index 6393a08df95e..76b8d246ea7b 100644 --- a/platform/util/progress/intellij.platform.util.progress.iml +++ b/platform/util/progress/intellij.platform.util.progress.iml @@ -12,6 +12,12 @@ + + + $KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar + + + @@ -31,5 +37,7 @@ + + \ No newline at end of file diff --git a/platform/util/progress/src/ProgressState.kt b/platform/util/progress/src/ProgressState.kt index 0a7a921aca27..f94ec12a031e 100644 --- a/platform/util/progress/src/ProgressState.kt +++ b/platform/util/progress/src/ProgressState.kt @@ -2,7 +2,9 @@ package com.intellij.platform.util.progress import com.intellij.platform.util.progress.impl.ProgressText +import kotlinx.serialization.Serializable +@Serializable sealed interface ProgressState { val fraction: Double? diff --git a/platform/util/progress/src/StepState.kt b/platform/util/progress/src/StepState.kt index 8a032082a376..5c78c1b74150 100644 --- a/platform/util/progress/src/StepState.kt +++ b/platform/util/progress/src/StepState.kt @@ -2,7 +2,9 @@ package com.intellij.platform.util.progress; import com.intellij.platform.util.progress.impl.ProgressText +import kotlinx.serialization.Serializable +@Serializable internal data class StepState( override val fraction: Double?, override val text: ProgressText?, diff --git a/platform/util/progress/src/impl/impl.kt b/platform/util/progress/src/impl/impl.kt index 38dde799c0dd..093ae493fa6c 100644 --- a/platform/util/progress/src/impl/impl.kt +++ b/platform/util/progress/src/impl/impl.kt @@ -20,6 +20,6 @@ internal fun Flow.pushTextToDetails(text: ProgressText): Flow = suspend CoroutineScope.() -> T