[devkit] IJPL-226004 Implement dedicated executor and runner for Compose Hot Reload in monorepo
GitOrigin-RevId: 8240b41592a10dd8f579353f2f56090f72e779e0
@@ -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 |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 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 |
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 279 B |
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 279 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
|
||||
@@ -0,0 +1 @@
|
||||
skip: *
|
||||
@@ -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
|
||||
|
||||