[debugger-rd] IJPL-160146: ActiveSession should be provided only by backend

GitOrigin-RevId: cb80a52ce914dcf8f7106be9e889139dbb7363ca
This commit is contained in:
Nikolay Rykunov
2024-10-10 15:18:03 +02:00
committed by intellij-monorepo-bot
parent bcfe903c87
commit 1c1f348eeb
7 changed files with 159 additions and 136 deletions

View File

@@ -6,5 +6,6 @@
<platform.rpc.backend.remoteApiProvider
implementation="com.intellij.platform.debugger.impl.backend.BackendXDebuggerRemoteApiProviders"/>
<platform.entityTypes implementation="com.intellij.platform.debugger.impl.backend.BackendXDebuggerEntityTypesProvider"/>
<postStartupActivity implementation="com.intellij.platform.debugger.impl.backend.BackendXDebuggerActiveSessionControllerProjectActivity"/>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,100 @@
// 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.debugger.impl.backend
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.platform.kernel.withKernel
import com.intellij.platform.project.asEntity
import com.intellij.xdebugger.XDebuggerManager
import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.intellij.xdebugger.impl.XDebuggerActiveSessionEntity
import com.intellij.xdebugger.impl.XDebuggerManagerImpl
import com.intellij.xdebugger.impl.rpc.XDebuggerEvaluatorId
import com.jetbrains.rhizomedb.entity
import fleet.kernel.change
import fleet.kernel.shared
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
private class BackendXDebuggerActiveSessionControllerProjectActivity : ProjectActivity {
override suspend fun execute(project: Project) {
project.service<BackendXDebuggerActiveSessionController>().synchronizeActiveSessionWithDb()
}
}
@Service(Service.Level.PROJECT)
private class BackendXDebuggerActiveSessionController(private val project: Project, private val cs: CoroutineScope) {
fun synchronizeActiveSessionWithDb() {
val activeSessionState = (XDebuggerManager.getInstance(project) as? XDebuggerManagerImpl)?.currentSessionFlow ?: return
cs.launch(Dispatchers.IO) {
activeSessionState.collectLatest { newActiveSession ->
// if there is no active session -> remove entity from DB
if (newActiveSession == null) {
withKernel {
change {
shared {
val projectEntity = project.asEntity()
entity(XDebuggerActiveSessionEntity.Project, projectEntity)?.delete()
}
}
}
return@collectLatest
}
// remove an old session and create a new one
val newActiveSessionEntity = withKernel {
withKernel {
change {
shared {
val projectEntity = project.asEntity()
entity(XDebuggerActiveSessionEntity.Project, projectEntity)?.delete()
XDebuggerActiveSessionEntity.new {
it[XDebuggerActiveSessionEntity.Project] = projectEntity
}
}
}
}
}
// synchronize evaluator for the created session entity
// this scope will be cancelled when a newActiveSession is appeared
supervisorScope {
launch {
synchronizeSessionEvaluatorWithDb(newActiveSession, newActiveSessionEntity)
}
}
}
}
}
private suspend fun synchronizeSessionEvaluatorWithDb(session: XDebugSessionImpl, sessionEntity: XDebuggerActiveSessionEntity) {
// NB!: we assume that the current evaluator depends only on the current StackFrame
session.currentStackFrameFlow.collect { stackFrameRef ->
val stackFrame = stackFrameRef.get()
withKernel {
change {
val currentEvaluator = stackFrame?.evaluator
if (currentEvaluator == null) {
entity(LocalXDebuggerSessionEvaluatorEntity.Session, sessionEntity)?.delete()
shared {
sessionEntity[XDebuggerActiveSessionEntity.EvaluatorId] = null
}
}
else {
val evaluatorEntity = LocalXDebuggerSessionEvaluatorEntity.upsert(LocalXDebuggerSessionEvaluatorEntity.Session, sessionEntity) {
it[LocalXDebuggerSessionEvaluatorEntity.Session] = sessionEntity
it[LocalXDebuggerSessionEvaluatorEntity.Evaluator] = currentEvaluator
}
val evaluatorId = XDebuggerEvaluatorId(evaluatorEntity.eid)
shared {
sessionEntity[XDebuggerActiveSessionEntity.EvaluatorId] = evaluatorId
}
}
}
}
}
}
}

View File

@@ -12,7 +12,6 @@ import com.intellij.ui.SimpleTextAttributes
import com.intellij.xdebugger.XDebuggerBundle
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator.XEvaluationCallback
import com.intellij.xdebugger.frame.*
import com.intellij.xdebugger.impl.LocalXDebuggerSessionEvaluatorEntity
import com.intellij.xdebugger.impl.rpc.*
import com.intellij.xdebugger.impl.ui.tree.nodes.XValuePresentationUtil.XValuePresentationTextExtractor
import com.jetbrains.rhizomedb.entity

