Skip to content

Commit

Permalink
rewrite project-components into the modern way
Browse files Browse the repository at this point in the history
  • Loading branch information
van800 committed Nov 24, 2023
1 parent e1a5b90 commit b581653
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 243 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@ import com.intellij.execution.RunManager
import com.intellij.execution.configurations.ConfigurationTypeUtil
import com.intellij.execution.executors.DefaultDebugExecutor
import com.intellij.ide.impl.ProjectUtil
import com.intellij.openapi.client.ClientProjectSession
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.WindowManager
import com.intellij.util.BitUtil
import com.jetbrains.rd.framework.impl.RdTask
import com.jetbrains.rd.platform.util.idea.ProtocolSubscribedProjectComponent
import com.jetbrains.rd.platform.util.idea.LifetimedService
import com.jetbrains.rd.protocol.SolutionExtListener
import com.jetbrains.rd.util.firstOrNull
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rd.util.reactive.AddRemove
import com.jetbrains.rd.util.reactive.adviseNotNull
import com.jetbrains.rd.util.reactive.flowInto
import com.jetbrains.rider.debugger.*
import com.jetbrains.rider.model.debuggerWorker.OutputMessageWithSubject
import com.jetbrains.rider.model.debuggerWorker.OutputSubject
import com.jetbrains.rider.model.debuggerWorker.OutputType
import com.jetbrains.rider.model.godot.frontendBackend.GodotFrontendBackendModel
import com.jetbrains.rider.model.godot.frontendBackend.TestRunnerOutputEventType
import com.jetbrains.rider.model.godot.frontendBackend.godotFrontendBackendModel
import com.jetbrains.rider.plugins.godot.model.debuggerWorker.godotDebuggerWorkerModel
Expand All @@ -29,83 +33,86 @@ import com.jetbrains.rider.run.configurations.remote.MonoRemoteConfigType
import com.jetbrains.rider.util.NetUtils
import java.awt.Frame

