[devkit] IJPL-226004 Implement dedicated executor and runner for Compose Hot Reload in monorepo

GitOrigin-RevId: 8240b41592a10dd8f579353f2f56090f72e779e0
This commit is contained in:
Yuriy Artamonov
2026-01-02 11:50:16 +00:00
committed by intellij-monorepo-bot
parent 2d1cf52cf1
commit ba388d9687
35 changed files with 290 additions and 351 deletions
@@ -18,7 +18,6 @@ public final class DevkitCoreIcons {
return IconManager.getInstance().loadRasterizedIcon(path, expUIPath, DevkitCoreIcons.class.getClassLoader(), cacheKey, flags);
}
/** 16x16 */ public static final @NotNull Icon Add_sdk = load("icons/expui/addSDK.svg", "icons/add_sdk.svg", 641117830, 2);
/** 16x16 */ public static final @NotNull Icon ComposeToolWindow = load("icons/expui/composeToolWindow.svg", 542147654, 2);
public static final class Gutter {
/** 12x12 */ public static final @NotNull Icon DescriptionFile = load("icons/expui/gutter/descriptionFile@14x14.svg", "icons/gutter/descriptionFile.svg", 1318760137, 2);
@@ -1,89 +0,0 @@
// 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;
import com.intellij.devkit.core.icons.DevkitCoreIcons;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* @deprecated Use {@link DevkitCoreIcons} instead.
*/
@Deprecated(forRemoval = true)
public final class DevKitIcons {
private DevKitIcons() {
}
/** @deprecated Use {@link DevkitCoreIcons#Add_sdk} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon Add_sdk = DevkitCoreIcons.Add_sdk;
/** @deprecated Use {@link DevkitCoreIcons#ComposeToolWindow} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon ComposeToolWindow = DevkitCoreIcons.ComposeToolWindow;
/**
* @deprecated Use {@link DevkitCoreIcons.Gutter} instead.
*/
@Deprecated(forRemoval = true)
public static final class Gutter {
private Gutter() {
}
/** @deprecated Use {@link DevkitCoreIcons.Gutter#DescriptionFile} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon DescriptionFile = DevkitCoreIcons.Gutter.DescriptionFile;
/** @deprecated Use {@link DevkitCoreIcons.Gutter#Diff} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon Diff = DevkitCoreIcons.Gutter.Diff;
/** @deprecated Use {@link DevkitCoreIcons.Gutter#Plugin} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon Plugin = DevkitCoreIcons.Gutter.Plugin;
/** @deprecated Use {@link DevkitCoreIcons.Gutter#Properties} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon Properties = DevkitCoreIcons.Gutter.Properties;
}
/** @deprecated Use {@link DevkitCoreIcons#Items} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon Items = DevkitCoreIcons.Items;
/** @deprecated Use {@link DevkitCoreIcons#LegacyPluginModule} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon LegacyPluginModule = DevkitCoreIcons.LegacyPluginModule;
/** @deprecated Use {@link DevkitCoreIcons#PluginModule} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon PluginModule = DevkitCoreIcons.PluginModule;
/** @deprecated Use {@link DevkitCoreIcons#PluginV2} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon PluginV2 = DevkitCoreIcons.PluginV2;
/** @deprecated Use {@link DevkitCoreIcons#ProjectService} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon ProjectService = DevkitCoreIcons.ProjectService;
/** @deprecated Use {@link DevkitCoreIcons#ProjectState} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon ProjectState = DevkitCoreIcons.ProjectState;
/** @deprecated Use {@link DevkitCoreIcons#RemoteMapping} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon RemoteMapping = DevkitCoreIcons.RemoteMapping;
/** @deprecated Use {@link DevkitCoreIcons#Sdk_closed} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon Sdk_closed = DevkitCoreIcons.Sdk_closed;
/** @deprecated Use {@link DevkitCoreIcons#Service} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon Service = DevkitCoreIcons.Service;
/** @deprecated Use {@link DevkitCoreIcons#State} */
@Deprecated(forRemoval = true)
public static final @NotNull Icon State = DevkitCoreIcons.State;
}
@@ -778,10 +778,6 @@
language="JAVA"
implementationClass="org.jetbrains.idea.devkit.threadingModelHelper.java.JavaLockReqPsiOps"/>
</extensions>
<applicationListeners>
<listener class="com.intellij.devkit.compose.hotreload.ComposeHotReloadCommandLinePatcher$ComposeHotReloadRegistryListener"
topic="com.intellij.openapi.util.registry.RegistryValueListener" />
</applicationListeners>
<extensions defaultExtensionNs="JavaScript.JsonSchema">
<ProviderFactory implementation="org.jetbrains.idea.devkit.inspections.metaInformation.MetaInformationJsonSchemaProviderFactory"/>
@@ -803,9 +803,6 @@ inspection.content.module.namespace.missing.fix.add.name=Add ''{0}'' namespace
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}''.
usage.type.module.in.content=In content
usage.type.module.in.dependencies=In dependencies
@@ -21,7 +21,7 @@ jvm_library(
name = "compose",
module_name = "intellij.devkit.compose",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java", "src/**/*.form"], allow_empty = True),
srcs = glob(["src/**/*.kt", "src/**/*.java", "src/**/*.form", "gen/**/*.kt", "gen/**/*.java"], allow_empty = True),
resources = [":compose_resources"],
kotlinc_opts = ":custom_compose",
deps = [
@@ -57,6 +57,7 @@ jvm_library(
"//platform/execution",
"//platform/platform-util-io:ide-util-io",
"//platform/execution-impl",
"//platform/util/jdom",
],
plugins = ["@lib//:compose-plugin"]
)
@@ -0,0 +1,22 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.devkit.compose.icons;
import com.intellij.ui.IconManager;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* NOTE THIS FILE IS AUTO-GENERATED
* DO NOT EDIT IT BY HAND, run "Generate icon classes" configuration instead
*/
public final class DevkitComposeIcons {
private static @NotNull Icon load(@NotNull String path, int cacheKey, int flags) {
return IconManager.getInstance().loadRasterizedIcon(path, DevkitComposeIcons.class.getClassLoader(), cacheKey, flags);
}
/** 16x16 */ public static final @NotNull Icon ComposeHotReload = load("icons/composeHotReload.svg", 618232810, 2);
/** 16x16 */ public static final @NotNull Icon ComposeToolWindow = load("icons/composeToolWindow.svg", 542147654, 2);
/** 16x16 */ public static final @NotNull Icon Github = load("icons/github.svg", 2052536327, 2);
/** 16x16 */ public static final @NotNull Icon JewelToolWindow = load("icons/jewelToolWindow.svg", -219486260, 2);
/** 16x16 */ public static final @NotNull Icon LockReqs = load("icons/lock-reqs.svg", 1816243125, 2);
}
@@ -26,6 +26,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="com.intellij.devkit.compose" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
@@ -61,5 +62,6 @@
<orderEntry type="module" module-name="intellij.platform.execution" />
<orderEntry type="module" module-name="intellij.platform.ide.util.io" />
<orderEntry type="module" module-name="intellij.platform.execution.impl" />
<orderEntry type="module" module-name="intellij.platform.util.jdom" />
</component>
</module>
@@ -0,0 +1,7 @@
<!-- Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.69727 0.0169841C8.84168 0.0546167 8.9625 0.154106 9.02539 0.289445L9.0332 0.306047C9.03945 0.31903 9.04911 0.339823 9.0625 0.366594C9.0895 0.42059 9.13125 0.501836 9.1875 0.605851C9.30047 0.814715 9.47272 1.11689 9.70605 1.47987C10.1736 2.2072 10.8844 3.1778 11.8535 4.14687C12.6194 4.91275 13.1685 5.58798 13.5176 6.35585C13.8393 7.06375 13.9741 7.81611 13.9951 8.73671L12.9531 8.08632C12.8959 7.5787 12.7843 7.15917 12.6074 6.76991C12.3315 6.16299 11.8803 5.58776 11.1465 4.8539C10.1159 3.82331 9.36194 2.79348 8.86523 2.02089C8.67641 1.72716 8.52655 1.46802 8.41016 1.26112C6.78055 2.61846 6 4.35139 6 6.50038C5.99981 6.71541 5.86223 6.90698 5.6582 6.97499C5.45418 7.04293 5.22874 6.97211 5.09961 6.80019L4.07031 5.42714C3.96053 5.64586 3.83431 5.90749 3.70996 6.19765C3.34196 7.0564 3 8.10492 3 9.00038C3.00016 10.7373 3.89641 11.9873 4.83301 12.8236C5.2574 13.2025 5.68461 13.4883 6.02344 13.6889C6.1107 12.1455 6.44726 11.2374 6.84961 10.7006C7.08741 10.3835 7.34272 10.2036 7.5625 10.1059C7.67059 10.0578 7.7663 10.0314 7.8418 10.017C7.87929 10.0098 7.91196 10.0057 7.93848 10.0033C7.95177 10.0021 7.9644 10.0018 7.97461 10.0014C7.97955 10.0011 7.9841 10.0005 7.98828 10.0004H7.99902L8 10.2562V11.0092C7.99133 11.0121 7.98045 11.0147 7.96875 11.0199C7.9073 11.0473 7.78731 11.1177 7.65039 11.3002C7.36894 11.6756 7 12.549 7 14.5004C6.99985 14.6669 6.91664 14.8227 6.77832 14.9154C6.63991 15.0081 6.46368 15.0265 6.30957 14.9633L6.30762 14.9623C6.30668 14.9619 6.3051 14.9609 6.30371 14.9603C6.30085 14.9591 6.29655 14.9574 6.29199 14.9555C6.28244 14.9514 6.26937 14.9452 6.25293 14.9379C6.21939 14.923 6.1715 14.9018 6.1123 14.8734C5.99346 14.8164 5.82594 14.7313 5.62695 14.6176C5.22985 14.3907 4.69923 14.0449 4.16699 13.5697C3.10379 12.6204 2.00016 11.12 2 9.00038C2 7.89618 2.40816 6.69423 2.79004 5.80312C2.984 5.35054 3.1788 4.96257 3.32422 4.68788C3.39681 4.55079 3.45786 4.44061 3.50098 4.36464C3.52237 4.32695 3.5398 4.29721 3.55176 4.27675C3.55762 4.26672 3.56213 4.25795 3.56543 4.25234C3.56681 4.25001 3.56842 4.24793 3.56934 4.24648L3.57031 4.24452L3.57129 4.24355C3.65694 4.1008 3.80836 4.00994 3.97461 4.00136C4.11996 3.99396 4.25971 4.05037 4.35938 4.1537L4.40039 4.20058L5.10352 5.1371C5.41822 3.09765 6.46603 1.40753 8.27832 0.0951091C8.39923 0.00766307 8.55285 -0.0206096 8.69727 0.0169841Z" fill="#DB3B4B"/>
<path d="M8.41016 1.26074C8.52651 1.46757 8.67649 1.72689 8.86523 2.02051C9.36191 2.79305 10.116 3.823 11.1465 4.85352C11.8802 5.58725 12.3315 6.16272 12.6074 6.76953C12.7842 7.15869 12.8959 7.57849 12.9531 8.08594L13.4805 8.41504L13.4814 8.41699L11.8232 7.38184C10.1583 6.34291 8.00041 7.53949 8 9.50195V10.1279L7.99902 10H7.98828C7.98411 10.0001 7.97954 10.0008 7.97461 10.001C7.96441 10.0014 7.95176 10.0017 7.93848 10.0029C7.91198 10.0053 7.87926 10.0095 7.8418 10.0166C7.76633 10.031 7.67053 10.0575 7.5625 10.1055C7.34277 10.2032 7.08736 10.3833 6.84961 10.7002C6.4473 11.237 6.11073 12.1453 6.02344 13.6885C5.68464 13.4879 5.25733 13.2021 4.83301 12.8232C3.89648 11.987 3.00016 10.7368 3 9C3.0001 8.10462 3.34202 7.0559 3.70996 6.19727C3.83427 5.90724 3.96057 5.6454 4.07031 5.42676L5.09961 6.7998C5.22874 6.97173 5.45418 7.04255 5.6582 6.97461C5.86217 6.90657 5.99981 6.71499 6 6.5C6.00009 4.35119 6.78064 2.618 8.41016 1.26074Z" fill="#FFF7F7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 9.50215C9 8.32441 10.2951 7.60608 11.2942 8.22967L15.2962 10.7275C16.2372 11.3149 16.2372 12.6851 15.2962 13.2725L11.2942 15.7703C10.2951 16.3939 9 15.6756 9 14.4978V9.50215ZM10.7647 9.07799C10.4317 8.87013 10 9.10957 10 9.50215V14.4978C10 14.8904 10.4317 15.1298 10.7647 14.922L14.7667 12.4241C15.0804 12.2284 15.0804 11.7716 14.7667 11.5758L10.7647 9.07799Z" fill="#208A3C"/>
<path d="M10 9.50226C10 9.10968 10.4317 8.87024 10.7647 9.0781L14.7667 11.5759C15.0804 11.7717 15.0804 12.2285 14.7667 12.4243L10.7647 14.9221C10.4317 15.13 10 14.8905 10 14.4979V9.50226Z" fill="#F2FCF3"/>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -0,0 +1,7 @@
<!-- Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.69727 0.0169841C8.84168 0.0546167 8.9625 0.154106 9.02539 0.289445L9.0332 0.306047C9.03945 0.31903 9.04911 0.339823 9.0625 0.366594C9.0895 0.42059 9.13125 0.501836 9.1875 0.605851C9.30047 0.814715 9.47272 1.11689 9.70605 1.47987C10.1736 2.2072 10.8844 3.1778 11.8535 4.14687C12.6194 4.91275 13.1685 5.58798 13.5176 6.35585C13.8393 7.06375 13.9741 7.81611 13.9951 8.73671L12.9531 8.08632C12.8959 7.5787 12.7843 7.15917 12.6074 6.76991C12.3315 6.16299 11.8803 5.58776 11.1465 4.8539C10.1159 3.82331 9.36194 2.79348 8.86523 2.02089C8.67641 1.72716 8.52655 1.46802 8.41016 1.26112C6.78055 2.61846 6 4.35139 6 6.50038C5.99981 6.71541 5.86223 6.90698 5.6582 6.97499C5.45418 7.04293 5.22874 6.97211 5.09961 6.80019L4.07031 5.42714C3.96053 5.64586 3.83431 5.90749 3.70996 6.19765C3.34196 7.0564 3 8.10492 3 9.00038C3.00016 10.7373 3.89641 11.9873 4.83301 12.8236C5.2574 13.2025 5.68461 13.4883 6.02344 13.6889C6.1107 12.1455 6.44726 11.2374 6.84961 10.7006C7.08741 10.3835 7.34272 10.2036 7.5625 10.1059C7.67059 10.0578 7.7663 10.0314 7.8418 10.017C7.87929 10.0098 7.91196 10.0057 7.93848 10.0033C7.95177 10.0021 7.9644 10.0018 7.97461 10.0014C7.97955 10.0011 7.9841 10.0005 7.98828 10.0004H7.99902L8 10.2562V11.0092C7.99133 11.0121 7.98045 11.0147 7.96875 11.0199C7.9073 11.0473 7.78731 11.1177 7.65039 11.3002C7.36894 11.6756 7 12.549 7 14.5004C6.99985 14.6669 6.91664 14.8227 6.77832 14.9154C6.63991 15.0081 6.46368 15.0265 6.30957 14.9633L6.30762 14.9623C6.30668 14.9619 6.3051 14.9609 6.30371 14.9603C6.30085 14.9591 6.29655 14.9574 6.29199 14.9555C6.28244 14.9514 6.26937 14.9452 6.25293 14.9379C6.21939 14.923 6.1715 14.9018 6.1123 14.8734C5.99346 14.8164 5.82594 14.7313 5.62695 14.6176C5.22985 14.3907 4.69923 14.0449 4.16699 13.5697C3.10379 12.6204 2.00016 11.12 2 9.00038C2 7.89618 2.40816 6.69423 2.79004 5.80312C2.984 5.35054 3.1788 4.96257 3.32422 4.68788C3.39681 4.55079 3.45786 4.44061 3.50098 4.36464C3.52237 4.32695 3.5398 4.29721 3.55176 4.27675C3.55762 4.26672 3.56213 4.25795 3.56543 4.25234C3.56681 4.25001 3.56842 4.24793 3.56934 4.24648L3.57031 4.24452L3.57129 4.24355C3.65694 4.1008 3.80836 4.00994 3.97461 4.00136C4.11996 3.99396 4.25971 4.05037 4.35938 4.1537L4.40039 4.20058L5.10352 5.1371C5.41822 3.09765 6.46603 1.40753 8.27832 0.0951091C8.39923 0.00766307 8.55285 -0.0206096 8.69727 0.0169841Z" fill="#DB5C5C"/>
<path d="M8.41016 1.26074C8.52651 1.46757 8.67649 1.72689 8.86523 2.02051C9.36191 2.79305 10.116 3.823 11.1465 4.85352C11.8802 5.58725 12.3315 6.16272 12.6074 6.76953C12.7842 7.15869 12.8959 7.57849 12.9531 8.08594L13.4805 8.41504L13.4814 8.41699L11.8232 7.38184C10.1583 6.34291 8.00041 7.53949 8 9.50195V10.1279L7.99902 10H7.98828C7.98411 10.0001 7.97954 10.0008 7.97461 10.001C7.96441 10.0014 7.95176 10.0017 7.93848 10.0029C7.91198 10.0053 7.87926 10.0095 7.8418 10.0166C7.76633 10.031 7.67053 10.0575 7.5625 10.1055C7.34277 10.2032 7.08736 10.3833 6.84961 10.7002C6.4473 11.237 6.11073 12.1453 6.02344 13.6885C5.68464 13.4879 5.25733 13.2021 4.83301 12.8232C3.89648 11.987 3.00016 10.7368 3 9C3.0001 8.10462 3.34202 7.0559 3.70996 6.19727C3.83427 5.90724 3.96057 5.6454 4.07031 5.42676L5.09961 6.7998C5.22874 6.97173 5.45418 7.04255 5.6582 6.97461C5.86217 6.90657 5.99981 6.71499 6 6.5C6.00009 4.35119 6.78064 2.618 8.41016 1.26074Z" fill="#402929"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 9.50215C9 8.32441 10.2951 7.60608 11.2942 8.22967L15.2962 10.7275C16.2372 11.3149 16.2372 12.6851 15.2962 13.2725L11.2942 15.7703C10.2951 16.3939 9 15.6756 9 14.4978V9.50215ZM10.7647 9.07799C10.4317 8.87013 10 9.10957 10 9.50215V14.4978C10 14.8904 10.4317 15.1298 10.7647 14.922L14.7667 12.4241C15.0804 12.2284 15.0804 11.7716 14.7667 11.5758L10.7647 9.07799Z" fill="#57965C"/>
<path d="M10 9.50226C10 9.10968 10.4317 8.87024 10.7647 9.0781L14.7667 11.5759C15.0804 11.7717 15.0804 12.2285 14.7667 12.4243L10.7647 14.9221C10.4317 15.13 10 14.8905 10 14.4979V9.50226Z" fill="#253627"/>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
skip: *20x20.svg
@@ -1,6 +0,0 @@
<svg fill="none" height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg">
<g stroke="#6c707e" stroke-width="1.5">
<path d="m1.06066 10 8.93934-8.93934 8.9393 8.93934-8.9393 8.9393z" stroke-linejoin="round"/>
<path d="m10 1v18m-9-9h18"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 295 B

@@ -1,6 +0,0 @@
<svg fill="none" height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg">
<g stroke="#ced0d6" stroke-width="1.5">
<path d="m1.06066 10 8.93934-8.93934 8.9393 8.93934-8.9393 8.9393z" stroke-linejoin="round"/>
<path d="m10 1v18m-9-9h18"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 295 B

@@ -27,27 +27,28 @@
<registryKey key="devkit.compose.example.settings.page" defaultValue="false"
description="Enable example Settings page implemented with Compose UI"/>
<editorNotificationProvider implementation="com.intellij.devkit.compose.hotreload.ComposeHotReloadSuggester"/>
<lang.documentationProvider language="kotlin" implementationClass="com.intellij.devkit.compose.hotreload.ComposeHotReloadTips"/>
<editorNotificationProvider implementation="com.intellij.devkit.compose.hotreload.DevkitHotReloadSuggester"/>
<lang.documentationProvider language="kotlin" implementationClass="com.intellij.devkit.compose.hotreload.DevkitHotReloadTips"/>
<editorFactoryListener implementation="com.intellij.devkit.compose.hotreload.ToggleComposeSandboxTipsEditorListener"/>
<registryKey key="devkit.compose.desktop.preview" defaultValue="true"
restartRequired="true" description="Enables Compose UI previews tool window"/>
<library.toolWindow id="ComposeUIPreview" anchor="right"
factoryClass="com.intellij.devkit.compose.preview.ComposePreviewToolWindowFactory"
icon="com.intellij.devkit.core.icons.DevkitCoreIcons.ComposeToolWindow"
icon="com.intellij.devkit.compose.icons.DevkitComposeIcons.ComposeToolWindow"
librarySearchClass="com.intellij.devkit.compose.preview.ComposePreviewSearcher"/>
<implicitUsageProvider implementation="com.intellij.devkit.compose.preview.ComposePreviewImplicitUsageProvider"/>
<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="com.intellij.devkit.compose.hotreload.ComposeHotReloadCommandLinePatcher" />
<runConfigurationExtension implementation="com.intellij.devkit.compose.hotreload.DevkitHotReloadCommandLinePatcher" />
<executor implementation="com.intellij.devkit.compose.hotreload.DevkitHotReloadExecutor"/>
<programRunner implementation="com.intellij.devkit.compose.hotreload.DevkitHotReloadRunner"/>
</extensions>
<actions>
<group id="ComposeToolsGroup" searchable="false">
<action id="JewelToolwindow" class="com.intellij.devkit.compose.demo.dialog.JewelToolwindowAction"
icon="com.intellij.devkit.core.icons.DevkitCoreIcons.ComposeToolWindow"/>
icon="com.intellij.devkit.compose.icons.DevkitComposeIcons.ComposeToolWindow"/>
<action id="JewelComponentShowcaseDialog" class="com.intellij.devkit.compose.demo.dialog.ComponentShowcaseDialogAction"
icon="com.intellij.devkit.core.icons.DevkitCoreIcons.Items"/>
<action id="JewelWizardDialog" class="com.intellij.devkit.compose.demo.dialog.JewelWizardDialogAction"/>
@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.707107 8L8 0.707107L15.2929 8L8 15.2929L0.707107 8Z" stroke="#6C707E" stroke-linejoin="round"/>
<path d="M8 0.799988V15.2M0.800003 7.99999H15.2" stroke="#6C707E"/>
</svg>

Before

Width:  |  Height:  |  Size: 279 B

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.707107 8L8 0.707107L15.2929 8L8 15.2929L0.707107 8Z" stroke="#CED0D6" stroke-linejoin="round"/>
<path d="M8 0.799988V15.2M0.800003 7.99999H15.2" stroke="#CED0D6"/>
</svg>

Before

Width:  |  Height:  |  Size: 279 B

@@ -48,9 +48,8 @@ jewel.swing.text.areas=Text areas:
compose.sandbox=Compose Sandbox
compose.sandbox.show.automatically.on.project.open=Show automatically on project open
configurable.name.settings.page.on.compose=Settings Page on Compose
label.compose.hot.reload.available.banner=Compose Hot Reload is available. Enjoy!
link.label.enable.hot.reload=Enable Hot Reload
link.label.dismiss=Dismiss
label.compose.hot.reload.available.banner=Hot Reload is available. Use the 'Run with Compose Hot Reload' run action.
link.label.dismiss=Got it!
toolwindow.stripe.ComposeUIPreview=Compose UI Preview
compose.preview.empty.text.top=Preview will appear here \u1F300.
@@ -71,4 +70,10 @@ action.ComposePreviewRefreshAuto.description=Toggles automatic refresh on change
action.LockReqAnalysis.text=Analyze Lock Requirements
progress.title.analyzing.lock.requirements=Analyzing lock requirements
tab.title.locking.requirements=Locking Requirements
action.DevkitComposeHotReloadExecutorAction.text=Run with Compose Hot Reload
action.DevkitComposeHotReloadExecutorAction.description=Run with Compose Hot Reload
action.DevkitComposeHotReloadExecutorAction.text.0=Run ''{0}'' with Compose Hot Reload
action.JewelCustomPopupRenderer.text=Use Jewel Custom Popup Renderer
compose.hot.reload.failed.to.download.compose.hot.reload.agent=Failed to download 'Compose Hot Reload' agent
@@ -4,5 +4,5 @@ import org.jetbrains.jewel.ui.icon.PathIconKey
internal object IdeSampleIconKeys {
val gitHub = PathIconKey("icons/github.svg", IdeSampleIconKeys::class.java)
val jewelToolWindow = PathIconKey("icons/jewel-tool-window.svg", IdeSampleIconKeys::class.java)
val jewelToolWindow = PathIconKey("icons/jewelToolWindow.svg", IdeSampleIconKeys::class.java)
}
@@ -1,8 +0,0 @@
package com.intellij.devkit.compose.demo
import com.intellij.openapi.util.IconLoader
internal object JewelIcons {
@JvmField
val ToolWindowIcon = IconLoader.getIcon("/icons/jewel-tool-window.svg", javaClass)
}
@@ -12,6 +12,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.intellij.devkit.compose.DevkitComposeBundle
import com.intellij.devkit.compose.icons.DevkitComposeIcons
import com.intellij.icons.AllIcons
import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI
import com.intellij.openapi.editor.colors.EditorFontType
@@ -212,7 +213,7 @@ internal class SwingComparisonTabPanel : BorderLayoutPanel() {
private fun Panel.iconsRow() {
row(DevkitComposeBundle.message("jewel.swing.icons")) {
cell(JBLabel(JewelIcons.ToolWindowIcon).apply { border = JBUI.Borders.customLine(JBColor.RED) })
cell(JBLabel(DevkitComposeIcons.JewelToolWindow).apply { border = JBUI.Borders.customLine(JBColor.RED) })
.align(AlignY.CENTER)
compose {
@@ -2,7 +2,7 @@
package com.intellij.devkit.compose.demo.dialog
import com.intellij.devkit.compose.DevkitComposeBundle
import com.intellij.devkit.core.icons.DevkitCoreIcons
import com.intellij.devkit.compose.icons.DevkitComposeIcons
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.DumbAwareAction
@@ -41,7 +41,7 @@ internal class JewelToolwindowAction : DumbAwareAction() {
id = TOOLWINDOW_ID,
anchor = ToolWindowAnchor.BOTTOM,
component = null,
icon = DevkitCoreIcons.ComposeToolWindow,
icon = DevkitComposeIcons.ComposeToolWindow,
contentFactory = com.intellij.devkit.compose.demo.JewelDemoToolWindowFactory(),
stripeTitle = DevkitComposeBundle.messagePointer("jewel.toolwindow.title"),
)
@@ -28,7 +28,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.intellij.devkit.compose.demo.JewelIcons
import com.intellij.devkit.compose.icons.DevkitComposeIcons
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.util.ui.JBUI
@@ -332,7 +332,7 @@ private fun RightColumn(selectedItem: ContentItem?, modifier: Modifier = Modifie
@Composable
private fun ReleaseImage(imagePath: String) {
val painterProvider = rememberResourcePainterProvider(imagePath, JewelIcons::class.java)
val painterProvider = rememberResourcePainterProvider(imagePath, DevkitComposeIcons::class.java)
val painter by painterProvider.getPainter()
val controller = rememberFoilInteractionController()
@@ -1,186 +0,0 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.devkit.compose.hotreload
import com.intellij.execution.JavaRunConfigurationBase
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.IntelliJProjectUtil
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.1.0-alpha03"
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"
internal 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?) {
if (!IntelliJProjectUtil.isIntelliJPlatformProject(configuration.project)) return
if (configuration !is JavaRunConfigurationBase) return
if (params.mainClass != "org.jetbrains.intellij.build.devServer.DevMainKt"
&& params.mainClass != "com.intellij.idea.Main") {
// only IDE build configurations supported here so far
return
}
val module = configuration.configurationModule.module ?: return
val jdk = JavaParameters.getJdkToRunModule(module, true) ?: return
val versionString = jdk.versionString ?: ""
if (!versionString.contains("JetBrains Runtime")) {
// we may only expect -XX:+AllowEnhancedClassRedefinition on JBR
return
}
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("-Dcompose.reload.staticsReinitializeMode=AllDirty")
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
}
}
@@ -0,0 +1,208 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.devkit.compose.hotreload
import com.intellij.debugger.impl.GenericDebuggerRunner
import com.intellij.devkit.compose.DevkitComposeBundle
import com.intellij.devkit.compose.icons.DevkitComposeIcons
import com.intellij.execution.ExecutionException
import com.intellij.execution.Executor
import com.intellij.execution.JavaRunConfigurationBase
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.RunProfile
import com.intellij.execution.configurations.RunnerSettings
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.IntelliJProjectUtil.isIntelliJPlatformProject
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NlsActions.ActionDescription
import com.intellij.openapi.util.NlsActions.ActionText
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.wm.ToolWindowId
import com.intellij.util.download.DownloadableFileService
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NonNls
import java.nio.file.Files
import java.nio.file.Path
import javax.swing.Icon
import kotlin.io.path.absolutePathString
import kotlin.io.path.exists
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.pathString
private const val COMPOSE_HOT_RELOAD_AGENT_DEFAULT_VERSION = "1.1.0-alpha03"
private const val COMPOSE_HOT_RELOAD_AGENT_FILE_PREFIX = "agent"
private const val HOT_RELOAD_AGENT_VERSION_REGISTRY_KEY = "devkit.compose.hot.reload.agent.version"
private val LOG = logger<DevkitHotReloadCommandLinePatcher>()
private val COMPOSE_HOT_RELOAD_AGENT_VERSION: String
get() {
val versionFromRegistry = Registry.stringValue(HOT_RELOAD_AGENT_VERSION_REGISTRY_KEY)
return versionFromRegistry.ifEmpty { COMPOSE_HOT_RELOAD_AGENT_DEFAULT_VERSION }
}
private val COMPOSE_HOT_RELOAD_AGENT_URL: String
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: String
get() = "$COMPOSE_HOT_RELOAD_AGENT_FILE_PREFIX-$COMPOSE_HOT_RELOAD_AGENT_VERSION.jar"
private val agentFilePath: Path
get() = getAgentTempPath().resolve(COMPOSE_HOT_RELOAD_AGENT_FILE_NAME)
@Throws(ExecutionException::class)
internal fun ensureAgentDownloaded(project: Project?) {
val currentAgentFilePath = agentFilePath
if (!currentAgentFilePath.exists()) {
LOG.info("Compose Hot Reload agent not found at '$currentAgentFilePath'. Downloading it from '$COMPOSE_HOT_RELOAD_AGENT_URL'...")
try {
downloadAgentFile(project)
}
catch (t: Throwable) {
throw ExecutionException(DevkitComposeBundle.message("compose.hot.reload.failed.to.download.compose.hot.reload.agent"), t)
}
}
else {
LOG.debug("Compose Hot Reload agent already downloaded to '$currentAgentFilePath'")
}
}
private fun downloadAgentFile(project: Project?): Path? {
val fileService = DownloadableFileService.getInstance()
val fileDescription = fileService.createFileDescription(COMPOSE_HOT_RELOAD_AGENT_URL, COMPOSE_HOT_RELOAD_AGENT_FILE_NAME)
val downloader = fileService.createDownloader(listOf(fileDescription), "Compose Hot Reload Agent")
Files.createDirectories(getAgentTempPath())
downloader.downloadFilesWithProgress(getAgentTempPath().absolutePathString(), project, null)
cleanOldAgents()
return getAgentTempPath().resolve(COMPOSE_HOT_RELOAD_AGENT_FILE_NAME)
.takeIf { it.exists() }
}
private const val DEVKIT_HOT_RELOAD_EXECUTOR_ID = "DevkitComposeHotReloadExecutor"
internal class DevkitHotReloadExecutor : Executor() {
override fun getId(): @NonNls String = DEVKIT_HOT_RELOAD_EXECUTOR_ID
override fun getContextActionId(): @NonNls String = id
override fun getToolWindowId(): String = ToolWindowId.DEBUG
override fun getToolWindowIcon(): Icon = DevkitComposeIcons.ComposeHotReload
override fun getIcon(): Icon = DevkitComposeIcons.ComposeHotReload
override fun getDisabledIcon(): Icon? = null
@Suppress("DialogTitleCapitalization")
override fun getDescription(): @ActionDescription String = DevkitComposeBundle.message("action.DevkitComposeHotReloadExecutorAction.description")
override fun getActionName(): @ActionText String = DevkitComposeBundle.message("action.DevkitComposeHotReloadExecutorAction.text")
override fun getStartActionText(): @Nls(capitalization = Nls.Capitalization.Title) String {
return DevkitComposeBundle.message("action.DevkitComposeHotReloadExecutorAction.text")
}
override fun getStartActionText(configurationName: @NlsSafe String): @Nls(capitalization = Nls.Capitalization.Title) String {
if (configurationName.isEmpty()) return startActionText
val configName = shortenNameIfNeeded(configurationName)
return DevkitComposeBundle.message("action.DevkitComposeHotReloadExecutorAction.text.0", configName)
}
override fun getHelpId(): @NonNls String? = null
override fun isApplicable(project: Project): Boolean {
return isIntelliJPlatformProject(project)
}
}
internal class DevkitHotReloadRunner : GenericDebuggerRunner() {
override fun getRunnerId(): String = "DevkitHotReloadRunner"
override fun canRun(executorId: String, profile: RunProfile): Boolean {
return executorId == DEVKIT_HOT_RELOAD_EXECUTOR_ID
}
@Throws(ExecutionException::class)
override fun execute(environment: ExecutionEnvironment) {
ensureAgentDownloaded(environment.project)
super.execute(environment)
}
}
internal class DevkitHotReloadCommandLinePatcher : RunConfigurationExtension() {
override fun <T : RunConfigurationBase<*>?> updateJavaParameters(
configuration: T & Any,
params: JavaParameters,
runnerSettings: RunnerSettings?,
) {
// only update parameters for our executor, here we don't know it
}
override fun <T : RunConfigurationBase<*>?> updateJavaParameters(
configuration: T & Any,
params: JavaParameters,
runnerSettings: RunnerSettings?,
executor: Executor
) {
if (executor.id != DEVKIT_HOT_RELOAD_EXECUTOR_ID) return
if (!isIntelliJPlatformProject(configuration.project)) return
if (configuration !is JavaRunConfigurationBase) return
if (params.mainClass != "org.jetbrains.intellij.build.devServer.DevMainKt"
&& params.mainClass != "com.intellij.idea.Main") {
// only IDE build configurations supported here so far
return
}
val module = configuration.configurationModule.module ?: return
val jdk = JavaParameters.getJdkToRunModule(module, true) ?: return
val versionString = jdk.versionString ?: ""
if (!versionString.contains("JetBrains Runtime")) {
// we may only expect -XX:+AllowEnhancedClassRedefinition on JBR
return
}
val vmParametersList = params.vmParametersList
vmParametersList.add("-javaagent:${agentFilePath.pathString}")
vmParametersList.add("-Dcompose.reload.devToolsEnabled=false")
vmParametersList.add("-Dcompose.reload.staticsReinitializeMode=AllDirty")
vmParametersList.add("-XX:+AllowEnhancedClassRedefinition")
vmParametersList.add("-Didea.dev.additional.classpath=${agentFilePath.pathString}")
}
override fun isApplicableFor(configuration: RunConfigurationBase<*>): Boolean {
//use this instead of 'is KotlinRunConfiguration' to avoid having dependency on Kotlin plugin here
return configuration is ApplicationConfiguration
|| configuration.factory?.id == "JetRunConfigurationType"
}
}
private fun getAgentTempPath(): Path {
return PathManager.getTempDir().resolve("devkit-hot-reload")
}
private fun cleanOldAgents() {
val agentTempPath = getAgentTempPath()
if (!agentTempPath.exists()) return
try {
for (it in agentTempPath.listDirectoryEntries("$COMPOSE_HOT_RELOAD_AGENT_FILE_PREFIX-*.jar")) {
if (it.fileName.toString() == COMPOSE_HOT_RELOAD_AGENT_FILE_NAME) continue // skip the actual version
try {
Files.delete(it)
}
catch (t: Throwable) {
// ignore
LOG.warn("Failed to delete old Compose Hot Reload agent file", t)
}
}
}
catch (_: Throwable) {
// ignore
}
}
@@ -3,15 +3,14 @@ package com.intellij.devkit.compose.hotreload
import com.intellij.devkit.compose.DevkitComposeBundle
import com.intellij.devkit.compose.hasCompose
import com.intellij.devkit.compose.icons.DevkitComposeIcons
import com.intellij.facet.FacetManager
import com.intellij.icons.AllIcons
import com.intellij.ide.util.PropertiesComponent
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.IntelliJProjectUtil.isIntelliJPlatformProject
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.util.registry.RegistryManager
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
import com.intellij.ui.EditorNotificationPanel
@@ -24,23 +23,26 @@ import java.util.function.Function
import javax.swing.JComponent
private const val COMPOSE_HOT_RELOAD_ENABLED_MARKER = "plugin:androidx.compose.compiler.plugins.kotlin:generateFunctionKeyMetaAnnotations"
private const val REGISTRY_KEY = "devkit.compose.hot.reload.enabled"
internal class ComposeHotReloadSuggester : EditorNotificationProvider, DumbAware {
private val SUGGESTION_DISMISSED_KEY = Key.create<Boolean>("HOT_RELOAD_SUGGESTION_DISMISSED")
internal class DevkitHotReloadSuggester : EditorNotificationProvider, DumbAware {
private val SUGGESTION_DISMISSED_KEY = "COMPOSE_HOT_RELOAD_GOT_IT"
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
if (Registry.`is`(REGISTRY_KEY)) return null
if (!isIntelliJPlatformProject(project)) return null
if (!hasCompose(project)) return null
if (isSuggestionDismissed(file)) return null
if (isSuggestionDismissed(project)) return null
if (!isComposeUiFile(project, file)) return null
return Function { editor -> Banner(project, editor.file) }
return Function { _ -> Banner(project) }
}
private fun isSuggestionDismissed(file: VirtualFile): Boolean {
return file.getUserData(SUGGESTION_DISMISSED_KEY) != null
private fun isSuggestionDismissed(project: Project): Boolean {
return PropertiesComponent.getInstance(project).getBoolean(SUGGESTION_DISMISSED_KEY)
}
private fun dismissSuggestion(project: Project) {
PropertiesComponent.getInstance(project).setValue(SUGGESTION_DISMISSED_KEY, true)
}
private fun isComposeUiFile(project: Project, file: VirtualFile): Boolean {
@@ -67,21 +69,13 @@ internal class ComposeHotReloadSuggester : EditorNotificationProvider, DumbAware
&& segments[2].identifier == "runtime"
}
private inner class Banner(val project: Project, val file: VirtualFile) : EditorNotificationPanel(Status.Success) {
private inner class Banner(val project: Project) : EditorNotificationPanel(Status.Success) {
init {
@Suppress("DialogTitleCapitalization")
text = DevkitComposeBundle.message("label.compose.hot.reload.available.banner")
icon(AllIcons.Debugger.DebuggerSync)
@Suppress("DialogTitleCapitalization")
createActionLabel(DevkitComposeBundle.message("link.label.enable.hot.reload")) {
RegistryManager.getInstance().get(REGISTRY_KEY).setValue(true)
EditorNotifications.getInstance(project).updateAllNotifications()
}
icon(DevkitComposeIcons.ComposeHotReload)
createActionLabel(DevkitComposeBundle.message("link.label.dismiss")) {
file.putCopyableUserData(SUGGESTION_DISMISSED_KEY, true)
dismissSuggestion(project)
EditorNotifications.getInstance(project).updateAllNotifications()
}
@@ -16,7 +16,7 @@ import java.util.function.Consumer
private const val tipPrefix: String = "//TIP"
private const val MAGIC_SANDBOX_FILENAME = "ComposeSandbox.kt"
internal class ComposeHotReloadTips : com.intellij.lang.documentation.DocumentationProvider {
internal class DevkitHotReloadTips : com.intellij.lang.documentation.DocumentationProvider {
private val commentTokenType: IElementType = KtTokens.EOL_COMMENT