View File

@@ -5,15 +5,20 @@ import com.intellij.platform.kernel.EntityTypeProvider
import com.intellij.platform.project.ProjectEntity
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator
import com.intellij.xdebugger.frame.XValue
import com.intellij.xdebugger.impl.XDebuggerActiveSessionEntity
import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHint
import com.jetbrains.rhizomedb.*
import com.jetbrains.rhizomedb.EID
import com.jetbrains.rhizomedb.Entity
import com.jetbrains.rhizomedb.EntityType
import com.jetbrains.rhizomedb.RefFlags
import org.jetbrains.annotations.ApiStatus
private class BackendXDebuggerEntityTypesProvider : EntityTypeProvider {
override fun entityTypes(): List<EntityType<*>> {
return listOf(
LocalValueHintEntity,
LocalHintXValueEntity
LocalHintXValueEntity,
LocalXDebuggerSessionEvaluatorEntity,
)
}
}
@@ -46,4 +51,19 @@ internal data class LocalHintXValueEntity(override val eid: EID) : Entity {
val Project = requiredRef<ProjectEntity>("project")
val XValue = requiredTransient<XValue>("xValue")
}
}
internal data class LocalXDebuggerSessionEvaluatorEntity(override val eid: EID) : Entity {
val sessionEntity by Session
val evaluator by Evaluator
@ApiStatus.Internal
companion object : EntityType<LocalXDebuggerSessionEvaluatorEntity>(
LocalXDebuggerSessionEvaluatorEntity::class.java.name,
"com.intellij",
::LocalXDebuggerSessionEvaluatorEntity
) {
val Session = requiredRef<XDebuggerActiveSessionEntity>("session", RefFlags.UNIQUE)
val Evaluator = requiredTransient<XDebuggerEvaluator>("evaluator")
}
}

View File

@@ -1,130 +0,0 @@
// 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.xdebugger.impl
import com.intellij.openapi.project.Project
import com.intellij.platform.kernel.EntityTypeProvider
import com.intellij.platform.kernel.withKernel
import com.intellij.platform.project.ProjectEntity
import com.intellij.platform.project.asEntity
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator
import com.intellij.xdebugger.impl.rpc.XDebuggerEvaluatorId
import com.jetbrains.rhizomedb.*
import fleet.kernel.DurableEntityType
import fleet.kernel.change
import fleet.kernel.shared
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import org.jetbrains.annotations.ApiStatus
private class XDebuggerActiveSessionEntityTypesProvider : EntityTypeProvider {
override fun entityTypes(): List<EntityType<*>> {
return listOf(
XDebuggerActiveSessionEntity,
LocalXDebuggerSessionEvaluatorEntity
)
}
}
@ApiStatus.Internal
data class XDebuggerActiveSessionEntity(override val eid: EID) : Entity {
val projectEntity by Project
val evaluatorId by EvaluatorId
@ApiStatus.Internal
companion object : DurableEntityType<XDebuggerActiveSessionEntity>(
XDebuggerActiveSessionEntity::class.java.name,
"com.intellij",
::XDebuggerActiveSessionEntity
) {
val Project = requiredRef<ProjectEntity>("project", RefFlags.UNIQUE)
var EvaluatorId = optionalValue<XDebuggerEvaluatorId>("evaluatorId", XDebuggerEvaluatorId.serializer())
}
}
@ApiStatus.Internal
data class LocalXDebuggerSessionEvaluatorEntity(override val eid: EID) : Entity {
val sessionEntity by Session
val evaluator by Evaluator
@ApiStatus.Internal
companion object : DurableEntityType<LocalXDebuggerSessionEvaluatorEntity>(
LocalXDebuggerSessionEvaluatorEntity::class.java.name,
"com.intellij",
::LocalXDebuggerSessionEvaluatorEntity
) {
val Session = requiredRef<XDebuggerActiveSessionEntity>("session", RefFlags.UNIQUE)
val Evaluator = requiredTransient<XDebuggerEvaluator>("evaluator")
}
}
internal fun synchronizeActiveSessionWithDb(coroutineScope: CoroutineScope, project: Project, activeSessionState: Flow<XDebugSessionImpl?>) {
coroutineScope.launch(Dispatchers.IO) {
activeSessionState.collectLatest { newActiveSession ->
// if there is no active session -> remove entity from DB
if (newActiveSession == null) {
withKernel {
change {
shared {
val projectEntity = project.asEntity()
entity(XDebuggerActiveSessionEntity.Project, projectEntity)?.delete()
}
}
}
return@collectLatest
}
// remove an old session and create a new one
val newActiveSessionEntity = withKernel {
withKernel {
change {
shared {
val projectEntity = project.asEntity()
entity(XDebuggerActiveSessionEntity.Project, projectEntity)?.delete()
XDebuggerActiveSessionEntity.new {
it[XDebuggerActiveSessionEntity.Project] = projectEntity
}
}
}
}
}
// synchronize evaluator for the created session entity
// this scope will be cancelled when a newActiveSession is appeared
supervisorScope {
launch {
synchronizeSessionEvaluatorWithDb(newActiveSession, newActiveSessionEntity)
}
}
}
}
}
private suspend fun synchronizeSessionEvaluatorWithDb(session: XDebugSessionImpl, sessionEntity: XDebuggerActiveSessionEntity) {
// NB!: we assume that the current evaluator depends only on the current StackFrame
session.currentStackFrameFlow.collect { stackFrameRef ->
val stackFrame = stackFrameRef.get()
withKernel {
change {
val currentEvaluator = stackFrame?.evaluator
if (currentEvaluator == null) {
entity(LocalXDebuggerSessionEvaluatorEntity.Session, sessionEntity)?.delete()
shared {
sessionEntity[XDebuggerActiveSessionEntity.EvaluatorId] = null
}
}
else {
val evaluatorEntity = LocalXDebuggerSessionEvaluatorEntity.upsert(LocalXDebuggerSessionEvaluatorEntity.Session, sessionEntity) {
it[LocalXDebuggerSessionEvaluatorEntity.Session] = sessionEntity
it[LocalXDebuggerSessionEvaluatorEntity.Evaluator] = currentEvaluator
}
val evaluatorId = XDebuggerEvaluatorId(evaluatorEntity.eid)
shared {
sessionEntity[XDebuggerActiveSessionEntity.EvaluatorId] = evaluatorId
}
}
}
}
}
}