class FrontendBackendHost(project: Project) : ProtocolSubscribedProjectComponent(project) {
class FrontendBackendHost(project: Project) : LifetimedService() {
val model = project.solution.godotFrontendBackendModel
private val debugProcesses = mutableMapOf<Int, DotNetDebugProcess>()

init {
model.activateRider.advise(projectComponentLifetime) {
ProjectUtil.focusProjectWindow(project, true)
val frame = WindowManager.getInstance().getFrame(project)
if (frame != null) {
if (BitUtil.isSet(frame.extendedState, Frame.ICONIFIED))
frame.extendedState = BitUtil.set(frame.extendedState, Frame.ICONIFIED, false)
class ProtocolListener : SolutionExtListener<GodotFrontendBackendModel> {
private val debugProcesses = mutableMapOf<Int, DotNetDebugProcess>()
override fun extensionCreated(lifetime: Lifetime, session: ClientProjectSession, model: GodotFrontendBackendModel) {
val project = session.project
model.activateRider.advise(lifetime) {
ProjectUtil.focusProjectWindow(project, true)
val frame = WindowManager.getInstance().getFrame(project)
if (frame != null) {
if (BitUtil.isSet(frame.extendedState, Frame.ICONIFIED))
frame.extendedState = BitUtil.set(frame.extendedState, Frame.ICONIFIED, false)
}
}
}

model.onTestRunnerOutputEvent.advise(projectComponentLifetime) { output->
debugProcesses.filter{it.key == output.port}.firstOrNull()?.value?.console?.tryWriteMessageToConsoleView(
OutputMessageWithSubject(
output = "${output.message}\r\n",
type = when (output.type) {
TestRunnerOutputEventType.Message -> OutputType.Info
TestRunnerOutputEventType.Error -> OutputType.Error
},
subject = OutputSubject.Default
model.onTestRunnerOutputEvent.advise(lifetime) { output->
debugProcesses.filter{it.key == output.port}.firstOrNull()?.value?.console?.tryWriteMessageToConsoleView(
OutputMessageWithSubject(
output = "${output.message}\r\n",
type = when (output.type) {
TestRunnerOutputEventType.Message -> OutputType.Info
TestRunnerOutputEventType.Error -> OutputType.Error
},
subject = OutputSubject.Default
)
)
)
}
}

model.startDebuggerServer.set { lt, _ ->
val task = RdTask<Int>()
val runManager = RunManager.getInstance(project)
val configurationType = ConfigurationTypeUtil.findConfigurationType(MonoRemoteConfigType::class.java)
val runConfiguration = runManager.createConfiguration(
GodotRunConfigurationGenerator.ATTACH_CONFIGURATION_NAME,
GodotDotNetRemoteConfigurationFactory(configurationType)
)
val remoteConfiguration = runConfiguration.configuration as GodotDotNetRemoteConfiguration
remoteConfiguration.listenPortForConnections = true
remoteConfiguration.port = NetUtils.findFreePort(500013)
remoteConfiguration.address = "127.0.0.1"
model.startDebuggerServer.set { lt, _ ->
val task = RdTask<Int>()
val runManager = RunManager.getInstance(project)
val configurationType = ConfigurationTypeUtil.findConfigurationType(MonoRemoteConfigType::class.java)
val runConfiguration = runManager.createConfiguration(
GodotRunConfigurationGenerator.ATTACH_CONFIGURATION_NAME,
GodotDotNetRemoteConfigurationFactory(configurationType)
)
val remoteConfiguration = runConfiguration.configuration as GodotDotNetRemoteConfiguration
remoteConfiguration.listenPortForConnections = true
remoteConfiguration.port = NetUtils.findFreePort(500013)
remoteConfiguration.address = "127.0.0.1"

val processTracker: RiderDebugActiveDotNetSessionsTracker =
RiderDebugActiveDotNetSessionsTracker.getInstance(project)
processTracker.dotNetDebugProcesses.change.advise(lt) { (event, debugProcess) ->
if (event == AddRemove.Add) {
debugProcesses[remoteConfiguration.port] = debugProcess
debugProcess.sessionLifetime.onTermination { debugProcesses.remove(remoteConfiguration.port) }
debugProcess.initializeDebuggerTask.debuggerInitializingState.advise(lt) {
if (it == DebuggerInitializingState.Initialized)
task.set(remoteConfiguration.port)
if (it == DebuggerInitializingState.Canceled)
task.set(0)
val processTracker: RiderDebugActiveDotNetSessionsTracker =
RiderDebugActiveDotNetSessionsTracker.getInstance(project)
processTracker.dotNetDebugProcesses.change.advise(lt) { (event, debugProcess) ->
if (event == AddRemove.Add) {
debugProcesses[remoteConfiguration.port] = debugProcess
debugProcess.sessionLifetime.onTermination { debugProcesses.remove(remoteConfiguration.port) }
debugProcess.initializeDebuggerTask.debuggerInitializingState.advise(lt) {
if (it == DebuggerInitializingState.Initialized)
task.set(remoteConfiguration.port)
if (it == DebuggerInitializingState.Canceled)
task.set(0)
}
}
}
}

ProgramRunnerUtil.executeConfiguration(runConfiguration, DefaultDebugExecutor.getDebugExecutorInstance())
ProgramRunnerUtil.executeConfiguration(runConfiguration, DefaultDebugExecutor.getDebugExecutorInstance())

task
}
task
}

GodotProjectDiscoverer.getInstance(project).godotMonoPath.adviseNotNull(projectComponentLifetime){
model.godotPath.set(it)
}
GodotProjectDiscoverer.getInstance(project).godotMonoPath.adviseNotNull(lifetime){
model.godotPath.set(it)
}

GodotProjectDiscoverer.getInstance(project).godotCorePath.adviseNotNull(projectComponentLifetime){
GodotProjectDiscoverer.getInstance(project).godotCorePath.adviseNotNull(lifetime){ s ->

RiderDebuggerWorkerModelManager.getModels().adviseNotNull(projectComponentLifetime){
model.backendSettings.enableDebuggerExtensions.flowInto(projectComponentLifetime,
it.value.godotDebuggerWorkerModel.showCustomRenderers)
RiderDebuggerWorkerModelManager.getModels().adviseNotNull(lifetime){
model.backendSettings.enableDebuggerExtensions.flowInto(lifetime,
it.value.godotDebuggerWorkerModel.showCustomRenderers)

}
}

model.godotPath.set(it)
model.godotPath.set(s)
}
}
}

companion object {
fun getInstance(project: Project): FrontendBackendHost = project.getComponent(FrontendBackendHost::class.java)
fun getInstance(project: Project): FrontendBackendHost = project.getService(FrontendBackendHost::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import com.intellij.openapi.client.ClientProjectSession
import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.jetbrains.rd.platform.util.idea.LifetimedService
import com.intellij.openapi.rd.util.lifetime
import com.jetbrains.rd.platform.util.idea.LifetimedService
import com.jetbrains.rd.protocol.SolutionExtListener
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rd.util.reactive.IProperty
Expand All @@ -32,8 +32,10 @@ class GodotProjectDiscoverer(project: Project) : LifetimedService() {
init {
mainProjectBasePath.adviseNotNull(project.lifetime){
logger.info("Godot mainProjectBasePath: $it")
godotMonoPath.set(MetadataMonoFileWatcher.getFromMonoMetadataPath(it) ?: MetadataMonoFileWatcher.getGodotPath(it) ?: getGodotPathFromPlayerRunConfiguration(project))
godotCorePath.set(MetadataCoreFileWatcher.getGodotPath(it) ?: getGodotPathFromCorePlayerRunConfiguration(project))
godotMonoPath.set(
MetadataMonoFileWatcher.Util.getFromMonoMetadataPath(it)
?: MetadataMonoFileWatcher.Util.getGodotPath(it) ?: getGodotPathFromPlayerRunConfiguration(project))
godotCorePath.set(MetadataCoreFileWatcher.Util.getGodotPath(it) ?: getGodotPathFromCorePlayerRunConfiguration(project))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package com.jetbrains.rider.plugins.godot

import com.intellij.openapi.application.EDT
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.rd.util.launchBackground
import com.intellij.openapi.rd.util.lifetime
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.util.application
import com.jetbrains.rd.util.lifetime.isAlive
import com.jetbrains.rd.util.reactive.viewNotNull
import com.jetbrains.rd.util.reactive.whenTrue
import com.jetbrains.rdclient.util.idea.LifetimedProjectComponent
import com.jetbrains.rider.projectView.solution
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.time.withTimeout
import kotlinx.coroutines.withContext
import java.io.File
import java.nio.file.*
import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE
Expand All @@ -19,13 +23,9 @@ import java.time.Duration
import kotlin.io.path.isDirectory


class MetadataCoreFileWatcher(project: Project) : LifetimedProjectComponent(project) {

companion object {
const val cfgDir = ".godot/editor"
const val cfgFileName = "project_metadata.cfg"
private val logger = Logger.getInstance(MetadataCoreFileWatcher::class.java)
class MetadataCoreFileWatcher : ProjectActivity {

object Util {
fun getGodotPath(mainProjectBasePath: String): String? {
val projectPath = File(mainProjectBasePath)
val projectMetadataCfg = projectPath.resolve(cfgDir).resolve(cfgFileName)
Expand All @@ -42,49 +42,58 @@ class MetadataCoreFileWatcher(project: Project) : LifetimedProjectComponent(proj
}
}

init {
project.solution.isLoaded.whenTrue(componentLifetime) { l ->
val godotDiscoverer = GodotProjectDiscoverer.getInstance(project)
godotDiscoverer.mainProjectBasePath.viewNotNull(l) { lt, mainProjectBasePath ->
lt.launchBackground {
val watchService: WatchService = FileSystems.getDefault().newWatchService()
val metaFileDir = File(mainProjectBasePath).resolve(cfgDir).toPath()
companion object {
const val cfgDir = ".godot/editor"
const val cfgFileName = "project_metadata.cfg"
private val logger = Logger.getInstance(MetadataCoreFileWatcher::class.java)

}

override suspend fun execute(project: Project) {
withContext(Dispatchers.EDT) {
project.solution.isLoaded.whenTrue(project.lifetime) {l->
val godotDiscoverer = GodotProjectDiscoverer.getInstance(project)
godotDiscoverer.mainProjectBasePath.viewNotNull(l) { lt, mainProjectBasePath ->
lt.launchBackground {
val watchService: WatchService = FileSystems.getDefault().newWatchService()
val metaFileDir = File(mainProjectBasePath).resolve(cfgDir).toPath()

withTimeout(Duration.ofMinutes(5)) {
while (!(metaFileDir.isDirectory())) {
// wait for folder to appear
delay(3000)
withTimeout(Duration.ofMinutes(5)) {
while (!(metaFileDir.isDirectory())) {
// wait for folder to appear
delay(3000)
}
}
}

if (!(metaFileDir.isDirectory()))
return@launchBackground
if (!(metaFileDir.isDirectory()))
return@launchBackground

metaFileDir.register(watchService, ENTRY_CREATE, ENTRY_MODIFY)
metaFileDir.register(watchService, ENTRY_CREATE, ENTRY_MODIFY)

lt.onTerminationIfAlive {
watchService.close() // releases watchService.take()
}
lt.onTerminationIfAlive {
watchService.close() // releases watchService.take()
}

var key: WatchKey
try {
while (watchService.take().also { key = it } != null && lt.isAlive) {
for (event in key.pollEvents()) {
val context = event.context() ?: continue
if (context.toString() == cfgFileName) {
logger.info("GodotCoreProjectDiscoverer.getInstance(project).godotPath.set()")
val newPath = getGodotPath(mainProjectBasePath) ?: continue
logger.info("GodotCoreProjectDiscoverer.getInstance(project).godotPath.set($newPath)")
application.invokeLater {
logger.info("application.invokeLater GodotProjectDiscoverer.getInstance(project).godotPath.set($newPath)")
GodotProjectDiscoverer.getInstance(project).godotCorePath.set(newPath)
var key: WatchKey
try {
while (watchService.take().also { key = it } != null && lt.isAlive) {
for (event in key.pollEvents()) {
val context = event.context() ?: continue
if (context.toString() == cfgFileName) {
logger.info("GodotCoreProjectDiscoverer.getInstance(project).godotPath.set()")
val newPath = Util.getGodotPath(mainProjectBasePath) ?: continue
logger.info("GodotCoreProjectDiscoverer.getInstance(project).godotPath.set($newPath)")
application.invokeLater {
logger.info("application.invokeLater GodotProjectDiscoverer.getInstance(project).godotPath.set($newPath)")
GodotProjectDiscoverer.getInstance(project).godotCorePath.set(newPath)
}
}
}
key.reset()
}
key.reset()
}
} catch (e: ClosedWatchServiceException) {
} // this is expected on `watchService.close()`
} catch (e: ClosedWatchServiceException) {
} // this is expected on `watchService.close()`
}
}
}
}
Expand Down

0 comments on commit b581653

Please sign in to comment.