From e74d9cb93942bc95789b23a773a5e2013116bf09 Mon Sep 17 00:00:00 2001
From: Victor Kropp
Date: Mon, 19 May 2025 10:54:47 +0200
Subject: [PATCH 1/2] CMP-7958 deprecate IJ plugin in favor of Kotlin
Multiplatform plugin
---
idea-plugin/build.gradle.kts | 2 +-
.../compose/ComposePluginReplacement.kt | 5 +++++
.../src/main/resources/META-INF/plugin.xml | 21 ++++++++++++-------
3 files changed, 20 insertions(+), 8 deletions(-)
create mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/ComposePluginReplacement.kt
diff --git a/idea-plugin/build.gradle.kts b/idea-plugin/build.gradle.kts
index 63a14fd5285..086de86f516 100644
--- a/idea-plugin/build.gradle.kts
+++ b/idea-plugin/build.gradle.kts
@@ -34,7 +34,7 @@ dependencies {
intellijPlatform {
pluginConfiguration {
- name = "Compose Multiplatform IDE Support"
+ name = "Compose Multiplatform for Desktop IDE Support"
ideaVersion {
sinceBuild = "242.20224"
untilBuild = provider { null }
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/ComposePluginReplacement.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/ComposePluginReplacement.kt
new file mode 100644
index 00000000000..272d6e008a1
--- /dev/null
+++ b/idea-plugin/src/main/kotlin/org/jetbrains/compose/ComposePluginReplacement.kt
@@ -0,0 +1,5 @@
+package org.jetbrains.compose
+
+import com.intellij.ide.plugins.PluginReplacement
+
+class ComposePluginReplacement : PluginReplacement("com.jetbrains.kmm")
\ No newline at end of file
diff --git a/idea-plugin/src/main/resources/META-INF/plugin.xml b/idea-plugin/src/main/resources/META-INF/plugin.xml
index 6accf89e542..d26150d4a39 100644
--- a/idea-plugin/src/main/resources/META-INF/plugin.xml
+++ b/idea-plugin/src/main/resources/META-INF/plugin.xml
@@ -1,16 +1,20 @@
org.jetbrains.compose.desktop.ide
- Compose Multiplatform IDE Support
+ Compose Multiplatform for Desktop IDE SupportJetBrains
Compose for Desktop
- applications.
- The main feature at the moment is IDE preview of composable functions
- marked by @Preview annotation.
+
⚠️ This plugin is in maintenance mode
+
The Compose Multiplatform for Desktop IDE Support plugin is no longer under active development.
+Its functionality is being gradually integrated into the new Kotlin Multiplatform plugin,
+which already includes support for Compose Previews on macOS.
+
What this means for you:
+
+
On macOS: We recommend switching to the Kotlin Multiplatform plugin, which now includes improved Compose Previews support.
+
On Windows/Linux: You can continue using this plugin. It remains available and maintained for now, until equivalent support becomes available in the Kotlin Multiplatform plugin.
+
+
No new features will be added, but we’ll address critical issues as needed during this transition.
]]>
-
@@ -27,7 +31,10 @@
+ com.jetbrains.kmm
+
+
From ad4a308882ee449ffb2d565b3681197ba5413ae4 Mon Sep 17 00:00:00 2001
From: Victor Kropp
Date: Wed, 1 Oct 2025 13:50:35 +0200
Subject: [PATCH 2/2] CMP-9002 Really deprecate IJ plugin
---
idea-plugin/build.gradle.kts | 4 +-
idea-plugin/gradle/libs.versions.toml | 6 +-
.../jetbrains/compose/common/intellijUtils.kt | 12 --
.../preview/ConfigurePreviewTaskNameCache.kt | 94 ----------
.../desktop/ide/preview/PreviewActions.kt | 92 ---------
.../desktop/ide/preview/PreviewEntryPoint.kt | 55 ------
.../preview/PreviewFloatingToolbarProvider.kt | 79 --------
.../desktop/ide/preview/PreviewIcons.kt | 15 --
.../desktop/ide/preview/PreviewLocation.kt | 28 ---
.../desktop/ide/preview/PreviewMessages.kt | 10 -
.../desktop/ide/preview/PreviewRootLogger.kt | 11 --
.../PreviewRunLineMarkerContributor.kt | 47 -----
.../ide/preview/PreviewStateService.kt | 135 --------------
.../desktop/ide/preview/PreviewToolWindow.kt | 36 ----
.../desktop/ide/preview/editorUtils.kt | 41 -----
.../ide/preview/ijCompatibilityUtils.kt | 45 -----
.../desktop/ide/preview/locationUtils.kt | 138 --------------
.../desktop/ide/preview/ui/PreviewPanel.kt | 89 ---------
.../compose/desktop/ide/preview/ui/uiUtils.kt | 39 ----
.../compose/inspections/ComposeSuppressor.kt | 41 -----
.../ide/run/WebRunConfigurationProducer.kt | 56 ------
.../ide/run/WebRunLineMarkerContributor.kt | 23 ---
.../jetbrains/compose/web/ide/run/psiUtils.kt | 174 ------------------
.../src/main/resources/META-INF/plugin.xml | 39 +---
.../main/resources/icons/compose/compose.svg | 1 -
.../resources/icons/compose/runPreview.svg | 4 -
26 files changed, 7 insertions(+), 1307 deletions(-)
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/common/intellijUtils.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ConfigurePreviewTaskNameCache.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewActions.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewEntryPoint.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewFloatingToolbarProvider.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewIcons.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewLocation.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewMessages.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewRootLogger.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewRunLineMarkerContributor.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewStateService.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewToolWindow.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/editorUtils.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ijCompatibilityUtils.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/locationUtils.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ui/PreviewPanel.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ui/uiUtils.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/inspections/ComposeSuppressor.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/WebRunConfigurationProducer.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/WebRunLineMarkerContributor.kt
delete mode 100644 idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/psiUtils.kt
delete mode 100644 idea-plugin/src/main/resources/icons/compose/compose.svg
delete mode 100644 idea-plugin/src/main/resources/icons/compose/runPreview.svg
diff --git a/idea-plugin/build.gradle.kts b/idea-plugin/build.gradle.kts
index 086de86f516..7f0260fa76a 100644
--- a/idea-plugin/build.gradle.kts
+++ b/idea-plugin/build.gradle.kts
@@ -24,8 +24,8 @@ dependencies {
implementation("org.jetbrains.compose:preview-rpc")
intellijPlatform {
+ @Suppress("DEPRECATION")
intellijIdeaCommunity(libs.versions.idea)
- instrumentationTools()
pluginVerifier()
bundledPlugins("com.intellij.java", "org.jetbrains.kotlin", "com.intellij.gradle")
@@ -36,7 +36,7 @@ intellijPlatform {
pluginConfiguration {
name = "Compose Multiplatform for Desktop IDE Support"
ideaVersion {
- sinceBuild = "242.20224"
+ sinceBuild = "252.26199"
untilBuild = provider { null }
}
}
diff --git a/idea-plugin/gradle/libs.versions.toml b/idea-plugin/gradle/libs.versions.toml
index b4f88efcb38..24c7e96f089 100644
--- a/idea-plugin/gradle/libs.versions.toml
+++ b/idea-plugin/gradle/libs.versions.toml
@@ -1,7 +1,7 @@
[versions]
-kotlin = "1.9.23"
-ideaPlugin = "2.1.0"
-idea = "2024.2.1"
+kotlin = "2.1.21"
+ideaPlugin = "2.9.0"
+idea = "2025.2.2"
changelog = "2.2.0"
[plugins]
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/common/intellijUtils.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/common/intellijUtils.kt
deleted file mode 100644
index 68c32de2263..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/common/intellijUtils.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.common
-
-import com.intellij.execution.actions.ConfigurationContext
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
-
-internal fun ConfigurationContext.modulePath(): String? =
- ExternalSystemApiUtil.getExternalProjectPath(location?.module)
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ConfigurePreviewTaskNameCache.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ConfigurePreviewTaskNameCache.kt
deleted file mode 100644
index 83cd4ad3454..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ConfigurePreviewTaskNameCache.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.externalSystem.model.DataNode
-import com.intellij.openapi.externalSystem.model.ProjectKeys
-import com.intellij.openapi.externalSystem.model.project.ModuleData
-import com.intellij.openapi.externalSystem.service.project.ProjectDataManager
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
-import com.intellij.openapi.module.Module
-import com.intellij.openapi.project.Project
-import com.intellij.util.concurrency.annotations.RequiresReadLock
-import org.jetbrains.plugins.gradle.settings.GradleSettings
-import org.jetbrains.plugins.gradle.util.GradleConstants
-import java.util.Locale
-
-internal val DEFAULT_CONFIGURE_PREVIEW_TASK_NAME = "configureDesktopPreview"
-
-internal interface ConfigurePreviewTaskNameProvider {
- @RequiresReadLock
- fun configurePreviewTaskNameOrNull(module: Module): String?
-}
-
-internal class ConfigurePreviewTaskNameProviderImpl : ConfigurePreviewTaskNameProvider {
- @RequiresReadLock
- override fun configurePreviewTaskNameOrNull(module: Module): String? {
- val modulePath = ExternalSystemApiUtil.getExternalProjectPath(module) ?: return null
- val moduleNode = moduleDataNodeOrNull(module.project, modulePath)
- if (moduleNode != null) {
- val target = ExternalSystemApiUtil.getChildren(moduleNode, kotlinTargetDataKey).singleOrNull()
- if (target != null) {
- return previewTaskName(target.data.externalName)
- }
- }
-
- return null
- }
-
- private fun previewTaskName(targetName: String = ""): String {
- val capitalizedTargetName =
- targetName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
- return "$DEFAULT_CONFIGURE_PREVIEW_TASK_NAME$capitalizedTargetName"
- }
-
- private fun moduleDataNodeOrNull(project: Project, modulePath: String): DataNode? {
- val projectDataManager = ProjectDataManager.getInstance()
- for (settings in GradleSettings.getInstance(project).linkedProjectsSettings) {
- val projectInfo = projectDataManager.getExternalProjectData(project, GradleConstants.SYSTEM_ID, settings.externalProjectPath)
- val projectNode = projectInfo?.externalProjectStructure ?: continue
- val moduleNodes = ExternalSystemApiUtil.getChildren(projectNode, ProjectKeys.MODULE)
- for (moduleNode in moduleNodes) {
- val externalProjectPath = moduleNode.data.linkedExternalProjectPath
- if (externalProjectPath == modulePath) {
- return moduleNode
- }
- }
- }
- return null
- }
-}
-
-internal class ConfigurePreviewTaskNameCache(
- private val provider: ConfigurePreviewTaskNameProvider
-) : ConfigurePreviewTaskNameProvider {
- private var cachedModuleId: String? = null
- private var cachedTaskName: String? = null
-
- @RequiresReadLock
- override fun configurePreviewTaskNameOrNull(module: Module): String? {
- val externalProjectPath = ExternalSystemApiUtil.getExternalProjectPath(module) ?: return null
- val moduleId = "$externalProjectPath#${module.name}"
-
- synchronized(this) {
- if (moduleId == cachedModuleId) return cachedTaskName
- }
-
- val taskName = provider.configurePreviewTaskNameOrNull(module)
- synchronized(this) {
- cachedTaskName = taskName
- cachedModuleId = moduleId
- }
- return taskName
- }
-
- fun invalidate() {
- synchronized(this) {
- cachedModuleId = null
- cachedTaskName = null
- }
- }
-}
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewActions.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewActions.kt
deleted file mode 100644
index 93851ff38c7..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewActions.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.execution.executors.DefaultRunExecutor
-import com.intellij.openapi.actionSystem.AnAction
-import com.intellij.openapi.actionSystem.AnActionEvent
-import com.intellij.openapi.actionSystem.CommonDataKeys
-import com.intellij.openapi.application.ReadAction
-import com.intellij.openapi.components.service
-import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings
-import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode
-import com.intellij.openapi.externalSystem.task.TaskCallback
-import com.intellij.openapi.externalSystem.util.ExternalSystemUtil.runTask
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.util.UserDataHolderBase
-import com.intellij.openapi.wm.ToolWindowManager
-import org.jetbrains.plugins.gradle.settings.GradleSettings
-import org.jetbrains.plugins.gradle.util.GradleConstants
-import javax.swing.SwingUtilities
-
-class RunPreviewAction(
- private val previewLocation: PreviewLocation
-) : AnAction({ "Show non-interactive preview" }, PreviewIcons.RUN_PREVIEW) {
- override fun actionPerformed(e: AnActionEvent) {
- val project = e.project ?: return
- buildPreviewViaGradle(project, previewLocation)
- }
-}
-
-internal const val PREVIEW_EDITOR_TOOLBAR_GROUP_ID = "Compose.Desktop.Preview.Editor.Toolbar"
-
-class RefreshOrRunPreviewAction : AnAction(PreviewIcons.COMPOSE) {
- override fun actionPerformed(e: AnActionEvent) {
- val project = e.project ?: return
- val previewLocation = ReadAction.compute {
- val editor = e.dataContext.getData(CommonDataKeys.EDITOR)
- if (editor != null) {
- e.presentation.isEnabled = false
- parentPreviewAtCaretOrNull(editor)
- } else null
- }
- if (previewLocation != null) {
- buildPreviewViaGradle(project, previewLocation)
- }
- }
-}
-
-private fun buildPreviewViaGradle(project: Project, previewLocation: PreviewLocation) {
- val previewToolWindow = ToolWindowManager.getInstance(project).getToolWindow("Desktop Preview")
- previewToolWindow?.setAvailable(true)
-
- val gradleVmOptions = GradleSettings.getInstance(project).gradleVmOptions
- val settings = ExternalSystemTaskExecutionSettings()
- settings.executionName = "Preview: ${previewLocation.fqName}"
- settings.externalProjectPath = previewLocation.modulePath
- settings.taskNames = listOf(previewLocation.taskName)
- settings.vmOptions = gradleVmOptions
- settings.externalSystemIdString = GradleConstants.SYSTEM_ID.id
- val previewService = project.service()
- val gradleCallbackPort = previewService.gradleCallbackPort
- settings.scriptParameters =
- listOf(
- "-Pcompose.desktop.preview.target=${previewLocation.fqName}",
- "-Pcompose.desktop.preview.ide.port=$gradleCallbackPort"
- ).joinToString(" ")
- SwingUtilities.invokeLater {
- ToolWindowManager.getInstance(project).getToolWindow("Desktop Preview")?.activate {
- previewService.buildStarted()
- }
- }
- runTask(
- settings,
- DefaultRunExecutor.EXECUTOR_ID,
- project,
- GradleConstants.SYSTEM_ID,
- object : TaskCallback {
- override fun onSuccess() {
- previewService.buildFinished(success = true)
- }
- override fun onFailure() {
- previewService.buildFinished(success = false)
- }
- },
- ProgressExecutionMode.IN_BACKGROUND_ASYNC,
- false,
- UserDataHolderBase()
- )
-}
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewEntryPoint.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewEntryPoint.kt
deleted file mode 100644
index 1d6c274c8b8..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewEntryPoint.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.codeInspection.reference.EntryPoint
-import com.intellij.codeInspection.reference.RefElement
-import com.intellij.configurationStore.deserializeInto
-import com.intellij.configurationStore.serializeObjectInto
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiMethod
-import org.jdom.Element
-
-/**
- * [EntryPoint] implementation to mark `@Preview` functions as entry points and avoid them being flagged as unused.
- *
- * Based on
- * com.android.tools.idea.compose.preview.PreviewEntryPoint from AOSP
- * with modifications
- */
-class PreviewEntryPoint : EntryPoint() {
- private var ADD_PREVIEW_TO_ENTRIES: Boolean = true
-
- override fun isEntryPoint(refElement: RefElement, psiElement: PsiElement): Boolean = isEntryPoint(psiElement)
-
- override fun isEntryPoint(psiElement: PsiElement): Boolean =
- psiElement is PsiMethod && psiElement.hasAnnotation(DESKTOP_PREVIEW_ANNOTATION_FQN)
-
- override fun readExternal(element: Element) = element.deserializeInto(this)
-
- override fun writeExternal(element: Element) {
- serializeObjectInto(this, element)
- }
-
- override fun getDisplayName(): String = "Compose Preview"
-
- override fun isSelected(): Boolean = ADD_PREVIEW_TO_ENTRIES
-
- override fun setSelected(selected: Boolean) {
- this.ADD_PREVIEW_TO_ENTRIES = selected
- }
-}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewFloatingToolbarProvider.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewFloatingToolbarProvider.kt
deleted file mode 100644
index e8a8f3543c6..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewFloatingToolbarProvider.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.Disposable
-import com.intellij.openapi.actionSystem.CommonDataKeys
-import com.intellij.openapi.actionSystem.DataContext
-import com.intellij.openapi.diff.impl.DiffUtil
-import com.intellij.openapi.editor.Editor
-import com.intellij.openapi.editor.event.CaretEvent
-import com.intellij.openapi.editor.event.CaretListener
-import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider
-import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent
-import com.intellij.openapi.fileEditor.FileDocumentManager
-import com.intellij.openapi.project.Project
-import com.intellij.testFramework.LightVirtualFileBase
-import com.intellij.util.concurrency.AppExecutorUtil
-import org.jetbrains.kotlin.idea.KotlinFileType
-
-class PreviewFloatingToolbarProvider : AbstractFloatingToolbarProvider(PREVIEW_EDITOR_TOOLBAR_GROUP_ID) {
- override val autoHideable = false
-
- // todo: disable if not in Compose JVM module
- override fun register(dataContext: DataContext, component: FloatingToolbarComponent, parentDisposable: Disposable) {
- val editor = dataContext.getData(CommonDataKeys.EDITOR) ?: return
- if (isInsideMainKtEditor(editor)) {
- registerComponent(component, editor, parentDisposable)
- }
- }
-
- private fun registerComponent(
- component: FloatingToolbarComponent,
- editor: Editor,
- parentDisposable: Disposable
- ) {
- val project = editor.project
- if (project != null && isPreviewCompatible(project)) {
- val listener = PreviewEditorToolbarVisibilityUpdater(component, project, editor)
- editor.caretModel.addCaretListener(listener, parentDisposable)
- }
- }
-}
-
-internal class PreviewEditorToolbarVisibilityUpdater(
- private val toolbar: FloatingToolbarComponent,
- private val project: Project,
- private val editor: Editor
-) : CaretListener {
- override fun caretPositionChanged(event: CaretEvent) {
- runNonBlocking { updateVisibility() }
- .inSmartMode(project)
- .submit(AppExecutorUtil.getAppExecutorService())
- }
-
- private fun updateVisibility() {
- if (!editor.isDisposed) {
- val parentPreviewFun = parentPreviewAtCaretOrNull(editor)
- if (parentPreviewFun != null) {
- toolbar.scheduleShow()
- } else {
- toolbar.scheduleHide()
- }
- }
- }
-}
-
-private fun isInsideMainKtEditor(editor: Editor): Boolean =
- !DiffUtil.isDiffEditor(editor) && editor.isKtFileEditor()
-
-private fun Editor.isKtFileEditor(): Boolean {
- val documentManager = FileDocumentManager.getInstance()
- val virtualFile = documentManager.getFile(document) ?: return false
- return virtualFile !is LightVirtualFileBase
- && virtualFile.isValid
- && virtualFile.fileType == KotlinFileType.INSTANCE
-}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewIcons.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewIcons.kt
deleted file mode 100644
index acdba102f13..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewIcons.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.util.IconLoader
-
-object PreviewIcons {
- private fun load(path: String) = IconLoader.getIcon(path, PreviewIcons::class.java)
-
- val COMPOSE = load("/icons/compose/compose.svg")
- val RUN_PREVIEW = load("/icons/compose/runPreview.svg")
-}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewLocation.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewLocation.kt
deleted file mode 100644
index 8c801ca03a5..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewLocation.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.components.service
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
-import com.intellij.openapi.roots.ProjectFileIndex
-import com.intellij.util.concurrency.annotations.RequiresReadLock
-import org.jetbrains.kotlin.psi.KtNamedFunction
-
-data class PreviewLocation(val fqName: String, val modulePath: String, val taskName: String)
-
-@RequiresReadLock
-internal fun KtNamedFunction.asPreviewFunctionOrNull(): PreviewLocation? {
- if (!isValidComposablePreviewFunction()) return null
-
- val fqName = composePreviewFunctionFqn()
- val module = ProjectFileIndex.getInstance(project).getModuleForFile(containingFile.virtualFile)
- if (module == null || module.isDisposed) return null
-
- val service = project.service()
- val previewTaskName = service.configurePreviewTaskNameOrNull(module) ?: DEFAULT_CONFIGURE_PREVIEW_TASK_NAME
- val modulePath = ExternalSystemApiUtil.getExternalProjectPath(module) ?: return null
- return PreviewLocation(fqName = fqName, modulePath = modulePath, taskName = previewTaskName)
-}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewMessages.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewMessages.kt
deleted file mode 100644
index a7000c37c72..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewMessages.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-internal object PreviewMessages {
- fun runPreview(name: String): String = "Preview $name"
-}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewRootLogger.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewRootLogger.kt
deleted file mode 100644
index 300ead3ad0a..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewRootLogger.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.diagnostic.Logger
-
-val LOG = Logger.getInstance(PreviewRootLogger::class.java)
-private class PreviewRootLogger
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewRunLineMarkerContributor.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewRunLineMarkerContributor.kt
deleted file mode 100644
index a8aefef758d..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewRunLineMarkerContributor.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.execution.lineMarker.RunLineMarkerContributor
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
-import com.intellij.psi.PsiElement
-import com.intellij.psi.impl.source.tree.LeafPsiElement
-import org.jetbrains.kotlin.idea.util.projectStructure.module
-import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.psi.KtNamedFunction
-
-/**
- * Based on
- * com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunLineMarkerContributor from AOSP
- * with modifications
- */
-class PreviewRunLineMarkerContributor : RunLineMarkerContributor() {
- override fun getInfo(element: PsiElement): Info? {
- // Marker should be in a single LeafPsiElement. We choose the identifier and return null for other elements within the function.
- if (element !is LeafPsiElement) return null
- if (element.node.elementType != KtTokens.IDENTIFIER) return null
-
- return when (val parent = element.parent) {
- is KtNamedFunction -> {
- val previewFunction = parent.asPreviewFunctionOrNull() ?: return null
- val actions = arrayOf(RunPreviewAction(previewFunction))
- Info(PreviewIcons.COMPOSE, actions) { PreviewMessages.runPreview(parent.name!!) }
- }
- else -> null
- }
- }
-}
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewStateService.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewStateService.kt
deleted file mode 100644
index 6e0b6dd399b..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewStateService.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.Disposable
-import com.intellij.openapi.components.Service
-import com.intellij.openapi.externalSystem.model.task.*
-import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager
-import com.intellij.openapi.module.Module
-import com.intellij.openapi.util.Disposer
-import com.intellij.ui.components.JBLoadingPanel
-import com.intellij.util.concurrency.annotations.RequiresReadLock
-import org.jetbrains.compose.desktop.ide.preview.ui.PreviewPanel
-import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.*
-import org.jetbrains.plugins.gradle.util.GradleConstants
-import javax.swing.JComponent
-import javax.swing.event.AncestorEvent
-import javax.swing.event.AncestorListener
-
-@Service(Service.Level.PROJECT)
-class PreviewStateService : Disposable {
- private val previewListener = CompositePreviewListener()
- private val previewManager: PreviewManager = PreviewManagerImpl(previewListener)
- val gradleCallbackPort: Int
- get() = previewManager.gradleCallbackPort
- private val configurePreviewTaskNameCache =
- ConfigurePreviewTaskNameCache(ConfigurePreviewTaskNameProviderImpl())
-
- init {
- val projectRefreshListener = ConfigurePreviewTaskNameCacheInvalidator(configurePreviewTaskNameCache)
- ExternalSystemProgressNotificationManager.getInstance()
- .addNotificationListener(projectRefreshListener, this)
- }
-
- @RequiresReadLock
- internal fun configurePreviewTaskNameOrNull(module: Module): String? =
- configurePreviewTaskNameCache.configurePreviewTaskNameOrNull(module)
-
- override fun dispose() {
- previewManager.close()
- configurePreviewTaskNameCache.invalidate()
- }
-
- internal fun registerPreviewPanels(
- previewPanel: PreviewPanel,
- loadingPanel: JBLoadingPanel
- ) {
- val previewResizeListener = PreviewResizeListener(previewManager)
- previewPanel.addAncestorListener(previewResizeListener)
- Disposer.register(this) { previewPanel.removeAncestorListener(previewResizeListener) }
-
- previewListener.addListener(PreviewPanelUpdater(previewPanel))
- previewListener.addListener(LoadingPanelUpdater(loadingPanel))
- }
-
- internal fun buildStarted() {
- previewListener.onNewBuildRequest()
- }
-
- internal fun buildFinished(success: Boolean) {
- previewListener.onFinishedBuild(success)
- }
-}
-
-private class PreviewResizeListener(private val previewManager: PreviewManager) : AncestorListener {
- private fun updateFrameSize(c: JComponent) {
- val frameConfig = FrameConfig(
- width = c.width,
- height = c.height,
- scale = c.graphicsConfiguration.defaultTransform.scaleX
- )
- previewManager.updateFrameConfig(frameConfig)
- }
-
- override fun ancestorAdded(event: AncestorEvent) {
- updateFrameSize(event.component)
- }
-
- override fun ancestorRemoved(event: AncestorEvent) {
- }
-
- override fun ancestorMoved(event: AncestorEvent) {
- updateFrameSize(event.component)
- }
-}
-
-private class PreviewPanelUpdater(private val panel: PreviewPanel) : PreviewListenerBase() {
- override fun onRenderedFrame(frame: RenderedFrame) {
- panel.previewImage(frame.image, frame.dimension)
- }
-
- override fun onError(error: String) {
- panel.error(error)
- }
-}
-
-private class LoadingPanelUpdater(private val panel: JBLoadingPanel) : PreviewListenerBase() {
- override fun onNewBuildRequest() {
- panel.setLoadingText("Building project")
- panel.startLoading()
- }
-
- override fun onFinishedBuild(success: Boolean) {
- panel.stopLoading()
- }
-
- override fun onNewRenderRequest(previewRequest: FrameRequest) {
- panel.setLoadingText("Rendering preview")
- panel.startLoading()
- }
-
- override fun onRenderedFrame(frame: RenderedFrame) {
- panel.stopLoading()
- }
-}
-
-// ExternalSystemTaskNotificationListenerAdapter is used,
-// because ExternalSystemTaskNotificationListener interface's API
-// was changed between 2020.3 and 2021.1, so a direct implementation
-// would not work with both 2020.3 and 2021.1
-private class ConfigurePreviewTaskNameCacheInvalidator(
- private val configurePreviewTaskNameCache: ConfigurePreviewTaskNameCache
-) : ExternalSystemTaskNotificationListenerAdapter(null) {
- override fun onStart(id: ExternalSystemTaskId, workingDir: String?) {
- if (
- id.projectSystemId == GradleConstants.SYSTEM_ID &&
- id.type == ExternalSystemTaskType.RESOLVE_PROJECT
- ) {
- configurePreviewTaskNameCache.invalidate()
- }
- }
-}
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewToolWindow.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewToolWindow.kt
deleted file mode 100644
index d4c0606b22e..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/PreviewToolWindow.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.application.ApplicationManager
-import com.intellij.openapi.components.service
-import com.intellij.openapi.project.DumbAware
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.wm.ToolWindow
-import com.intellij.openapi.wm.ToolWindowFactory
-import com.intellij.ui.components.JBLoadingPanel
-import java.awt.BorderLayout
-import org.jetbrains.compose.desktop.ide.preview.ui.PreviewPanel
-
-class PreviewToolWindow : ToolWindowFactory, DumbAware {
- override suspend fun isApplicableAsync(project: Project): Boolean = isPreviewCompatible(project)
-
- override fun init(toolWindow: ToolWindow) {
- ApplicationManager.getApplication().invokeLater { toolWindow.setIcon(PreviewIcons.COMPOSE) }
- }
-
- override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
- toolWindow.contentManager.let { content ->
- val panel = PreviewPanel(project)
- val loadingPanel = JBLoadingPanel(BorderLayout(), toolWindow.disposable)
- loadingPanel.add(panel, BorderLayout.CENTER)
- content.addContent(content.factory.createContent(loadingPanel, null, false))
- project.service().registerPreviewPanels(panel, loadingPanel)
- }
- }
-
- // don't show the toolwindow until a preview is requested
- override fun shouldBeAvailable(project: Project): Boolean = false
-}
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/editorUtils.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/editorUtils.kt
deleted file mode 100644
index e7c9b99fc16..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/editorUtils.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.editor.Editor
-import com.intellij.openapi.fileEditor.FileDocumentManager
-import com.intellij.psi.PsiFile
-import com.intellij.psi.PsiManager
-import com.intellij.util.concurrency.annotations.RequiresReadLock
-import org.jetbrains.kotlin.idea.KotlinFileType
-import org.jetbrains.kotlin.psi.KtNamedFunction
-
-@RequiresReadLock
-internal fun parentPreviewAtCaretOrNull(editor: Editor): PreviewLocation? {
- val caretModel = editor.caretModel
- val psiFile = kotlinPsiFile(editor)
- if (psiFile != null) {
- var node = psiFile.findElementAt(caretModel.offset)
- while (node != null) {
- val previewFunction = (node as? KtNamedFunction)?.asPreviewFunctionOrNull()
- if (previewFunction != null) {
- return previewFunction
- }
- node = node.parent
- }
- }
-
- return null
-}
-
-private fun kotlinPsiFile(editor: Editor): PsiFile? {
- val project = editor.project ?: return null
- val documentManager = FileDocumentManager.getInstance()
- val file = documentManager.getFile(editor.document)
- return if (file != null && file.fileType is KotlinFileType) {
- PsiManager.getInstance(project).findFile(file)
- } else null
-}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ijCompatibilityUtils.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ijCompatibilityUtils.kt
deleted file mode 100644
index 7bc501682b1..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ijCompatibilityUtils.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.ide.lightEdit.LightEdit
-import com.intellij.openapi.application.NonBlockingReadAction
-import com.intellij.openapi.application.ReadAction
-import com.intellij.openapi.externalSystem.model.Key
-import com.intellij.openapi.externalSystem.model.project.AbstractNamedData
-import com.intellij.openapi.project.Project
-import java.lang.reflect.Modifier
-import java.util.concurrent.Callable
-
-// todo: filter only Compose projects
-internal fun isPreviewCompatible(project: Project): Boolean =
- !LightEdit.owns(project)
-
-internal val kotlinTargetDataKey: Key = run {
- val kotlinTargetDataClass = try {
- Class.forName("org.jetbrains.kotlin.idea.gradle.configuration.KotlinTargetData")
- } catch (e: ClassNotFoundException) {
- try {
- Class.forName("org.jetbrains.kotlin.idea.configuration.KotlinTargetData")
- } catch (e: ClassNotFoundException) {
- error("Could not find 'KotlinTargetData' class")
- }
- }
- val companionField = kotlinTargetDataClass.fields.firstOrNull { Modifier.isStatic(it.modifiers) && it.name == "Companion" }
- ?: error("'${kotlinTargetDataClass.canonicalName}.Companion")
- val companionInstance = companionField.get(kotlinTargetDataClass)
- val companionClass = companionInstance.javaClass
- val getKeyMethod = companionClass.methods.firstOrNull { it.name == "getKEY" }
- ?: error("Cannot find '${kotlinTargetDataClass.canonicalName}.Companion.getKEY'")
- @Suppress("UNCHECKED_CAST")
- getKeyMethod.invoke(companionInstance) as Key
-}
-
-internal inline fun runNonBlocking(crossinline fn: () -> Unit): NonBlockingReadAction =
- ReadAction.nonBlocking(Callable {
- fn()
- null
- })
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/locationUtils.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/locationUtils.kt
deleted file mode 100644
index fec6f8fd538..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/locationUtils.kt
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jetbrains.compose.desktop.ide.preview
-
-import com.intellij.openapi.roots.ProjectRootModificationTracker
-import com.intellij.psi.util.CachedValueProvider
-import com.intellij.psi.util.CachedValuesManager
-import com.intellij.psi.util.parentOfType
-import com.intellij.util.concurrency.annotations.RequiresReadLock
-import org.jetbrains.kotlin.analysis.api.analyze
-import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol
-import org.jetbrains.kotlin.asJava.findFacadeClass
-import org.jetbrains.kotlin.name.ClassId
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtClass
-import org.jetbrains.kotlin.psi.KtFile
-import org.jetbrains.kotlin.psi.KtNamedFunction
-import org.jetbrains.kotlin.psi.allConstructors
-import org.jetbrains.kotlin.psi.psiUtil.containingClass
-
-internal const val DESKTOP_PREVIEW_ANNOTATION_FQN =
- "androidx.compose.desktop.ui.tooling.preview.Preview"
-internal const val COMPOSABLE_FQ_NAME = "androidx.compose.runtime.Composable"
-
-private val ComposableAnnotationClassId = ClassId.topLevel(FqName(COMPOSABLE_FQ_NAME))
-private val DesktopPreviewAnnotationClassId =
- ClassId.topLevel(FqName(DESKTOP_PREVIEW_ANNOTATION_FQN))
-
-/**
- * Utils based on functions from AOSP, taken from
- * tools/adt/idea/compose-designer/src/com/android/tools/idea/compose/preview/util/PreviewElement.kt
- */
-
-/**
- * Returns whether a `@Composable` [DESKTOP_PREVIEW_ANNOTATION_FQN] is defined in a valid location,
- * which can be either:
- * 1. Top-level functions
- * 2. Non-nested functions defined in top-level classes that have a default (no parameter)
- * constructor
- */
-private fun KtNamedFunction.isValidPreviewLocation(): Boolean {
- if (valueParameters.isNotEmpty()) return false
- if (receiverTypeReference != null) return false
-
- if (isTopLevel) return true
-
- if (parentOfType() == null) {
- // This is not a nested method
- val containingClass = containingClass()
- if (containingClass != null) {
- // We allow functions that are not top level defined in top level classes that have a
- // default (no parameter) constructor.
- if (containingClass.isTopLevel() && containingClass.hasDefaultConstructor()) {
- return true
- }
- }
- }
- return false
-}
-
-/**
- * Computes the qualified name of the class containing this [KtNamedFunction].
- *
- * For functions defined within a Kotlin class, returns the qualified name of that class. For
- * top-level functions, returns the JVM name of the Java facade class generated instead.
- */
-internal fun KtNamedFunction.getClassName(): String? =
- if (isTopLevel) ((parent as? KtFile)?.findFacadeClass())?.qualifiedName
- else parentOfType()?.getQualifiedName()
-
-/**
- * Computes the qualified name for a Kotlin Class. Returns null if the class is a kotlin built-in.
- */
-private fun KtClass.getQualifiedName(): String? =
- analyze(this) {
- val classSymbol = symbol
- return when {
- classSymbol !is KaClassLikeSymbol -> null
- classSymbol.classId.isKotlinPackage() -> null
- else -> classSymbol.classId?.asFqNameString()
- }
- }
-
-private fun ClassId?.isKotlinPackage() =
- this != null && startsWith(org.jetbrains.kotlin.builtins.StandardNames.BUILT_INS_PACKAGE_NAME)
-
-private fun KtClass.hasDefaultConstructor() =
- allConstructors.isEmpty().or(allConstructors.any { it.valueParameters.isEmpty() })
-
-internal fun KtNamedFunction.composePreviewFunctionFqn() = "${getClassName()}.${name}"
-
-@RequiresReadLock
-internal fun KtNamedFunction.isValidComposablePreviewFunction(): Boolean {
- fun isValidComposablePreviewImpl(): Boolean =
- analyze(this) {
- if (!isValidPreviewLocation()) return false
-
- val mySymbol = symbol
- val hasComposableAnnotation = mySymbol.annotations.contains(ComposableAnnotationClassId)
- val hasPreviewAnnotation =
- mySymbol.annotations.contains(DesktopPreviewAnnotationClassId)
-
- return hasComposableAnnotation && hasPreviewAnnotation
- }
-
- return CachedValuesManager.getCachedValue(this) { cachedResult(isValidComposablePreviewImpl()) }
-}
-
-// based on AndroidComposePsiUtils.kt from AOSP
-internal fun KtNamedFunction.isComposableFunction(): Boolean =
- CachedValuesManager.getCachedValue(this) {
- val hasComposableAnnotation =
- analyze(this) { symbol.annotations.contains(ComposableAnnotationClassId) }
-
- cachedResult(hasComposableAnnotation)
- }
-
-private fun KtNamedFunction.cachedResult(value: T) =
- CachedValueProvider.Result.create(
- // TODO: see if we can handle alias imports without ruining performance.
- value,
- this.containingKtFile,
- ProjectRootModificationTracker.getInstance(project),
- )
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ui/PreviewPanel.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ui/PreviewPanel.kt
deleted file mode 100644
index 4fe1241fb40..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ui/PreviewPanel.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview.ui
-
-import com.intellij.icons.AllIcons
-import com.intellij.openapi.project.Project
-import com.intellij.ui.SimpleTextAttributes
-import com.intellij.ui.components.JBPanel
-import com.intellij.util.ui.StatusText
-import java.awt.Color
-import java.awt.Dimension
-import java.awt.Graphics
-import java.awt.image.BufferedImage
-import java.util.concurrent.atomic.AtomicReference
-import javax.swing.SwingUtilities
-
-internal class PreviewPanel(private val myProject: Project) : JBPanel() {
- sealed class PreviewPanelState {
- data class Image(val image: BufferedImage, val dimension: Dimension) : PreviewPanelState()
- class Error(val error: String) : PreviewPanelState()
- }
- private val myState = AtomicReference()
- private val myStatusText = object : StatusText(this) {
- override fun isStatusVisible(): Boolean {
- return myState.get() is PreviewPanelState.Error
- }
- }
-
- init {
- SwingUtilities.invokeLater {
- myStatusText.initStatusText()
- }
- }
-
- fun StatusText.initStatusText() {
- clear()
- appendLine(
- AllIcons.General.Error,
- "Preview rendering encountered an error",
- SimpleTextAttributes.REGULAR_ATTRIBUTES,
- null
- )
- appendLine(
- "Show details",
- SimpleTextAttributes.LINK_ATTRIBUTES
- ) {
- val errorText = (myState.get() as? PreviewPanelState.Error)?.error
- showTextDialog("Preview Error Details", errorText.orEmpty(), myProject)
- }
- }
-
- override fun paintComponent(g: Graphics) {
- super.paintComponent(g)
-
- when (val state = myState.get()) {
- is PreviewPanelState.Image -> {
- val (image, dimension) = state
- val w = dimension.width
- val h = dimension.height
- g.color = Color.WHITE
- g.fillRect(0, 0, w, h)
- g.drawImage(image, 0, 0, w, h, null)
- }
- is PreviewPanelState.Error -> {
- myStatusText.paint(this, g)
- }
- }
- }
-
- fun previewImage(image: BufferedImage, imageDimension: Dimension) {
- myState.set(PreviewPanelState.Image(image, imageDimension))
- SwingUtilities.invokeLater {
- repaint()
- }
- }
-
- fun error(error: String) {
- myState.set(PreviewPanelState.Error(error))
- SwingUtilities.invokeLater {
- repaint()
- }
- }
-
- override fun getPreferredSize(): Dimension? =
- (myState.get() as? PreviewPanelState.Image)?.dimension ?: super.getPreferredSize()
-}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ui/uiUtils.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ui/uiUtils.kt
deleted file mode 100644
index a31528f11ec..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/desktop/ide/preview/ui/uiUtils.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.desktop.ide.preview.ui
-
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.ui.DialogWrapper
-import com.intellij.ui.ScrollPaneFactory
-import java.awt.BorderLayout
-import javax.swing.JComponent
-import javax.swing.JPanel
-import javax.swing.JTextArea
-
-fun showTextDialog(
- title: String,
- text: String,
- project: Project? = null
- ) {
- val wrapper: DialogWrapper = object : DialogWrapper(project, false) {
- init {
- init()
- }
-
- override fun createCenterPanel(): JComponent {
- val textArea = JTextArea(text).apply {
- isEditable = false
- rows = 40
- columns = 70
- }
- return JPanel(BorderLayout()).apply {
- add(ScrollPaneFactory.createScrollPane(textArea))
- }
- }
- }
- wrapper.title = title
- wrapper.show()
-}
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/inspections/ComposeSuppressor.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/inspections/ComposeSuppressor.kt
deleted file mode 100644
index 04407d2f02b..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/inspections/ComposeSuppressor.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jetbrains.compose.inspections
-
-import com.intellij.codeInspection.InspectionSuppressor
-import com.intellij.codeInspection.SuppressQuickFix
-import com.intellij.psi.PsiElement
-import org.jetbrains.compose.desktop.ide.preview.isComposableFunction
-import org.jetbrains.kotlin.idea.KotlinLanguage
-import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.psi.KtNamedFunction
-
-/**
- * Suppress inspection that require composable function names to start with a lower case letter.
- */
-class ComposeSuppressor : InspectionSuppressor {
- override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean {
- return toolId == "FunctionName" &&
- element.language == KotlinLanguage.INSTANCE &&
- element.node.elementType == KtTokens.IDENTIFIER &&
- element.parent.let { it is KtNamedFunction && it.isComposableFunction() }
- }
-
- override fun getSuppressActions(element: PsiElement?, toolId: String): Array {
- return SuppressQuickFix.EMPTY_ARRAY
- }
-}
-
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/WebRunConfigurationProducer.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/WebRunConfigurationProducer.kt
deleted file mode 100644
index d0cc3a9641b..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/WebRunConfigurationProducer.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-package org.jetbrains.compose.web.ide.run
-
-import com.intellij.execution.actions.ConfigurationContext
-import com.intellij.execution.actions.LazyRunConfigurationProducer
-import com.intellij.execution.configurations.ConfigurationFactory
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
-import com.intellij.openapi.util.Ref
-import com.intellij.psi.PsiElement
-import org.jetbrains.compose.common.modulePath
-import org.jetbrains.kotlin.psi.KtNamedFunction
-import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType
-import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration
-import org.jetbrains.plugins.gradle.util.GradleConstants
-
-class WebRunConfigurationProducer : LazyRunConfigurationProducer() {
- override fun getConfigurationFactory(): ConfigurationFactory =
- GradleExternalTaskConfigurationType.getInstance().factory
-
- override fun isConfigurationFromContext(
- configuration: GradleRunConfiguration,
- context: ConfigurationContext
- ): Boolean {
- val mainFun = context.jsMainOrNull ?: return false
- return configuration.run {
- name == mainFun.name!!
- && settings.externalProjectPath == context.modulePath()
- && settings.taskNames.contains(jsRunTaskName)
- }
- }
-
- override fun setupConfigurationFromContext(
- configuration: GradleRunConfiguration,
- context: ConfigurationContext,
- sourceElement: Ref
- ): Boolean {
- val mainFun = context.jsMainOrNull ?: return false
- configuration.apply {
- name = mainFun.name!!
- settings.taskNames.add(jsRunTaskName)
- settings.externalProjectPath =
- ExternalSystemApiUtil.getExternalProjectPath(context.location?.module)
- }
- return true
- }
-
- companion object {
- private const val jsRunTaskName = "jsBrowserDevelopmentRun"
- }
-}
-
-private val ConfigurationContext.jsMainOrNull: KtNamedFunction?
- get() = psiLocation?.parent?.getAsJsMainFunctionOrNull()
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/WebRunLineMarkerContributor.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/WebRunLineMarkerContributor.kt
deleted file mode 100644
index 14c1a0d1ea8..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/WebRunLineMarkerContributor.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-package org.jetbrains.compose.web.ide.run
-
-import com.intellij.execution.lineMarker.ExecutorAction
-import com.intellij.execution.lineMarker.RunLineMarkerContributor
-import com.intellij.icons.AllIcons
-import com.intellij.psi.PsiElement
-import com.intellij.psi.impl.source.tree.LeafPsiElement
-import org.jetbrains.kotlin.lexer.KtTokens
-
-class WebRunLineMarkerContributor : RunLineMarkerContributor() {
- override fun getInfo(element: PsiElement): Info? {
- if (element !is LeafPsiElement) return null
- if (element.node.elementType != KtTokens.IDENTIFIER) return null
- if (element.parent.getAsJsMainFunctionOrNull() == null) return null
-
- val icon = AllIcons.RunConfigurations.TestState.Run
- return Info(icon, arrayOf(ExecutorAction.getActions()[0]))
- }
-}
diff --git a/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/psiUtils.kt b/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/psiUtils.kt
deleted file mode 100644
index 3e9b3ec8cd1..00000000000
--- a/idea-plugin/src/main/kotlin/org/jetbrains/compose/web/ide/run/psiUtils.kt
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
- */
-
-package org.jetbrains.compose.web.ide.run
-
-import com.intellij.psi.PsiElement
-import com.intellij.util.concurrency.annotations.RequiresReadLock
-import org.jetbrains.kotlin.analysis.api.KaSession
-import org.jetbrains.kotlin.analysis.api.analyze
-import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol
-import org.jetbrains.kotlin.analysis.api.types.KaClassType
-import org.jetbrains.kotlin.analysis.api.types.KaType
-import org.jetbrains.kotlin.analysis.api.types.KaTypeNullability
-import org.jetbrains.kotlin.config.LanguageFeature
-import org.jetbrains.kotlin.idea.base.facet.platform.platform
-import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
-import org.jetbrains.kotlin.idea.base.psi.KotlinPsiHeuristics
-import org.jetbrains.kotlin.idea.base.util.module
-import org.jetbrains.kotlin.name.StandardClassIds
-import org.jetbrains.kotlin.platform.js.JsPlatforms
-import org.jetbrains.kotlin.psi.KtNamedFunction
-import org.jetbrains.kotlin.types.Variance
-
-internal fun PsiElement.getAsJsMainFunctionOrNull(): KtNamedFunction? =
- (this as? KtNamedFunction)?.takeIf { it.isValidJsMain() }
-
-internal fun KtNamedFunction.isValidJsMain(): Boolean = isTopLevel && isJsPlatform() && isMainFun()
-
-internal fun KtNamedFunction.isJsPlatform(): Boolean =
- module?.platform?.let { platform -> platform in JsPlatforms.allJsPlatforms } == true
-
-internal fun KtNamedFunction.isMainFun(): Boolean =
- isMainFromPsiOnly(this) && isMainFromAnalysis(this)
-
-//////////////////////////////////////////////////////////////////
-// Copied and simplified from PsiOnlyKotlinMainFunctionDetector //
-//////////////////////////////////////////////////////////////////
-@RequiresReadLock
-private fun isMainFromPsiOnly(function: KtNamedFunction): Boolean {
- if (function.isLocal || function.typeParameters.isNotEmpty()) {
- return false
- }
-
- val isTopLevel = function.isTopLevel
- val parameterCount =
- function.valueParameters.size + (if (function.receiverTypeReference != null) 1 else 0)
-
- if (parameterCount == 0) {
- if (!isTopLevel) {
- return false
- }
-
- if (
- !function.languageVersionSettings.supportsFeature(
- LanguageFeature.ExtendedMainConvention
- )
- ) {
- return false
- }
- } else if (parameterCount == 1 && !isMainCheckParameter(function)) {
- return false
- } else {
- return false
- }
-
- if ((KotlinPsiHeuristics.findJvmName(function) ?: function.name) != "main") {
- return false
- }
-
- if (!isTopLevel && !KotlinPsiHeuristics.hasJvmStaticAnnotation(function)) {
- return false
- }
-
- val returnTypeReference = function.typeReference
- return !(returnTypeReference != null &&
- !KotlinPsiHeuristics.typeMatches(returnTypeReference, StandardClassIds.Unit))
-}
-
-private fun isMainCheckParameter(function: KtNamedFunction): Boolean {
- val receiverTypeReference = function.receiverTypeReference
- if (receiverTypeReference != null) {
- return KotlinPsiHeuristics.typeMatches(
- receiverTypeReference,
- StandardClassIds.Array,
- StandardClassIds.String,
- )
- }
-
- val parameter = function.valueParameters.singleOrNull() ?: return false
- val parameterTypeReference = parameter.typeReference ?: return false
-
- return when {
- parameter.isVarArg ->
- KotlinPsiHeuristics.typeMatches(parameterTypeReference, StandardClassIds.String)
- else ->
- KotlinPsiHeuristics.typeMatches(
- parameterTypeReference,
- StandardClassIds.Array,
- StandardClassIds.String,
- )
- }
-}
-
-//////////////////////////////////////////////////////////////////////
-// Copied and simplified from SymbolBasedKotlinMainFunctionDetector //
-//////////////////////////////////////////////////////////////////////
-private fun isMainFromAnalysis(function: KtNamedFunction): Boolean {
- if (function.isLocal || function.typeParameters.isNotEmpty()) {
- return false
- }
-
- val supportsExtendedMainConvention =
- function.languageVersionSettings.supportsFeature(LanguageFeature.ExtendedMainConvention)
-
- val isTopLevel = function.isTopLevel
- val parameterCount =
- function.valueParameters.size + (if (function.receiverTypeReference != null) 1 else 0)
-
- if (parameterCount == 0) {
- if (!isTopLevel || !supportsExtendedMainConvention) {
- return false
- }
- } else if (parameterCount > 1) {
- return false
- }
-
- analyze(function) {
- if (parameterCount == 1) {
- val parameterTypeReference =
- function.receiverTypeReference
- ?: function.valueParameters[0].typeReference
- ?: return false
-
- val parameterType = parameterTypeReference.type
- if (
- !parameterType.isResolvedClassType() ||
- !parameterType.isSubtypeOf(buildMainParameterType())
- ) {
- return false
- }
- }
-
- val functionSymbol = function.symbol
- if (functionSymbol !is KaNamedFunctionSymbol) {
- return false
- }
-
- if (functionSymbol.name.identifier != "main") {
- return false
- }
-
- if (!function.returnType.isUnitType) {
- return false
- }
- }
- return true
-}
-
-private fun KaSession.buildMainParameterType(): KaType =
- buildClassType(StandardClassIds.Array) {
- val argumentType =
- buildClassType(StandardClassIds.String) { nullability = KaTypeNullability.NON_NULLABLE }
-
- argument(argumentType, Variance.OUT_VARIANCE)
- nullability = KaTypeNullability.NULLABLE
- }
-
-private fun KaType.isResolvedClassType(): Boolean =
- when (this) {
- is KaClassType -> typeArguments.mapNotNull { it.type }.all { it.isResolvedClassType() }
- else -> false
- }
diff --git a/idea-plugin/src/main/resources/META-INF/plugin.xml b/idea-plugin/src/main/resources/META-INF/plugin.xml
index d26150d4a39..0ff72a848dc 100644
--- a/idea-plugin/src/main/resources/META-INF/plugin.xml
+++ b/idea-plugin/src/main/resources/META-INF/plugin.xml
@@ -4,16 +4,8 @@
JetBrains
⚠️ This plugin is in maintenance mode
-
The Compose Multiplatform for Desktop IDE Support plugin is no longer under active development.
-Its functionality is being gradually integrated into the new Kotlin Multiplatform plugin,
-which already includes support for Compose Previews on macOS.
-
What this means for you:
-
-
On macOS: We recommend switching to the Kotlin Multiplatform plugin, which now includes improved Compose Previews support.
-
On Windows/Linux: You can continue using this plugin. It remains available and maintained for now, until equivalent support becomes available in the Kotlin Multiplatform plugin.
-
-
No new features will be added, but we’ll address critical issues as needed during this transition.