View File

@@ -0,0 +1,36 @@
// 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.xdebugger.impl
import com.intellij.platform.kernel.EntityTypeProvider
import com.intellij.platform.project.ProjectEntity
import com.intellij.xdebugger.impl.rpc.XDebuggerEvaluatorId
import com.jetbrains.rhizomedb.EID
import com.jetbrains.rhizomedb.Entity
import com.jetbrains.rhizomedb.EntityType
import com.jetbrains.rhizomedb.RefFlags
import fleet.kernel.DurableEntityType
import org.jetbrains.annotations.ApiStatus
private class XDebuggerActiveSessionEntityTypesProvider : EntityTypeProvider {
override fun entityTypes(): List<EntityType<*>> {
return listOf(
XDebuggerActiveSessionEntity,
)
}
}
@ApiStatus.Internal
data class XDebuggerActiveSessionEntity(override val eid: EID) : Entity {
val projectEntity by Project
val evaluatorId by EvaluatorId
@ApiStatus.Internal
companion object : DurableEntityType<XDebuggerActiveSessionEntity>(
XDebuggerActiveSessionEntity::class.java.name,
"com.intellij",
::XDebuggerActiveSessionEntity
) {
val Project = requiredRef<ProjectEntity>("project", RefFlags.UNIQUE)
var EvaluatorId = optionalValue<XDebuggerEvaluatorId>("evaluatorId", XDebuggerEvaluatorId.serializer())
}
}

View File

@@ -81,7 +81,6 @@ import java.util.*;
import java.util.concurrent.ExecutorService;
import static com.intellij.xdebugger.impl.CoroutineUtilsKt.createMutableStateFlow;
import static com.intellij.xdebugger.impl.XDebuggerActiveSessionControllerKt.synchronizeActiveSessionWithDb;
@ApiStatus.Internal
@State(name = "XDebuggerManager", storages = @Storage(StoragePathMacros.WORKSPACE_FILE))
@@ -114,8 +113,6 @@ public final class XDebuggerManagerImpl extends XDebuggerManager implements Pers
myPinToTopManager = new XDebuggerPinToTopManager(coroutineScope);
myExecutionPointManager = new XDebuggerExecutionPointManager(project, coroutineScope);
synchronizeActiveSessionWithDb(coroutineScope, project, myActiveSession);
messageBusConnection.subscribe(FileDocumentManagerListener.TOPIC, new FileDocumentManagerListener() {
@Override
public void fileContentLoaded(@NotNull VirtualFile file, @NotNull Document document) {