mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 09:03:31 +07:00
[Compose Hot Reload] Move the hot reload feature to the DevKit plugin, promote the version. Add registry listener to download agent by the registry value change
GitOrigin-RevId: 3255421eb5131c0621c8396cbcad916d03052fbb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
faf0483bbf
commit
a477fda360
@@ -698,6 +698,10 @@
|
||||
<properties.spellcheckerMnemonicsTokenizer implementation="org.jetbrains.idea.devkit.spellchecker.DevKitMnemonicsTokenizer"/>
|
||||
|
||||
<useScopeEnlarger implementation="org.jetbrains.idea.devkit.references.DomTargetUseScopeEnlarger"/>
|
||||
|
||||
<registryKey key="devkit.compose.hot.reload.enabled" defaultValue="false" description="Enable 'Compose Hot Reload' agent for run configurations"/>
|
||||
<registryKey key="devkit.compose.hot.reload.agent.version" defaultValue="" description="'Compose Hot Reload' agent version override"/>
|
||||
<runConfigurationExtension implementation="org.jetbrains.idea.devkit.composeHotReload.ComposeHotReloadCommandLinePatcher" />
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="DevKit.lang">
|
||||
@@ -729,6 +733,10 @@
|
||||
language="JAVA"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.quickfix.JavaAddServiceAnnotationProvider"/>
|
||||
</extensions>
|
||||
<applicationListeners>
|
||||
<listener class="org.jetbrains.idea.devkit.composeHotReload.ComposeHotReloadCommandLinePatcher$ComposeHotReloadRegistryListener"
|
||||
topic="com.intellij.openapi.util.registry.RegistryValueListener" />
|
||||
</applicationListeners>
|
||||
|
||||
<extensions defaultExtensionNs="JavaScript.JsonSchema">
|
||||
<ProviderFactory implementation="org.jetbrains.idea.devkit.inspections.metaInformation.MetaInformationJsonSchemaProviderFactory"/>
|
||||
|
||||
@@ -782,3 +782,6 @@ inspection.module.not.registered.as.plugin.content.fix.add=Register as content m
|
||||
line.marker.descriptors.including.content.module.name=Descriptors including this content module
|
||||
line.marker.descriptors.including.content.module.tooltip=''{0}'' content module is included in {1} plugin XML descriptors
|
||||
line.marker.descriptors.including.content.module.popup.title=Descriptors Including This Content Module
|
||||
compose.hot.reload.failed.to.download.compose.hot.reload.agent=Failed to download 'Compose Hot Reload' agent
|
||||
compose.hot.reload.restart.your.run.configuration=Compose Hot Reload agent is being downloaded to ''{0}''. Restart your run configuration after it gets completed.
|
||||
compose.hot.reload.hot.reload.agent.has.been.saved=Restart your run configuration. Compose Hot Reload agent has been saved to ''{0}''.
|
||||
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.composeHotReload
|
||||
|
||||
import com.intellij.execution.RunConfigurationExtension
|
||||
import com.intellij.execution.application.ApplicationConfiguration
|
||||
import com.intellij.execution.configurations.JavaParameters
|
||||
import com.intellij.execution.configurations.RunConfigurationBase
|
||||
import com.intellij.execution.configurations.RunnerSettings
|
||||
import com.intellij.notification.Notification
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.diagnostic.trace
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.util.registry.RegistryValue
|
||||
import com.intellij.openapi.util.registry.RegistryValueListener
|
||||
import com.intellij.openapi.wm.IdeFocusManager
|
||||
import com.intellij.util.download.DownloadableFileService
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.future.await
|
||||
import org.jetbrains.idea.devkit.DevKitBundle
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.io.path.*
|
||||
|
||||
private val logger = logger<ComposeHotReloadCommandLinePatcher>()
|
||||
|
||||
private const val COMPOSE_HOT_RELOAD_AGENT_DEFAULT_VERSION = "1.0.0-beta04"
|
||||
private const val COMPOSE_HOT_RELOAD_AGENT_FILE_PREFIX = "compose-hot-reload-agent"
|
||||
private const val HOT_RELOAD_ENABLED_REGISTRY_KEY = "devkit.compose.hot.reload.enabled"
|
||||
private const val HOT_RELOAD_AGENT_VERSION_REGISTRY_KEY = "devkit.compose.hot.reload.agent.version"
|
||||
|
||||
private val COMPOSE_HOT_RELOAD_AGENT_VERSION: String get() {
|
||||
val versionFromRegistry = try {
|
||||
Registry.stringValue(HOT_RELOAD_AGENT_VERSION_REGISTRY_KEY)
|
||||
}
|
||||
catch (_: MissingResourceException) {
|
||||
""
|
||||
}
|
||||
return versionFromRegistry.ifEmpty { COMPOSE_HOT_RELOAD_AGENT_DEFAULT_VERSION }
|
||||
}
|
||||
//https://repo1.maven.org/maven2/org/jetbrains/compose/hot-reload/hot-reload-agent/1.0.0-beta04/hot-reload-agent-1.0.0-beta04-standalone.jar
|
||||
private val COMPOSE_HOT_RELOAD_AGENT_URL get() = "https://repo1.maven.org/maven2/org/jetbrains/compose/hot-reload/hot-reload-agent/$COMPOSE_HOT_RELOAD_AGENT_VERSION/hot-reload-agent-$COMPOSE_HOT_RELOAD_AGENT_VERSION-standalone.jar"
|
||||
private val COMPOSE_HOT_RELOAD_AGENT_FILE_NAME get() = "$COMPOSE_HOT_RELOAD_AGENT_FILE_PREFIX-$COMPOSE_HOT_RELOAD_AGENT_VERSION.jar"
|
||||
|
||||
class ComposeHotReloadCommandLinePatcher : RunConfigurationExtension() {
|
||||
|
||||
class ComposeHotReloadRegistryListener : RegistryValueListener {
|
||||
override fun afterValueChanged(value: RegistryValue) {
|
||||
if (value.key == HOT_RELOAD_ENABLED_REGISTRY_KEY && value.asBoolean()) {
|
||||
val project = IdeFocusManager.getGlobalInstance().lastFocusedFrame?.project ?: ProjectManager.getInstance().openProjects.firstOrNull()
|
||||
service<ComposeHotReloadAgentHolder>().triggerDownload(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
private class ComposeHotReloadAgentHolder(val cs: CoroutineScope) {
|
||||
private val downloadJob: AtomicReference<Job?> = AtomicReference(null)
|
||||
val agentFilePath: Path get() = Path(PathManager.getTempPath(), COMPOSE_HOT_RELOAD_AGENT_FILE_NAME)
|
||||
|
||||
fun triggerDownload(project: Project?) {
|
||||
val currentAgentFilePath = agentFilePath
|
||||
if (!currentAgentFilePath.exists()) {
|
||||
logger.info("Compose Hot Reload agent not found at '$currentAgentFilePath'. Downloading it from '$COMPOSE_HOT_RELOAD_AGENT_URL'...")
|
||||
|
||||
val job = cs.launch(Dispatchers.IO, start = CoroutineStart.LAZY) {
|
||||
try {
|
||||
downloadAgent(project)
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
logger.error("Failed to download Compose Hot Reload agent", t)
|
||||
Notification("Debugger", DevKitBundle.message("compose.hot.reload.failed.to.download.compose.hot.reload.agent"), NotificationType.ERROR).notify(project)
|
||||
}
|
||||
finally {
|
||||
downloadJob.compareAndSet(coroutineContext.job, null)
|
||||
}
|
||||
}
|
||||
val prevValue = downloadJob.compareAndExchange(null, job)
|
||||
if (prevValue == null) {
|
||||
job.start()
|
||||
logger.info("Compose Hot Reload agent download job has been started")
|
||||
}
|
||||
else {
|
||||
job.cancel()
|
||||
logger.info("Compose Hot Reload agent download already in progress")
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.trace { "Compose Hot Reload agent already downloaded to '$currentAgentFilePath'" }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun downloadAgent(project: Project?) {
|
||||
val downloadingNotification = Notification("Debugger", DevKitBundle.message("compose.hot.reload.restart.your.run.configuration", agentFilePath), NotificationType.INFORMATION)
|
||||
downloadingNotification.notify(project)
|
||||
|
||||
val tempFile = Files.createTempFile(Path(PathManager.getTempPath()), COMPOSE_HOT_RELOAD_AGENT_FILE_NAME, ".tmp").apply {
|
||||
// we need only the name
|
||||
deleteIfExists()
|
||||
}
|
||||
|
||||
val fileService = DownloadableFileService.getInstance()
|
||||
val fileDescription = fileService.createFileDescription(COMPOSE_HOT_RELOAD_AGENT_URL, tempFile.fileName.pathString)
|
||||
val downloader = fileService.createDownloader(listOf(fileDescription), "Compose Hot Reload Agent")
|
||||
val downloadedFilesFuture = downloader.downloadWithBackgroundProgress(PathManager.getTempPath(), project)
|
||||
cleanOldAgents()
|
||||
val agentTempFile = downloadedFilesFuture.await()?.firstOrNull() ?: throw IllegalStateException("Failed to download Compose Hot Reload agent")
|
||||
val agentTempFilePath = Path(agentTempFile.first.path)
|
||||
try {
|
||||
agentTempFilePath.moveTo(agentFilePath, overwrite = true)
|
||||
}
|
||||
catch (_: Throwable) {
|
||||
agentTempFilePath.deleteIfExists()
|
||||
throw IllegalStateException("Failed to move Compose Hot Reload agent from '$agentTempFilePath' to '$agentFilePath'")
|
||||
}
|
||||
downloadingNotification.expire()
|
||||
Notification("Debugger", DevKitBundle.message("compose.hot.reload.hot.reload.agent.has.been.saved", agentFilePath), NotificationType.INFORMATION).notify(project)
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : RunConfigurationBase<*>?> updateJavaParameters(configuration: T & Any, params: JavaParameters, runnerSettings: RunnerSettings?) {
|
||||
val project = configuration.project
|
||||
val agentHolder = service<ComposeHotReloadAgentHolder>()
|
||||
|
||||
val agentFilePath = agentHolder.agentFilePath
|
||||
if (!agentFilePath.exists()) {
|
||||
agentHolder.triggerDownload(project)
|
||||
return
|
||||
}
|
||||
|
||||
val vmParametersList = params.vmParametersList
|
||||
vmParametersList.add("-javaagent:${agentFilePath.pathString}")
|
||||
vmParametersList.add("-Dcompose.reload.devToolsEnabled=false")
|
||||
vmParametersList.add("-XX:+AllowEnhancedClassRedefinition")
|
||||
vmParametersList.add("-Didea.dev.additional.classpath=${agentFilePath.pathString}")
|
||||
}
|
||||
|
||||
override fun isApplicableFor(configuration: RunConfigurationBase<*>): Boolean {
|
||||
return Registry.`is`(HOT_RELOAD_ENABLED_REGISTRY_KEY, false) && (configuration is ApplicationConfiguration //use this instead of 'is KotlinRunConfiguration' to avoid having dependency on Kotlin plugin here
|
||||
|| configuration.factory?.id == "JetRunConfigurationType")
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanOldAgents() {
|
||||
try {
|
||||
for (it in Path(PathManager.getTempPath()).listDirectoryEntries("$COMPOSE_HOT_RELOAD_AGENT_FILE_PREFIX-*.jar")) {
|
||||
if (it.fileName.toString() == COMPOSE_HOT_RELOAD_AGENT_FILE_NAME) continue // skip the actual version
|
||||
try {
|
||||
it.toFile().delete()
|
||||
}
|
||||
catch (_: Throwable) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (_ : Throwable) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user