mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
[debugger-rd] IJPL-160146: Use new withKernel API
GitOrigin-RevId: 698cd83aac3469010f9b6a1d16ab99f0b511621e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1a38e580a4
commit
686bcc08a7
@@ -0,0 +1,138 @@
|
|||||||
|
// 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.project.impl
|
||||||
|
|
||||||
|
import com.intellij.openapi.observable.util.whenDisposed
|
||||||
|
import com.intellij.openapi.progress.runBlockingMaybeCancellable
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.util.getOrCreateUserData
|
||||||
|
import com.intellij.platform.kernel.KernelService
|
||||||
|
import com.intellij.platform.kernel.util.flushLatestChange
|
||||||
|
import com.intellij.platform.kernel.withKernel
|
||||||
|
import com.jetbrains.rhizomedb.*
|
||||||
|
import fleet.kernel.DurableEntityType
|
||||||
|
import fleet.kernel.change
|
||||||
|
import fleet.kernel.kernel
|
||||||
|
import fleet.kernel.shared
|
||||||
|
import fleet.util.UID
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.jetbrains.annotations.ApiStatus
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
val KERNEL_PROJECT_ID = Key.create<UID>("ProjectImpl.KERNEL_PROJECT_ID")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a project entity that can be shared between backend and frontends.
|
||||||
|
* The entity is created on project initialization before any services and components are loaded.
|
||||||
|
*
|
||||||
|
* To convert a project to the entity use [asEntity]
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
data class ProjectEntity(override val eid: EID) : Entity {
|
||||||
|
var projectId: UID by ProjectId
|
||||||
|
|
||||||
|
companion object: DurableEntityType<ProjectEntity>(ProjectEntity::class.java.name, "com.intellij", ::ProjectEntity) {
|
||||||
|
val ProjectId = requiredValue("projectId", UID.serializer(), Indexing.UNIQUE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class LocalProjectEntity(override val eid: EID) : Entity {
|
||||||
|
val sharedEntity: ProjectEntity by ProjectEntity
|
||||||
|
val project: Project by Project
|
||||||
|
|
||||||
|
companion object: EntityType<LocalProjectEntity>(LocalProjectEntity::class, ::LocalProjectEntity) {
|
||||||
|
val ProjectEntity = requiredRef<ProjectEntity>("sharedEntity", RefFlags.CASCADE_DELETE_BY)
|
||||||
|
val Project = requiredTransient<Project>("project")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a given project to its corresponding [ProjectEntity].
|
||||||
|
*
|
||||||
|
* The method has to be called in a kernel context - see [com.intellij.platform.kernel.KernelService.kernelCoroutineContext]
|
||||||
|
*
|
||||||
|
* @return The [ProjectEntity] instance associated with the provided project,
|
||||||
|
* or null if no such entity is found
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
fun Project.asEntity(): ProjectEntity? {
|
||||||
|
return LocalProjectEntity.all().singleOrNull { it.project == this }?.sharedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a given project entity to its corresponding [Project].
|
||||||
|
*
|
||||||
|
* The method has to be called in a kernel context - see [com.intellij.platform.kernel.KernelService.kernelCoroutineContext]
|
||||||
|
*
|
||||||
|
* @return The [Project] instance associated with the provided entity,
|
||||||
|
* or null if no such project is found (for example, if [ProjectEntity] doesn't exist anymore).
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
fun ProjectEntity.asProject(): Project? {
|
||||||
|
return LocalProjectEntity.all().singleOrNull { it.sharedEntity == this }?.project
|
||||||
|
}
|
||||||
|
|
||||||
|
internal suspend fun Project.createEntity() = withKernel {
|
||||||
|
val project = this@createEntity
|
||||||
|
val projectId = project.getOrCreateUserData(KERNEL_PROJECT_ID) { UID.random() }
|
||||||
|
|
||||||
|
// TODO it shouldn't be here
|
||||||
|
change {
|
||||||
|
shared {
|
||||||
|
register(ProjectEntity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
change {
|
||||||
|
val projectEntity = shared {
|
||||||
|
/*
|
||||||
|
This check is added to ensure that only one ProjectEntity is going to be created in split mode.
|
||||||
|
Two entities are possible due to a different flow in creating a project in split mode.
|
||||||
|
|
||||||
|
First, a project is created on the backend (ProjectEntity is created at the same time).
|
||||||
|
Then a signal about project creation is sent to the frontend via RD protocol.
|
||||||
|
At the same time, the shared part of Rhizome DB (where ProjectEntity is stored) sends the changes to the frontend.
|
||||||
|
|
||||||
|
Events which are coming via RD protocol are not synced with events coming via Rhizome DB.
|
||||||
|
So it can happen that while on the backend the signal is sent strictly after ProjectEntity creation,
|
||||||
|
on the frontend the signal can be received before there is ProjectEntity available in DB.
|
||||||
|
|
||||||
|
If it happens that the entity has not been found and the frontend creates a new one, Rhizome DB will perform a "rebase"
|
||||||
|
which basically re-invokes the whole "change" block either on the backend or the frontend side.
|
||||||
|
*/
|
||||||
|
val existing = ProjectEntity.all().singleOrNull { it.projectId == projectId }
|
||||||
|
if (existing != null) {
|
||||||
|
existing
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ProjectEntity.new {
|
||||||
|
it[ProjectEntity.ProjectId] = projectId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalProjectEntity.new {
|
||||||
|
it[LocalProjectEntity.ProjectEntity] = projectEntity
|
||||||
|
it[LocalProjectEntity.Project] = project
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project.whenDisposed {
|
||||||
|
runBlockingMaybeCancellable {
|
||||||
|
removeProjectEntity(project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun removeProjectEntity(project: Project) = withKernel {
|
||||||
|
change {
|
||||||
|
shared {
|
||||||
|
project.asEntity()?.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removing ProjectEntity and LocalProjectEntity is the last operation in most of the tests
|
||||||
|
// Without calling "flushLatestChange" kernel keeps the project, which causes "testProjectLeak" failures
|
||||||
|
kernel().flushLatestChange()
|
||||||
|
}
|
||||||
@@ -106,7 +106,7 @@ private class BackendDebuggerValueLookupHintsRemoteApi : XDebuggerValueLookupHin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getProjectFromUID(projectId: UID): Project? {
|
private suspend fun getProjectFromUID(projectId: UID): Project? {
|
||||||
return withContext(KernelService.kernelCoroutineContext()) {
|
return withKernel {
|
||||||
val projectEntity = entity<ProjectEntity, UID>(ProjectEntity.ProjectId, projectId)
|
val projectEntity = entity<ProjectEntity, UID>(ProjectEntity.ProjectId, projectId)
|
||||||
projectEntity?.asProject()
|
projectEntity?.asProject()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
package com.intellij.xdebugger.impl.frontend.evaluate.quick.common
|
package com.intellij.xdebugger.impl.frontend.evaluate.quick.common
|
||||||
|
|
||||||
import com.intellij.platform.kernel.KernelService
|
import com.intellij.platform.kernel.KernelService
|
||||||
|
import com.intellij.platform.kernel.withKernel
|
||||||
import com.intellij.xdebugger.impl.evaluate.XDebuggerValueLookupHideHintsRequestEntity
|
import com.intellij.xdebugger.impl.evaluate.XDebuggerValueLookupHideHintsRequestEntity
|
||||||
import com.intellij.xdebugger.impl.evaluate.XDebuggerValueLookupListeningStartedEntity
|
import com.intellij.xdebugger.impl.evaluate.XDebuggerValueLookupListeningStartedEntity
|
||||||
import fleet.kernel.change
|
import fleet.kernel.change
|
||||||
@@ -15,7 +16,7 @@ import kotlinx.coroutines.withContext
|
|||||||
|
|
||||||
internal fun subscribeForDebuggingStart(cs: CoroutineScope, onStartListening: () -> Unit) {
|
internal fun subscribeForDebuggingStart(cs: CoroutineScope, onStartListening: () -> Unit) {
|
||||||
cs.launch(Dispatchers.Default) {
|
cs.launch(Dispatchers.Default) {
|
||||||
withContext(KernelService.kernelCoroutineContext()) {
|
withKernel {
|
||||||
change {
|
change {
|
||||||
shared {
|
shared {
|
||||||
register(XDebuggerValueLookupListeningStartedEntity)
|
register(XDebuggerValueLookupListeningStartedEntity)
|
||||||
@@ -32,7 +33,7 @@ internal fun subscribeForDebuggingStart(cs: CoroutineScope, onStartListening: ()
|
|||||||
|
|
||||||
internal fun subscribeForValueHintHideRequest(cs: CoroutineScope, onHintHidden: () -> Unit) {
|
internal fun subscribeForValueHintHideRequest(cs: CoroutineScope, onHintHidden: () -> Unit) {
|
||||||
cs.launch(Dispatchers.Default) {
|
cs.launch(Dispatchers.Default) {
|
||||||
withContext(KernelService.kernelCoroutineContext()) {
|
withKernel {
|
||||||
change {
|
change {
|
||||||
shared {
|
shared {
|
||||||
register(XDebuggerValueLookupHideHintsRequestEntity)
|
register(XDebuggerValueLookupHideHintsRequestEntity)
|
||||||
@@ -43,10 +44,12 @@ internal fun subscribeForValueHintHideRequest(cs: CoroutineScope, onHintHidden:
|
|||||||
onHintHidden()
|
onHintHidden()
|
||||||
}
|
}
|
||||||
// TODO: support multiple clients by clientId
|
// TODO: support multiple clients by clientId
|
||||||
cs.launch(Dispatchers.Default + KernelService.kernelCoroutineContext()) {
|
cs.launch(Dispatchers.Default) {
|
||||||
change {
|
withKernel {
|
||||||
shared {
|
change {
|
||||||
entity.delete()
|
shared {
|
||||||
|
entity.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.impl.asEntity
|
|||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.platform.kernel.KernelService
|
import com.intellij.platform.kernel.KernelService
|
||||||
|
import com.intellij.platform.kernel.withKernel
|
||||||
import com.intellij.platform.rpc.RemoteApiProviderService
|
import com.intellij.platform.rpc.RemoteApiProviderService
|
||||||
import com.intellij.platform.util.coroutines.childScope
|
import com.intellij.platform.util.coroutines.childScope
|
||||||
import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHint
|
import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHint
|
||||||
@@ -51,20 +52,20 @@ open class ValueLookupManagerQuickEvaluateHandler : QuickEvaluateHandler() {
|
|||||||
val hintCoroutineScope = editor.childCoroutineScope("ValueLookupManagerValueHintParentScope")
|
val hintCoroutineScope = editor.childCoroutineScope("ValueLookupManagerValueHintParentScope")
|
||||||
|
|
||||||
val hint: Deferred<AbstractValueHint?> = coroutineScope.async(Dispatchers.IO) {
|
val hint: Deferred<AbstractValueHint?> = coroutineScope.async(Dispatchers.IO) {
|
||||||
withContext(KernelService.kernelCoroutineContext()) {
|
withKernel {
|
||||||
val remoteApi = RemoteApiProviderService.resolve(remoteApiDescriptor<XDebuggerValueLookupHintsRemoteApi>())
|
val remoteApi = RemoteApiProviderService.resolve(remoteApiDescriptor<XDebuggerValueLookupHintsRemoteApi>())
|
||||||
val projectEntity = project.asEntity()
|
val projectEntity = project.asEntity()
|
||||||
if (projectEntity == null) {
|
if (projectEntity == null) {
|
||||||
return@withContext null
|
return@withKernel null
|
||||||
}
|
}
|
||||||
val projectId = projectEntity.projectId
|
val projectId = projectEntity.projectId
|
||||||
val editorId = editor.editorId()
|
val editorId = editor.editorId()
|
||||||
val canShowHint = remoteApi.canShowHint(projectId, editorId, offset, type)
|
val canShowHint = remoteApi.canShowHint(projectId, editorId, offset, type)
|
||||||
if (!canShowHint) {
|
if (!canShowHint) {
|
||||||
return@withContext null
|
return@withKernel null
|
||||||
}
|
}
|
||||||
val hint = ValueLookupManagerValueHint(hintCoroutineScope, project, projectId, editor, point, type, offset)
|
val hint = ValueLookupManagerValueHint(hintCoroutineScope, project, projectId, editor, point, type, offset)
|
||||||
return@withContext hint
|
return@withKernel hint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val hintPromise = hint.asCompletableFuture().asCancellablePromise()
|
val hintPromise = hint.asCompletableFuture().asCancellablePromise()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.intellij.openapi.components.service
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.platform.kernel.KernelService
|
import com.intellij.platform.kernel.KernelService
|
||||||
|
import com.intellij.platform.kernel.withKernel
|
||||||
import com.jetbrains.rhizomedb.EID
|
import com.jetbrains.rhizomedb.EID
|
||||||
import com.jetbrains.rhizomedb.Entity
|
import com.jetbrains.rhizomedb.Entity
|
||||||
import fleet.kernel.DurableEntityType
|
import fleet.kernel.DurableEntityType
|
||||||
@@ -47,7 +48,7 @@ class ValueLookupManagerController(private val project: Project, private val cs:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
cs.launch(Dispatchers.Main) {
|
cs.launch(Dispatchers.Main) {
|
||||||
withContext(KernelService.kernelCoroutineContext()) {
|
withKernel {
|
||||||
change {
|
change {
|
||||||
shared {
|
shared {
|
||||||
register(XDebuggerValueLookupListeningStartedEntity)
|
register(XDebuggerValueLookupListeningStartedEntity)
|
||||||
@@ -71,7 +72,7 @@ class ValueLookupManagerController(private val project: Project, private val cs:
|
|||||||
*/
|
*/
|
||||||
fun hideHint() {
|
fun hideHint() {
|
||||||
cs.launch(Dispatchers.Main) {
|
cs.launch(Dispatchers.Main) {
|
||||||
withContext(KernelService.kernelCoroutineContext()) {
|
withKernel {
|
||||||
change {
|
change {
|
||||||
shared {
|
shared {
|
||||||
register(XDebuggerValueLookupHideHintsRequestEntity)
|
register(XDebuggerValueLookupHideHintsRequestEntity)
|
||||||
|
|||||||
Reference in New Issue
Block a user