mirror of
https://github.com/jlengrand/compose-multiplatform.git
synced 2026-03-10 08:11:20 +00:00
Don't create repeated run configurations for preview (#851)
This commit is contained in:
@@ -11,4 +11,5 @@ 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")
|
||||
}
|
||||
@@ -1,99 +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.actions.ConfigurationContext
|
||||
import com.intellij.execution.actions.ConfigurationFromContext
|
||||
import com.intellij.execution.actions.LazyRunConfigurationProducer
|
||||
import com.intellij.execution.configurations.ConfigurationFactory
|
||||
import com.intellij.openapi.components.service
|
||||
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.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType
|
||||
import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration
|
||||
|
||||
/**
|
||||
* Producer of [ComposePreviewRunConfiguration] for `@Composable` functions annotated with [PREVIEW_ANNOTATION_FQN]. The configuration
|
||||
* created is initially named after the `@Composable` function, and its fully qualified name is properly set in the configuration.
|
||||
*
|
||||
* The [ConfigurationContext] where the [ComposePreviewRunConfiguration] is created from can be any descendant of the `@Composable` function
|
||||
* in the PSI tree, such as its annotations, function name or even the keyword "fun".
|
||||
*
|
||||
* Based on com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer from AOSP
|
||||
* with modifications
|
||||
*/
|
||||
class PreviewRunConfigurationProducer : LazyRunConfigurationProducer<GradleRunConfiguration>() {
|
||||
override fun getConfigurationFactory(): ConfigurationFactory =
|
||||
GradleExternalTaskConfigurationType.getInstance().factory
|
||||
|
||||
override fun isConfigurationFromContext(
|
||||
configuration: GradleRunConfiguration,
|
||||
context: ConfigurationContext
|
||||
): Boolean {
|
||||
val composeFunction = context.containingComposePreviewFunction() ?: return false
|
||||
return configuration.run {
|
||||
name == runConfigurationNameFor(composeFunction)
|
||||
&& settings.externalProjectPath == context.modulePath()
|
||||
&& settings.taskNames.singleOrNull() == configureDesktopPreviewTaskName
|
||||
&& settings.scriptParameters.split(" ").containsAll(
|
||||
runConfigurationScriptParameters(composeFunction.composePreviewFunctionFqn(), context.port)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupConfigurationFromContext(
|
||||
configuration: GradleRunConfiguration,
|
||||
context: ConfigurationContext,
|
||||
sourceElement: Ref<PsiElement>
|
||||
): Boolean {
|
||||
val composeFunction = context.containingComposePreviewFunction() ?: return false
|
||||
// todo: temporary configuration?
|
||||
configuration.apply {
|
||||
name = runConfigurationNameFor(composeFunction)
|
||||
settings.taskNames.add(configureDesktopPreviewTaskName)
|
||||
settings.externalProjectPath = ExternalSystemApiUtil.getExternalProjectPath(context.location?.module)
|
||||
settings.scriptParameters =
|
||||
runConfigurationScriptParameters(composeFunction.composePreviewFunctionFqn(), context.port)
|
||||
.joinToString(" ")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private val configureDesktopPreviewTaskName = "configureDesktopPreview"
|
||||
|
||||
private fun runConfigurationNameFor(function: KtNamedFunction): String =
|
||||
"Compose Preview: ${function.name!!}"
|
||||
|
||||
private fun runConfigurationScriptParameters(target: String, idePort: Int): List<String> =
|
||||
listOf(
|
||||
"-Pcompose.desktop.preview.target=$target",
|
||||
"-Pcompose.desktop.preview.ide.port=${idePort}"
|
||||
)
|
||||
|
||||
private val ConfigurationContext.port: Int
|
||||
get() = project.service<PreviewStateService>().gradleCallbackPort
|
||||
|
||||
private fun KtNamedFunction.composePreviewFunctionFqn() = "${getClassName()}.${name}"
|
||||
|
||||
private fun ConfigurationContext.containingComposePreviewFunction() =
|
||||
psiLocation?.let { location -> location.getNonStrictParentOfType<KtNamedFunction>()?.takeIf { it.isValidComposePreview() } }
|
||||
@@ -16,10 +16,11 @@
|
||||
|
||||
package org.jetbrains.compose.desktop.ide.preview
|
||||
|
||||
import com.intellij.execution.lineMarker.ExecutorAction
|
||||
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
|
||||
|
||||
@@ -37,11 +38,13 @@ class PreviewRunLineMarkerContributor : RunLineMarkerContributor() {
|
||||
val parent = element.parent
|
||||
return when {
|
||||
parent is KtNamedFunction && parent.isValidComposePreview() -> {
|
||||
val actions = arrayOf(ExecutorAction.getActions(0).first())
|
||||
val fqName = parent.composePreviewFunctionFqn()
|
||||
val module = parent.module?.let { ExternalSystemApiUtil.getExternalProjectPath(it) }
|
||||
?: error("Could not determine module for $parent")
|
||||
val actions = arrayOf(RunPreviewAction(fqName, module))
|
||||
Info(PreviewIcons.COMPOSE, actions) { PreviewMessages.runPreview(parent.name!!) }
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.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.util.UserDataHolderBase
|
||||
import org.jetbrains.plugins.gradle.settings.GradleSettings
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class RunPreviewAction(
|
||||
private val fqName: String,
|
||||
private val modulePath: String
|
||||
) : AnAction({ "Show non-interactive preview" }, PreviewIcons.RUN_PREVIEW) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project!!
|
||||
val gradleVmOptions = GradleSettings.getInstance(project).gradleVmOptions
|
||||
val settings = ExternalSystemTaskExecutionSettings()
|
||||
settings.executionName = "Preview: $fqName"
|
||||
settings.externalProjectPath = modulePath
|
||||
settings.taskNames = listOf("configureDesktopPreview")
|
||||
settings.vmOptions = gradleVmOptions
|
||||
settings.externalSystemIdString = GradleConstants.SYSTEM_ID.id
|
||||
val gradleCallbackPort = project.service<PreviewStateService>().gradleCallbackPort
|
||||
settings.scriptParameters =
|
||||
listOf(
|
||||
"-Pcompose.desktop.preview.target=$fqName",
|
||||
"-Pcompose.desktop.preview.ide.port=$gradleCallbackPort"
|
||||
).joinToString(" ")
|
||||
val future = CompletableFuture<Nothing>()
|
||||
runTask(
|
||||
settings,
|
||||
DefaultRunExecutor.EXECUTOR_ID,
|
||||
project,
|
||||
GradleConstants.SYSTEM_ID,
|
||||
object : TaskCallback {
|
||||
override fun onSuccess() {
|
||||
future.complete(null)
|
||||
}
|
||||
override fun onFailure() {
|
||||
future.completeExceptionally(RuntimeException("Preview for $fqName failed"))
|
||||
}
|
||||
},
|
||||
ProgressExecutionMode.IN_BACKGROUND_ASYNC,
|
||||
false,
|
||||
UserDataHolderBase()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -109,4 +109,6 @@ private fun KtAnnotationEntry.fqNameMatches(fqName: String): Boolean {
|
||||
private fun KtAnnotationEntry.getQualifiedName(): String? =
|
||||
analyze(BodyResolveMode.PARTIAL).get(BindingContext.ANNOTATION, this)?.fqName?.asString()
|
||||
|
||||
internal fun KtNamedFunction.composePreviewFunctionFqn() = "${getClassName()}.${name}"
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
<runLineMarkerContributor
|
||||
language="kotlin"
|
||||
implementationClass="org.jetbrains.compose.desktop.ide.preview.PreviewRunLineMarkerContributor"/>
|
||||
<runConfigurationProducer
|
||||
implementation="org.jetbrains.compose.desktop.ide.preview.PreviewRunConfigurationProducer"/>
|
||||
<deadCode implementation="org.jetbrains.compose.desktop.ide.preview.PreviewEntryPoint" />
|
||||
|
||||
<runLineMarkerContributor
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<!-- Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<polygon fill="#59A869" fill-rule="evenodd" points="4 2 14 8 4 14"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 309 B |
Reference in New Issue
Block a user