diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleExecutionHelper.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleExecutionHelper.java index fb9388524b65..4a7cdc53e0e5 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleExecutionHelper.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleExecutionHelper.java @@ -167,6 +167,48 @@ public final class GradleExecutionHelper { return getBuildEnvironment(connection, context); } + public static @NotNull Model getModel( + @NotNull ProjectConnection connection, + @NotNull GradleExecutionContext context, + @NotNull Class modelClass + ) { + Span span = ExternalSystemTelemetryUtil.getTracer(GradleConstants.SYSTEM_ID) + .spanBuilder("GetModel") + .setAttribute("modelClass", modelClass.getName()) + .startSpan(); + try (Scope ignore = span.makeCurrent()) { + ExternalSystemTaskId taskId = context.getTaskId(); + ExternalSystemTaskNotificationListener listener = context.getListener(); + + ModelBuilder modelBuilder = connection.model(modelClass); + + modelBuilder.withCancellationToken(context.getCancellationToken()); + + setupJavaHome(modelBuilder, context.getSettings(), taskId, listener, null); + + // do not use connection.getModel methods since it doesn't allow to handle progress events + // and we can miss gradle tooling client side events like distribution download. + GradleProgressListener gradleProgressListener = new GradleProgressListener(listener, taskId); + modelBuilder.addProgressListener((ProgressListener)gradleProgressListener); + modelBuilder.addProgressListener((org.gradle.tooling.events.ProgressListener)gradleProgressListener); + modelBuilder.setStandardOutput(new OutputWrapper(listener, taskId, true)); + modelBuilder.setStandardError(new OutputWrapper(listener, taskId, false)); + + return modelBuilder.get(); + } + catch (CancellationException ce) { + throw ce; + } + catch (Exception ex) { + span.recordException(ex); + span.setStatus(StatusCode.ERROR); + throw new RuntimeException(String.format("Failed to obtain model %s from Gradle daemon.", modelClass.getSimpleName()), ex); + } + finally { + span.end(); + } + } + /** * @deprecated use the {@link GradleExecutionHelper#execute} function with {@link GradleExecutionContext} instead */ diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/modelAction/GradleIdeaModelHolder.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/modelAction/GradleIdeaModelHolder.kt index 9efb42ccfd24..c1f982d8d540 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/modelAction/GradleIdeaModelHolder.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/modelAction/GradleIdeaModelHolder.kt @@ -53,7 +53,8 @@ class GradleIdeaModelHolder( } fun getRootModel(modelClazz: Class): T? { - return getBuildModel(getRootBuild(), modelClazz) + return getModel(getRootModelId(modelClazz), modelClazz) + ?: rootBuild?.let { getBuildModel(it, modelClazz) } } fun getBuildModel(buildModel: BuildModel, modelClass: Class): T? { @@ -99,6 +100,7 @@ class GradleIdeaModelHolder( } } + @ApiStatus.Internal private fun deserializeModel(model: Any, modelId: GradleModelId, modelClass: Class): T? { if (model !is ByteArray) { return null @@ -116,6 +118,12 @@ class GradleIdeaModelHolder( return deserializedModel } + @ApiStatus.Internal + fun addRootModel(modelClass: Class, model: T) { + val modelId = getRootModelId(modelClass) + models[modelId] = model + } + fun addState(state: GradleModelHolderState) { val rootBuild = state.rootBuild val nestedBuilds = state.nestedBuilds @@ -166,6 +174,10 @@ class GradleIdeaModelHolder( return GradleModelId(modelId.classId, originalBuildId, modelId.projectId) } + private fun getRootModelId(modelClass: Class<*>): GradleModelId { + return GradleModelId.createRootModelId(modelClass) + } + private fun getBuildModelId(buildModel: BuildModel, modelClass: Class<*>): GradleModelId { val modelId = GradleModelId.createBuildModelId(buildModel, modelClass) return applyBuildIdMapping(modelId) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java index 97e5e0a59144..5ae99340b06f 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java @@ -44,6 +44,7 @@ import org.gradle.tooling.GradleConnector; import org.gradle.tooling.ProjectConnection; import org.gradle.tooling.model.ProjectModel; import org.gradle.tooling.model.build.BuildEnvironment; +import org.gradle.tooling.model.dsl.GradleDslBaseScriptModel; import org.gradle.tooling.model.idea.IdeaModule; import org.gradle.tooling.model.idea.IdeaProject; import org.jetbrains.annotations.*; @@ -282,6 +283,12 @@ public final class GradleProjectResolver implements ExternalSystemProjectResolve .spanBuilder("GradleCall") .startSpan(); try (Scope ignore = gradleCallSpan.makeCurrent()) { + if (GradleVersionUtil.isGradleAtLeast(resolverContext.getGradleVersion(), "9.2")) { + var scriptModel = GradleExecutionHelper.getModel(connection, resolverContext, GradleDslBaseScriptModel.class); + models.addRootModel(GradleDslBaseScriptModel.class, scriptModel); + GradleSyncProjectConfigurator.runScriptBasePhase(resolverContext); + } + var modelFetchActionResultHandler = GradleSyncProjectConfigurator.createModelFetchResultHandler(resolverContext); GradleModelFetchActionRunner.runAndTraceBuildAction(connection, resolverContext, buildAction, modelFetchActionResultHandler); } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/syncAction/GradleSyncPhase.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/syncAction/GradleSyncPhase.kt index ac556a0b73be..e2a35a3a5b10 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/syncAction/GradleSyncPhase.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/syncAction/GradleSyncPhase.kt @@ -30,6 +30,9 @@ sealed interface GradleSyncPhase : Comparable { } } + @ApiStatus.Internal + sealed interface BaseScript: GradleSyncPhase + /** * In these phases, Gradle sync contributors are executed when models for [modelFetchPhase] are collected on the Gradle daemon side. */ @@ -61,6 +64,13 @@ sealed interface GradleSyncPhase : Comparable { @JvmField val DECLARATIVE_PHASE: GradleSyncPhase = Static(1000, "DECLARATIVE_PHASE") + /** + * + */ + @JvmField + @ApiStatus.Internal + val BASE_SCRIPT_MODEL_PHASE: GradleSyncPhase = GradleBaseScriptSyncPhase + /** * In this phase, Gradle sync contributors, * contribute to the IDE module and content root structure for each Gradle project. @@ -82,6 +92,12 @@ sealed interface GradleSyncPhase : Comparable { @JvmField val DEPENDENCY_MODEL_PHASE: GradleSyncPhase = GradleModelFetchPhase.PROJECT_SOURCE_SET_DEPENDENCY_PHASE.asSyncPhase() + /** + * + */ + @JvmField + val SCRIPT_MODEL_PHASE: GradleSyncPhase = GradleModelFetchPhase.SCRIPT_MODEL_PHASE.asSyncPhase() + /** * In this phase, Gradle sync contributors, * contribute to the IDE project model for a rich experience in IntelliJ IDEA. @@ -105,6 +121,7 @@ private class GradleStaticSyncPhase( override fun compareTo(other: GradleSyncPhase): Int { return when (other) { is GradleStaticSyncPhase -> order.compareTo(other.order) + is GradleBaseScriptSyncPhase -> -1 is GradleDynamicSyncPhase -> -1 } } @@ -134,6 +151,7 @@ private class GradleDynamicSyncPhase( override fun compareTo(other: GradleSyncPhase): Int { return when (other) { is GradleStaticSyncPhase -> 1 + is GradleBaseScriptSyncPhase -> 1 is GradleDynamicSyncPhase -> modelFetchPhase.compareTo(other.modelFetchPhase) } } @@ -150,4 +168,17 @@ private class GradleDynamicSyncPhase( override fun hashCode(): Int { return modelFetchPhase.hashCode() } +} + +private data object GradleBaseScriptSyncPhase: GradleSyncPhase.BaseScript { + + override val name: String = "BASE_SCRIPT_MODEL" + + override fun compareTo(other: GradleSyncPhase): Int { + return when (other) { + is GradleStaticSyncPhase -> 1 + is GradleBaseScriptSyncPhase -> 0 + is GradleDynamicSyncPhase -> -1 + } + } } \ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/syncAction/impl/GradleSyncProjectConfigurator.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/syncAction/impl/GradleSyncProjectConfigurator.kt index 4ce6ab71797c..6a69fdf6692d 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/syncAction/impl/GradleSyncProjectConfigurator.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/syncAction/impl/GradleSyncProjectConfigurator.kt @@ -40,6 +40,16 @@ object GradleSyncProjectConfigurator { } } + @JvmStatic + fun runScriptBasePhase(context: ProjectResolverContext) { + require(!application.isWriteAccessAllowed) { + "Must not execute inside write action" + } + runBlockingCancellable { + GradleSyncActionRunner().performSyncContributors(context) { it is GradleSyncPhase.BaseScript } + } + } + @JvmStatic fun createModelFetchResultHandler(context: ProjectResolverContext): GradleModelFetchActionListener { return object : GradleModelFetchActionListener { diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleOutputParsersMessagesImportingTest.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleOutputParsersMessagesImportingTest.kt index d39aaa9a4ea4..3aa1830d88eb 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleOutputParsersMessagesImportingTest.kt +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleOutputParsersMessagesImportingTest.kt @@ -490,6 +490,8 @@ class GradleOutputParsersMessagesImportingTest : GradleOutputParsersMessagesImpo val text = it.lineSequence() .dropWhile { s -> s == "Starting Gradle Daemon..." || s.startsWith("Gradle Daemon started in") + || s == "" + || s.startsWith("CONFIGURE SUCCESSFUL") || s.startsWith("Download ") } .joinToString(separator = "\n") diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/syncAction/GradlePhasedSyncTest.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/syncAction/GradlePhasedSyncTest.kt index 766964746cbc..28fa3c633f40 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/syncAction/GradlePhasedSyncTest.kt +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/syncAction/GradlePhasedSyncTest.kt @@ -174,6 +174,7 @@ class GradlePhasedSyncTest : GradlePhasedSyncTestCase() { val syncPhaseCompletionAssertions = ListenerAssertion() val allPhases = DEFAULT_SYNC_PHASES + val allStaticPhases = allPhases.filterIsInstance() val allDynamicPhases = allPhases.filterIsInstance() val completedPhases = CopyOnWriteArrayList() @@ -192,12 +193,17 @@ class GradlePhasedSyncTest : GradlePhasedSyncTestCase() { whenSyncPhaseCompleted(phase, disposable) { _ -> syncPhaseCompletionAssertions.trace { val completedStaticPhases = completedPhases.filterIsInstance() + val completedBaseScriptPhases = completedPhases.filterIsInstance() val completedDynamicPhases = completedPhases.filterIsInstance() val expectedEntities = when (phase) { is GradleSyncPhase.Static -> when (isSecondarySync) { true -> completedStaticPhases + allDynamicPhases else -> completedStaticPhases } + is GradleSyncPhase.BaseScript -> when (isSecondarySync) { + true -> allStaticPhases + completedBaseScriptPhases + allDynamicPhases + else -> completedBaseScriptPhases + } is GradleSyncPhase.Dynamic -> when (isSecondarySync) { true -> allDynamicPhases else -> completedDynamicPhases @@ -259,9 +265,11 @@ class GradlePhasedSyncTest : GradlePhasedSyncTestCase() { whenSyncPhaseCompleted(phase, disposable) { _ -> syncPhaseCompletionAssertions.trace { val completedStaticPhases = completedPhases.filterIsInstance() + val completedBaseScriptPhases = completedPhases.filterIsInstance() val completedDynamicPhases = completedPhases.filterIsInstance() val expectedEntities = when (phase) { is GradleSyncPhase.Static -> completedStaticPhases + is GradleSyncPhase.BaseScript -> completedBaseScriptPhases is GradleSyncPhase.Dynamic -> completedDynamicPhases } WorkspaceAssertions.assertEntities(myProject, expectedEntities.map { GradleTestEntityId(it) }) { diff --git a/plugins/gradle/tooling-extension-api/src/com/intellij/gradle/toolingExtension/modelAction/GradleModelFetchPhase.kt b/plugins/gradle/tooling-extension-api/src/com/intellij/gradle/toolingExtension/modelAction/GradleModelFetchPhase.kt index 653640b2f36c..818113ae0572 100644 --- a/plugins/gradle/tooling-extension-api/src/com/intellij/gradle/toolingExtension/modelAction/GradleModelFetchPhase.kt +++ b/plugins/gradle/tooling-extension-api/src/com/intellij/gradle/toolingExtension/modelAction/GradleModelFetchPhase.kt @@ -79,13 +79,19 @@ sealed interface GradleModelFetchPhase : Comparable, Seri @JvmField val PROJECT_SOURCE_SET_DEPENDENCY_PHASE: GradleModelFetchPhase = BuildFinished(2000, "DEPENDENCY_MODEL_PHASE") + /** + * + */ + @JvmField + val SCRIPT_MODEL_PHASE: GradleModelFetchPhase = BuildFinished(3000, "SCRIPT_MODEL_PHASE") + /** * In this phase, Gradle model providers fetch rest of Gradle models, which needed for rich experience in IntelliJ IDEA. * It is a code insight in Gradle scripts, data for run configuration creation and for code completion in him, * data for code profiling, etc. */ @JvmField - val ADDITIONAL_MODEL_PHASE: GradleModelFetchPhase = BuildFinished(3000, "ADDITIONAL_MODEL_PHASE") + val ADDITIONAL_MODEL_PHASE: GradleModelFetchPhase = BuildFinished(4000, "ADDITIONAL_MODEL_PHASE") } } diff --git a/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/modelAction/GradleModelId.java b/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/modelAction/GradleModelId.java index 88b1bf2cab44..d876c1ab93ef 100644 --- a/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/modelAction/GradleModelId.java +++ b/plugins/gradle/tooling-extension-impl/src/com/intellij/gradle/toolingExtension/impl/modelAction/GradleModelId.java @@ -70,6 +70,12 @@ public final class GradleModelId implements Serializable { return new GradleModelId(classId, buildId, projectId); } + public static @NotNull GradleModelId createRootModelId(@NotNull Class modelClass) { + String classId = createClassId(modelClass); + String projectId = ":"; + return new GradleModelId(classId, "", projectId); + } + public static @NotNull GradleModelId createProjectModelId(@NotNull ProjectModel projectModel, @NotNull Class modelClass) { ProjectIdentifier projectIdentifier = getProjectIdentifier(projectModel); BuildIdentifier buildIdentifier = projectIdentifier.getBuildIdentifier(); diff --git a/plugins/kotlin/base/scripting/scripting.k2/BUILD.bazel b/plugins/kotlin/base/scripting/scripting.k2/BUILD.bazel index 16949d7af7e5..773a3b13f72c 100644 --- a/plugins/kotlin/base/scripting/scripting.k2/BUILD.bazel +++ b/plugins/kotlin/base/scripting/scripting.k2/BUILD.bazel @@ -63,6 +63,7 @@ jvm_library( "//plugins/kotlin/base/scripting/scripting.shared", "//plugins/kotlin/highlighting/highlighting-shared:kotlin-highlighting-shared", "//platform/lang-core", + "//platform/backend/observation", ] ) @@ -130,6 +131,8 @@ jvm_library( "//platform/testFramework", "//platform/testFramework:testFramework_test_lib", "//plugins/kotlin/base/test:test_test_lib", + "//platform/backend/observation", + "//platform/backend/observation:observation_test_lib", ] ) ### auto-generated section `build intellij.kotlin.base.scripting` end diff --git a/plugins/kotlin/base/scripting/scripting.k2/intellij.kotlin.base.scripting.iml b/plugins/kotlin/base/scripting/scripting.k2/intellij.kotlin.base.scripting.iml index 3194688033e3..2ac9851d840f 100644 --- a/plugins/kotlin/base/scripting/scripting.k2/intellij.kotlin.base.scripting.iml +++ b/plugins/kotlin/base/scripting/scripting.k2/intellij.kotlin.base.scripting.iml @@ -63,5 +63,6 @@ + \ No newline at end of file diff --git a/plugins/kotlin/base/scripting/scripting.k2/src/org/jetbrains/kotlin/idea/core/script/k2/highlighting/KotlinScriptResolutionService.kt b/plugins/kotlin/base/scripting/scripting.k2/src/org/jetbrains/kotlin/idea/core/script/k2/highlighting/KotlinScriptResolutionService.kt index ace9a9728ed0..94638ddf6bed 100644 --- a/plugins/kotlin/base/scripting/scripting.k2/src/org/jetbrains/kotlin/idea/core/script/k2/highlighting/KotlinScriptResolutionService.kt +++ b/plugins/kotlin/base/scripting/scripting.k2/src/org/jetbrains/kotlin/idea/core/script/k2/highlighting/KotlinScriptResolutionService.kt @@ -8,17 +8,19 @@ import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.relativizeToClosestAncestor import com.intellij.openapi.util.io.toNioPathOrNull +import com.intellij.platform.backend.observation.launchTracked import com.intellij.util.application +import com.intellij.util.concurrency.ThreadingAssertions import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import org.jetbrains.kotlin.analysis.api.platform.modification.publishGlobalModuleStateModificationEvent import org.jetbrains.kotlin.analysis.api.platform.modification.publishGlobalScriptModuleStateModificationEvent import org.jetbrains.kotlin.idea.core.script.k2.configurations.getConfigurationResolver import org.jetbrains.kotlin.idea.core.script.k2.configurations.getWorkspaceModelManager -import org.jetbrains.kotlin.idea.core.script.k2.highlighting.KotlinScriptResolutionService.Companion.dropKotlinScriptCaches +import org.jetbrains.kotlin.idea.core.script.k2.highlighting.KotlinScriptResolutionService.Companion.dropKotlinScriptCachesUnderWriteAction import org.jetbrains.kotlin.idea.core.script.shared.KotlinScriptProcessingFilter import org.jetbrains.kotlin.idea.core.script.v1.ScriptDependenciesModificationTracker import org.jetbrains.kotlin.idea.core.script.v1.alwaysVirtualFile +import org.jetbrains.kotlin.idea.core.script.v1.awaitExternalSystemInitialization import org.jetbrains.kotlin.idea.core.script.v1.scriptingDebugLog import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition @@ -56,7 +58,8 @@ class KotlinScriptResolutionService( * @param ktFile the Kotlin script to be processed. */ fun launchProcessing(ktFile: KtFile) { - coroutineScope.launch { + coroutineScope.launchTracked { + project.awaitExternalSystemInitialization() process(listOf(ktFile)) } } @@ -112,7 +115,7 @@ class KotlinScriptResolutionService( * - Tries to get an existing configuration for the file; if absent, creates it. * - Accumulates configurations and applies them to the workspace model in a single batch update. * - * After updating the model, Kotlin script-related caches are dropped via [dropKotlinScriptCaches]. + * After updating the model, Kotlin script-related caches are dropped via [dropKotlinScriptCachesUnderWriteAction]. * * @param definitionByFile mapping of script files to their resolved [ScriptDefinition]. */ @@ -128,23 +131,29 @@ class KotlinScriptResolutionService( } assert(!application.isWriteAccessAllowed) - projectModelUpdater.updateWorkspaceModel(configurationPerVirtualFile) - dropKotlinScriptCaches(project) + dropKotlinScriptCachesUnderWriteAction(project) + projectModelUpdater.updateWorkspaceModel(configurationPerVirtualFile) } companion object { @JvmStatic fun getInstance(project: Project): KotlinScriptResolutionService = project.service() - suspend fun dropKotlinScriptCaches(project: Project) { + suspend fun dropKotlinScriptCachesUnderWriteAction(project: Project) { + edtWriteAction { + dropKotlinScriptCaches(project) + } + } + + fun dropKotlinScriptCaches(project: Project) { + ThreadingAssertions.assertWriteAccess() + ScriptDependenciesModificationTracker.getInstance(project).incModificationCount() HighlightingSettingsPerFile.getInstance(project).incModificationCount() - edtWriteAction { - project.publishGlobalModuleStateModificationEvent() - project.publishGlobalScriptModuleStateModificationEvent() - } + project.publishGlobalModuleStateModificationEvent() + project.publishGlobalScriptModuleStateModificationEvent() } } } \ No newline at end of file diff --git a/plugins/kotlin/base/scripting/scripting.shared/BUILD.bazel b/plugins/kotlin/base/scripting/scripting.shared/BUILD.bazel index d277f7cd98ac..23982f6504ca 100644 --- a/plugins/kotlin/base/scripting/scripting.shared/BUILD.bazel +++ b/plugins/kotlin/base/scripting/scripting.shared/BUILD.bazel @@ -41,6 +41,8 @@ jvm_library( "//plugins/kotlin/highlighting/highlighting-shared:kotlin-highlighting-shared", "//java/java-psi-impl:psi-impl", "//platform/indexing-api:indexing", + "//platform/external-system-impl:externalSystem-impl", + "//platform/external-system-api:externalSystem", ] ) ### auto-generated section `build intellij.kotlin.base.scripting.shared` end \ No newline at end of file diff --git a/plugins/kotlin/base/scripting/scripting.shared/intellij.kotlin.base.scripting.shared.iml b/plugins/kotlin/base/scripting/scripting.shared/intellij.kotlin.base.scripting.shared.iml index d68ccad46e16..77c624d34730 100644 --- a/plugins/kotlin/base/scripting/scripting.shared/intellij.kotlin.base.scripting.shared.iml +++ b/plugins/kotlin/base/scripting/scripting.shared/intellij.kotlin.base.scripting.shared.iml @@ -35,5 +35,7 @@ + + \ No newline at end of file diff --git a/plugins/kotlin/base/scripting/scripting.shared/src/org/jetbrains/kotlin/idea/core/script/v1/scriptUtils.kt b/plugins/kotlin/base/scripting/scripting.shared/src/org/jetbrains/kotlin/idea/core/script/v1/scriptUtils.kt index 7ac634835c81..2a639b3cb79d 100644 --- a/plugins/kotlin/base/scripting/scripting.shared/src/org/jetbrains/kotlin/idea/core/script/v1/scriptUtils.kt +++ b/plugins/kotlin/base/scripting/scripting.shared/src/org/jetbrains/kotlin/idea/core/script/v1/scriptUtils.kt @@ -5,11 +5,14 @@ package org.jetbrains.kotlin.idea.core.script.v1 import com.intellij.openapi.application.Application import com.intellij.openapi.application.runReadAction import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager +import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiManager +import kotlinx.coroutines.suspendCancellableCoroutine import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.psi.KtFile @@ -67,4 +70,12 @@ fun Project.getKtFile(virtualFile: VirtualFile?, ktFile: KtFile? = null): KtFile } else { return runReadAction { PsiManager.getInstance(this).findFile(virtualFile) as? KtFile } } +} + +suspend fun Project.awaitExternalSystemInitialization() { + suspendCancellableCoroutine { continuation -> + ExternalProjectsManagerImpl.getInstance(this).runWhenInitialized { + continuation.resumeWith(Result.success(Unit)) + } + } } \ No newline at end of file diff --git a/plugins/kotlin/gradle/gradle-java/k2/test/org/jetbrains/kotlin/idea/k2/codeInsight/gradle/navigation/K2GradleResilientHighlightingTest.kt b/plugins/kotlin/gradle/gradle-java/k2/test/org/jetbrains/kotlin/idea/k2/codeInsight/gradle/navigation/K2GradleResilientHighlightingTest.kt new file mode 100644 index 000000000000..5b4c8c539fa0 --- /dev/null +++ b/plugins/kotlin/gradle/gradle-java/k2/test/org/jetbrains/kotlin/idea/k2/codeInsight/gradle/navigation/K2GradleResilientHighlightingTest.kt @@ -0,0 +1,12 @@ +// 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.kotlin.idea.k2.codeInsight.gradle.navigation + +import org.jetbrains.kotlin.gradle.AbstractKotlinGradleResilientHighlightingTest +import org.jetbrains.kotlin.idea.test.AssertKotlinPluginMode +import org.jetbrains.kotlin.idea.test.UseK2PluginMode +import org.jetbrains.plugins.gradle.testFramework.fixtures.application.GradleProjectTestApplication + +@UseK2PluginMode +@GradleProjectTestApplication +@AssertKotlinPluginMode +class K2GradleResilientHighlightingTest : AbstractKotlinGradleResilientHighlightingTest() \ No newline at end of file diff --git a/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/gradle/AbstractKotlinGradleResilientHighlightingTest.kt b/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/gradle/AbstractKotlinGradleResilientHighlightingTest.kt new file mode 100644 index 000000000000..6c0c7cd0fe75 --- /dev/null +++ b/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/gradle/AbstractKotlinGradleResilientHighlightingTest.kt @@ -0,0 +1,92 @@ +// 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.kotlin.gradle + +import com.intellij.testFramework.TestDataPath +import com.intellij.testFramework.runInEdtAndWait +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.idea.base.plugin.useK2Plugin +import org.jetbrains.kotlin.idea.base.test.IgnoreTests +import org.jetbrains.kotlin.idea.base.test.TestRoot +import org.jetbrains.kotlin.idea.codeMetaInfo.renderConfigurations.HighlightingConfiguration +import org.jetbrains.kotlin.idea.highlighter.CHECK_SYMBOL_NAMES +import org.jetbrains.kotlin.idea.highlighter.checkHighlighting +import org.jetbrains.kotlin.idea.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestMetadata +import org.jetbrains.plugins.gradle.frameworkSupport.GradleDsl +import org.jetbrains.plugins.gradle.testFramework.GradleTestFixtureBuilder +import org.jetbrains.plugins.gradle.testFramework.annotations.BaseGradleVersionSource +import org.jetbrains.plugins.gradle.testFramework.util.withBuildFile +import org.jetbrains.plugins.gradle.testFramework.util.withSettingsFile +import org.junit.jupiter.params.ParameterizedTest +import java.io.File + +@TestRoot("idea/tests/testData/") +@TestDataPath($$"$CONTENT_ROOT") +@TestMetadata("../../../idea/tests/testData/gradle/highlighting/") +abstract class AbstractKotlinGradleResilientHighlightingTest : AbstractGradleCodeInsightTest() { + + @ParameterizedTest + @BaseGradleVersionSource + @TestMetadata("resilient/simple.test") + fun testSimple(gradleVersion: GradleVersion) { + verifyHighlighting(gradleVersion) + } + + private val outputFileExtensions: List = listOfNotNull(".highlighting.k2".takeIf { useK2Plugin == true }, ".highlighting") + + private fun verifyHighlighting(gradleVersion: GradleVersion) { + test(gradleVersion, GRADLE_KOTLIN_FIXTURE_WITH_ERROR) { + val mainFile = mainTestDataPsiFile + + val ktsFileUnderTest = mainFile.virtualFile.toNioPath().toFile() + val path = ktsFileUnderTest.path + val ktsFileHighlighting = outputFileExtensions.firstNotNullOfOrNull { ext -> + val resolveSibling = ktsFileUnderTest.resolveSibling("$path$ext") + resolveSibling.takeIf(File::exists) + } ?: error("highlighting file does not exist for ${ktsFileUnderTest.path}") + + val directives = KotlinTestUtils.parseDirectives(mainTestDataFile.content).also { + it.put(CHECK_SYMBOL_NAMES, true.toString()) + } + + runInEdtAndWait { + IgnoreTests.runTestIfNotDisabledByFileDirective( + mainFile.virtualFile.toNioPath(), + if (useK2Plugin == true) IgnoreTests.DIRECTIVES.IGNORE_K2 else IgnoreTests.DIRECTIVES.IGNORE_K1 + ) { + checkHighlighting( + mainFile, + ktsFileHighlighting, + directives, + project, + highlightWarnings = true, + severityOption = HighlightingConfiguration.SeverityRenderingOption.ALWAYS + ) + } + } + } + } + + companion object { + val GRADLE_KOTLIN_FIXTURE_WITH_ERROR: GradleTestFixtureBuilder = GradleTestFixtureBuilder.create("GradleKotlinFixture") { gradleVersion -> + withSettingsFile(gradleVersion, gradleDsl = GradleDsl.KOTLIN) { + setProjectName("GradleKotlinFixture") + include(":module1") + enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + } + withBuildFile(gradleVersion, gradleDsl = GradleDsl.KOTLIN) { + call("some-error-code") + } + withBuildFile(gradleVersion, "module1", gradleDsl = GradleDsl.KOTLIN) { + withKotlinDsl() + withMavenCentral() + } + withFile( + "gradle.properties", """ + kotlin.code.style=official + """.trimIndent() + ) + } + } + +} \ No newline at end of file diff --git a/plugins/kotlin/gradle/gradle-tooling/impl/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinDslScriptModelProvider.kt b/plugins/kotlin/gradle/gradle-tooling/impl/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinDslScriptModelProvider.kt index 9ddd497fbd2a..188187f98995 100644 --- a/plugins/kotlin/gradle/gradle-tooling/impl/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinDslScriptModelProvider.kt +++ b/plugins/kotlin/gradle/gradle-tooling/impl/src/org/jetbrains/kotlin/idea/gradleTooling/KotlinDslScriptModelProvider.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.idea.gradleTooling +import com.intellij.gradle.toolingExtension.modelAction.GradleModelFetchPhase import org.gradle.tooling.BuildController import org.gradle.tooling.model.gradle.GradleBuild import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel @@ -9,8 +10,11 @@ import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider.GradleModelConsumer class KotlinDslScriptModelProvider : ProjectImportModelProvider { + private val kotlinDslScriptModelClass: Class<*> = KotlinDslScriptsModel::class.java + override fun getPhase(): GradleModelFetchPhase = GradleModelFetchPhase.SCRIPT_MODEL_PHASE + override fun populateModels( controller: BuildController, buildModels: Collection, diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/GradleScriptDefinitionsProvider.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/GradleScriptDefinitionsProvider.kt index 60a24fd01bbf..295f9d752923 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/GradleScriptDefinitionsProvider.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/GradleScriptDefinitionsProvider.kt @@ -6,9 +6,10 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.progress.runBlockingMaybeCancellable import com.intellij.openapi.project.Project import org.jetbrains.kotlin.gradle.scripting.k1.roots.GradleBuildRootsLocatorImpl -import org.jetbrains.kotlin.gradle.scripting.shared.GradleDefinitionsParams import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptingBundle import org.jetbrains.kotlin.gradle.scripting.shared.definition.ErrorGradleScriptDefinition +import org.jetbrains.kotlin.gradle.scripting.shared.definition.GradleDefinitionsParams +import org.jetbrains.kotlin.gradle.scripting.shared.definition.loadGradleDefinitions import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootsLocator import org.jetbrains.kotlin.gradle.scripting.shared.roots.Imported import org.jetbrains.kotlin.gradle.scripting.shared.roots.WithoutScriptModels @@ -124,7 +125,7 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD return emptyList() } - return org.jetbrains.kotlin.gradle.scripting.shared.loadGradleDefinitions( + return loadGradleDefinitions( GradleDefinitionsParams( workingDir = root.workingDir, gradleHome = root.gradleHome, diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/roots/GradleBuildRootDataSerializer.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/roots/GradleBuildRootDataSerializer.kt index 9d6d86b43d52..f594435330d9 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/roots/GradleBuildRootDataSerializer.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/roots/GradleBuildRootDataSerializer.kt @@ -3,26 +3,21 @@ package org.jetbrains.kotlin.gradle.scripting.k1.roots import com.intellij.openapi.util.IntellijInternalApi import com.intellij.util.io.DataExternalizer -import org.jetbrains.kotlin.gradle.scripting.shared.GradleKotlinScriptConfigurationInputs -import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel -import org.jetbrains.kotlin.gradle.scripting.shared.roots.* -import org.jetbrains.kotlin.idea.core.script.v1.readString +import org.jetbrains.kotlin.gradle.scripting.shared.roots.AbstractGradleBuildRootDataSerializer +import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootData +import org.jetbrains.kotlin.gradle.scripting.shared.roots.StringsPool +import org.jetbrains.kotlin.gradle.scripting.shared.roots.writeList import org.jetbrains.kotlin.idea.core.script.v1.writeString import java.io.DataInput import java.io.DataOutput class GradleBuildRootDataSerializer : AbstractGradleBuildRootDataSerializer() { - override val externalizer: DataExternalizer - get() = object : DataExternalizer { - override fun save(out: DataOutput, value: GradleBuildRootData) = - writeKotlinDslScriptModels(out, value) - - override fun read(`in`: DataInput): GradleBuildRootData = - readKotlinDslScriptModels(`in`, currentBuildRoot.get().path) + override fun getExternalizer(): DataExternalizer = object : DataExternalizer { + override fun save(out: DataOutput, value: GradleBuildRootData) = writeKotlinDslScriptModels(out, value) + override fun read(`in`: DataInput): GradleBuildRootData = readKotlinDslScriptModels(`in`, currentBuildRoot.get().path) } companion object { - @IntellijInternalApi fun writeKotlinDslScriptModels(output: DataOutput, data: GradleBuildRootData) { val strings = StringsPool.writer(output) @@ -49,28 +44,6 @@ class GradleBuildRootDataSerializer : AbstractGradleBuildRootDataSerializer() { strings.writeStringIds(it.imports) } } - - @IntellijInternalApi - fun readKotlinDslScriptModels(input: DataInput, buildRoot: String): GradleBuildRootData { - val strings = StringsPool.reader(input) - - val importTs = input.readLong() - val projectRoots = strings.readStrings() - val gradleHome = strings.readString() - val javaHome = strings.readNullableString() - val models = input.readList { - KotlinDslScriptModel( - strings.readString(), - GradleKotlinScriptConfigurationInputs(input.readString(), input.readLong(), buildRoot), - strings.readStrings(), - strings.readStrings(), - strings.readStrings(), - listOf() - ) - } - - return GradleBuildRootData(importTs, projectRoots, gradleHome, javaHome, models) - } } } \ No newline at end of file diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/roots/GradleBuildRootsLocatorImpl.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/roots/GradleBuildRootsLocatorImpl.kt index 214e1958cb42..94579cadc478 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/roots/GradleBuildRootsLocatorImpl.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/src/org/jetbrains/kotlin/gradle/scripting/k1/roots/GradleBuildRootsLocatorImpl.kt @@ -16,7 +16,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.jetbrains.kotlin.gradle.scripting.k1.GradleScriptDefinitionsContributor import org.jetbrains.kotlin.gradle.scripting.k1.roots.GradleScriptingSupport.Companion.isApplicable -import org.jetbrains.kotlin.gradle.scripting.shared.getDefinitionsTemplateClasspath +import org.jetbrains.kotlin.gradle.scripting.shared.definition.getFullDefinitionsClasspath +import org.jetbrains.kotlin.gradle.scripting.shared.definition.toGradleHomePath import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel import org.jetbrains.kotlin.gradle.scripting.shared.kotlinDslScriptsModelImportSupported import org.jetbrains.kotlin.gradle.scripting.shared.roots.* @@ -26,6 +27,7 @@ import org.jetbrains.kotlin.idea.core.script.k1.configuration.DefaultScriptingSu import org.jetbrains.kotlin.idea.core.script.k1.configuration.ScriptingSupport import org.jetbrains.kotlin.idea.core.script.k1.settings.KotlinScriptingSettingsImpl import org.jetbrains.kotlin.idea.core.script.k1.ucache.ScriptClassRootsBuilder +import org.jetbrains.kotlin.idea.core.script.v1.scriptingInfoLog import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition @@ -35,6 +37,7 @@ import org.jetbrains.plugins.gradle.settings.GradleProjectSettings import java.nio.file.Path import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicBoolean +import kotlin.io.path.invariantSeparatorsPathString /** * [org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRoot] is a linked gradle build (don't confuse with gradle project and included build). @@ -330,10 +333,18 @@ class GradleScriptingSupport(val project: Project) : ScriptingSupport { return definitions.firstOrNull { it.isScript(scriptSource) } } + private fun getDefinitionsTemplateClasspath(gradleHome: String?): List = try { + getFullDefinitionsClasspath(gradleHome.toGradleHomePath()).map { it.invariantSeparatorsPathString } + } catch (e: Throwable) { + scriptingInfoLog("cannot get gradle classpath for Gradle Kotlin DSL scripts: ${e.message}") + + emptyList() + } + companion object { fun isApplicable(file: VirtualFile, project: Project): Boolean { val support = ScriptingSupport.EP_NAME.findExtension(GradleScriptingSupport::class.java, project) ?: return false return support.isApplicable(file) } } -} \ No newline at end of file +} diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/test/org/jetbrains/kotlin/gradle/scripting/k1/gradle/GradleBuildRootDataSerializerTest.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/test/org/jetbrains/kotlin/gradle/scripting/k1/gradle/GradleBuildRootDataSerializerTest.kt index e0fb5100026c..dbaeb884a08d 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/test/org/jetbrains/kotlin/gradle/scripting/k1/gradle/GradleBuildRootDataSerializerTest.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k1/test/org/jetbrains/kotlin/gradle/scripting/k1/gradle/GradleBuildRootDataSerializerTest.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.scripting.k1.gradle import org.jetbrains.kotlin.gradle.scripting.k1.roots.GradleBuildRootDataSerializer import org.jetbrains.kotlin.gradle.scripting.shared.GradleKotlinScriptConfigurationInputs import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel +import org.jetbrains.kotlin.gradle.scripting.shared.roots.AbstractGradleBuildRootDataSerializer import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootData import org.junit.Test import java.io.ByteArrayInputStream @@ -44,7 +45,7 @@ class GradleBuildRootDataSerializerTest { val buffer = ByteArrayOutputStream() GradleBuildRootDataSerializer.writeKotlinDslScriptModels(DataOutputStream(buffer), data) - val restored = GradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a") + val restored = AbstractGradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a") assertEquals(data.toString(), restored.toString()) } @@ -62,7 +63,7 @@ class GradleBuildRootDataSerializerTest { val buffer = ByteArrayOutputStream() GradleBuildRootDataSerializer.writeKotlinDslScriptModels(DataOutputStream(buffer), data) - val restored = GradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a") + val restored = AbstractGradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a") assertEquals(data.toString(), restored.toString()) } diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/GradleScriptDefinitionEntityModifications.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/GradleScriptDefinitionEntityModifications.kt index 10c35fa9f405..d58cfc4d8493 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/GradleScriptDefinitionEntityModifications.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/GradleScriptDefinitionEntityModifications.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel +import com.intellij.openapi.util.NlsSafe import com.intellij.platform.workspace.storage.* import org.jetbrains.kotlin.idea.core.script.k2.modules.ScriptCompilationConfigurationEntity import org.jetbrains.kotlin.idea.core.script.k2.modules.ScriptEvaluationConfigurationEntity diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/impl/GradleScriptDefinitionEntityImpl.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/impl/GradleScriptDefinitionEntityImpl.kt index 5531fcf5f8a8..7d686f9a3896 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/impl/GradleScriptDefinitionEntityImpl.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/impl/GradleScriptDefinitionEntityImpl.kt @@ -1,6 +1,7 @@ // 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.kotlin.gradle.scripting.k2.workspaceModel.impl +import com.intellij.openapi.util.NlsSafe import com.intellij.platform.workspace.storage.* import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/impl/MetadataStorageImpl.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/impl/MetadataStorageImpl.kt index e447f1ffc01a..6ad436331088 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/impl/MetadataStorageImpl.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/gen/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/impl/MetadataStorageImpl.kt @@ -16,6 +16,16 @@ internal object MetadataStorageImpl : MetadataStorageBase() { var typeMetadata: StorageTypeMetadata + typeMetadata = FinalClassMetadata.ObjectMetadata( + fqName = "org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource", properties = listOf( + OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "virtualFileUrl", + valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = true, + typeMetadata = FinalClassMetadata.KnownClass( + fqName = "com.intellij.platform.workspace.storage.url.VirtualFileUrl")), + withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.EntitySource")) + + addMetadata(typeMetadata) + typeMetadata = FinalClassMetadata.ClassMetadata( fqName = "org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.GradleScriptDefinitionEntityId", properties = listOf( OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "id", valueType = primitiveTypeStringNotNullable, @@ -128,6 +138,9 @@ internal object MetadataStorageImpl : MetadataStorageBase() { metadataHash = -362496579) addMetadataHash(typeFqn = "org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.GradleScriptDefinitionEntityId", metadataHash = -1956757513) + addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.EntitySource", metadataHash = 1287228111) + addMetadataHash(typeFqn = "org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource", + metadataHash = -532489477) addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.SymbolicEntityId", metadataHash = 1136338439) } diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/resources/intellij.kotlin.gradle.scripting.xml b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/resources/intellij.kotlin.gradle.scripting.xml index 957c5a7f0d5e..de9ad75a1547 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/resources/intellij.kotlin.gradle.scripting.xml +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/resources/intellij.kotlin.gradle.scripting.xml @@ -16,6 +16,7 @@ + diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/GradleKotlinScriptService.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/GradleKotlinScriptService.kt index 2fa150753eb9..c38c31cc6878 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/GradleKotlinScriptService.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/GradleKotlinScriptService.kt @@ -18,16 +18,17 @@ import com.intellij.platform.workspace.storage.MutableEntityStorage import com.intellij.platform.workspace.storage.VersionedStorageChange import com.intellij.platform.workspace.storage.toBuilder import com.intellij.platform.workspace.storage.url.VirtualFileUrl +import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager import kotlinx.coroutines.CoroutineScope import org.jetbrains.kotlin.gradle.scripting.k2.definition.withIdeKeys import org.jetbrains.kotlin.gradle.scripting.k2.importing.GradleScriptData import org.jetbrains.kotlin.gradle.scripting.k2.importing.GradleScriptModel import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.GradleScriptDefinitionEntity +import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.asEntity import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.deserialize -import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptEntitySource import org.jetbrains.kotlin.gradle.scripting.shared.definition.GradleScriptDefinition -import org.jetbrains.kotlin.gradle.scripting.shared.loadGradleDefinitions +import org.jetbrains.kotlin.gradle.scripting.shared.definition.loadGradleDefinitions import org.jetbrains.kotlin.idea.core.script.k2.configurations.sdkId import org.jetbrains.kotlin.idea.core.script.k2.configurations.toVirtualFileUrl import org.jetbrains.kotlin.idea.core.script.k2.definitions.ScriptDefinitionsModificationTracker @@ -37,7 +38,6 @@ import org.jetbrains.kotlin.idea.core.script.v1.indexSourceRootsEagerly import org.jetbrains.kotlin.idea.core.script.v1.scriptingDebugLog import org.jetbrains.kotlin.idea.core.script.v1.scriptingWarnLog import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition -import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.resolve.* import org.jetbrains.kotlin.utils.addIfNotNull import java.io.File @@ -48,65 +48,91 @@ import kotlin.script.experimental.jvm.JvmDependency import kotlin.script.experimental.jvm.jdkHome import kotlin.script.experimental.jvm.jvm +private sealed interface CachedCompilationConfigurationResult { + data class Some(val value: ScriptCompilationConfigurationResult) : CachedCompilationConfigurationResult + data object None : CachedCompilationConfigurationResult +} + +private fun ScriptCompilationConfigurationResult?.cached(): CachedCompilationConfigurationResult = + if (this == null) CachedCompilationConfigurationResult.None else CachedCompilationConfigurationResult.Some(this) + @Service(Service.Level.PROJECT) class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurationResolver, ScriptWorkspaceModelManager { - private val data = ConcurrentHashMap() + private val data = ConcurrentHashMap() - override suspend fun create(virtualFile: VirtualFile, definition: ScriptDefinition): ScriptCompilationConfigurationResult? = null + private val urlManager: VirtualFileUrlManager + get() = project.workspaceModel.getVirtualFileUrlManager() - override fun get(virtualFile: VirtualFile): ScriptCompilationConfigurationResult? { - val url = virtualFile.toVirtualFileUrl(project.workspaceModel.getVirtualFileUrlManager()) - val result = data.computeIfAbsent(url) { url -> - val entity = project.workspaceModel.currentSnapshot.getVirtualFileUrlIndex().findEntitiesByUrl(url) - .firstOrNull { - it.entitySource is KotlinGradleScriptEntitySource && it is KotlinScriptEntity - } as? KotlinScriptEntity + private val VirtualFile.virtualFileUrl: VirtualFileUrl + get() = toVirtualFileUrl(urlManager) - entity?.toConfigurationResult() ?: ResultWithDiagnostics.Failure() - } + override suspend fun create(virtualFile: VirtualFile, definition: ScriptDefinition): ScriptCompilationConfigurationResult { + val updatedConfiguration = refineScriptCompilationConfiguration(VirtualFileScriptSource(virtualFile), definition, project) + data[virtualFile.virtualFileUrl] = updatedConfiguration.cached() - return if (result == ResultWithDiagnostics.Failure()) null else result + return updatedConfiguration } - fun processScripts( + override fun get(virtualFile: VirtualFile): ScriptCompilationConfigurationResult? { + val wrapped = data.computeIfAbsent(virtualFile.virtualFileUrl) { key -> + val entity = project.workspaceModel.currentSnapshot.getVirtualFileUrlIndex().findEntitiesByUrl(key) + .singleOrNull { it.entitySource is KotlinGradleScriptEntitySource && it is KotlinScriptEntity } as? KotlinScriptEntity + + entity?.toConfigurationResult().cached() + } + + return (wrapped as? CachedCompilationConfigurationResult.Some)?.value + } + + fun updateStorage( scriptData: GradleScriptData, storageToUpdate: MutableEntityStorage ) { - val updatedStorage = MutableEntityStorage.create() - + val javaHome = scriptData.definitionsParams.javaHome val definitions = loadGradleDefinitions(scriptData.definitionsParams).map { it.withIdeKeys(project) } - updatedStorage.enrichWithDefinitions(definitions) - updatedStorage.enrichWithConfigurations(scriptData, definitions) - - storageToUpdate.applyChangesFrom(updatedStorage) + val updatedStorage = MutableEntityStorage.create().apply { + updateStorage(this, scriptData.models, definitions, javaHome) + } + storageToUpdate.replaceBySource({ it is KotlinGradleScriptEntitySource }, updatedStorage) } - private fun MutableEntityStorage.enrichWithConfigurations( - scriptsData: GradleScriptData, - definitions: List + fun updateStorage( + storage: MutableEntityStorage, + models: Collection, + definitions: Collection, + javaHome: String? = null ) { - val sdk = scriptsData.definitionsParams.javaHome.resolveSdk() - val javaHomePath = sdk?.homePath?.let { File(it) } + definitions.forEach { + storage addEntity GradleScriptDefinitionEntity( + it.definitionId, + it.compilationConfiguration.asEntity(), + it.hostConfiguration.asEntity(), + KotlinGradleScriptEntitySource + ) { + evaluationConfiguration = it.evaluationConfiguration?.asEntity() + } + } - val configurations = scriptsData.models.associate { gradleScript: GradleScriptModel -> - val sourceCode = VirtualFileScriptSource(gradleScript.virtualFile) - val definition = definitions.firstOrNull { it.isScript(sourceCode) } ?: findScriptDefinition(project, sourceCode) + val javaHomePath = javaHome.resolveSdk()?.homePath?.let { File(it) } + val configurations = mutableMapOf() + + for (model in models) { + val sourceCode = VirtualFileScriptSource(model.virtualFile) + val definition = definitions.firstOrNull { it.isScript(sourceCode) } ?: continue val configuration = definition.compilationConfiguration.with { if (javaHomePath != null) { jvm.jdkHome(javaHomePath) } - defaultImports(gradleScript.imports) - dependencies(JvmDependency(gradleScript.classPath.map { File(it) })) - ide.dependenciesSources(JvmDependency(gradleScript.sourcePath.map { File(it) })) + defaultImports(model.imports) + dependencies(JvmDependency(model.classPath.map { File(it) })) + ide.dependenciesSources(JvmDependency(model.sourcePath.map { File(it) })) }.adjustByDefinition(definition) - val updatedConfiguration = refineScriptCompilationConfiguration(sourceCode, definition, project, configuration) - - gradleScript.virtualFile to updatedConfiguration + configurations[model.virtualFile] = refineScriptCompilationConfiguration(sourceCode, definition, project, configuration) } - enrichStorage(configurations) + storage.enrichStorage(configurations) } private fun String?.resolveSdk(): Sdk? { @@ -120,46 +146,39 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati } override suspend fun updateWorkspaceModel(configurationPerFile: Map) { - if (configurationPerFile.size == 1) { - val currentStorage = project.workspaceModel.currentSnapshot.toBuilder() - project.workspaceModel.update("updating .gradle.kts scripts") { storage -> - currentStorage.enrichStorage(configurationPerFile) // under writeAction from workspaceModel.update - storage.applyChangesFrom(currentStorage) - } + val currentStorage = project.workspaceModel.currentSnapshot.toBuilder() + project.workspaceModel.update("updating .gradle.kts scripts") { storage -> + currentStorage.enrichStorage(configurationPerFile) // under writeAction from workspaceModel.update + storage.applyChangesFrom(currentStorage) } } - private fun MutableEntityStorage.enrichStorage( + fun MutableEntityStorage.enrichStorage( configurations: Map, ) { - val fileUrlManager = project.workspaceModel.getVirtualFileUrlManager() - for ((scriptFile, configurationResult) in configurations) { val configurationWrapper = configurationResult.valueOrNull() ?: continue + if (getVirtualFileUrlIndex().findEntitiesByUrl(scriptFile.virtualFileUrl).any()) continue - val scriptUrl = scriptFile.toVirtualFileUrl(fileUrlManager) - - val classes = configurationWrapper.dependenciesClassPath.sorted().map { it.path.toVirtualFileUrl(fileUrlManager) }.toMutableSet() - val sources = configurationWrapper.dependenciesSources.sorted().map { it.path.toVirtualFileUrl(fileUrlManager) }.toMutableSet() + val classes = + configurationWrapper.dependenciesClassPath.sorted().map { it.path.toVirtualFileUrl(urlManager) }.toMutableSet() + val sources = configurationWrapper.dependenciesSources.sorted().map { it.path.toVirtualFileUrl(urlManager) }.toMutableSet() val dependencies = buildList { addIfNotNull( extractRootsByPredicate(classes, sources) { it.url.contains("kotlin-stdlib") - } - ) + }) addIfNotNull( extractRootsByPredicate(classes, sources) { it.url.contains("accessors") - } - ) + }) addIfNotNull( extractRootsByPredicate(classes, sources) { it.url.contains("kotlin-gradle-plugin") - } - ) + }) if (indexSourceRootsEagerly() || AdvancedSettings.getBoolean("gradle.attach.scripts.dependencies.sources")) { addAll(extractDependenciesWithSources(classes, sources)) @@ -169,21 +188,17 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati addAll( classes.map { getOrCreateScriptLibrary(it, sources) - } - ) + }) } else { addAll( classes.map { getOrCreateScriptLibrary(it) - } - ) + }) } } this addEntity KotlinScriptEntity( - scriptUrl, - dependencies, - KotlinGradleScriptEntitySource + scriptFile.virtualFileUrl, dependencies, KotlinGradleScriptEntitySource ) { this.configuration = configurationWrapper.configuration?.asEntity() this.reports = configurationResult.reports.map { it.toData() }.toMutableList() @@ -193,16 +208,13 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati } private fun MutableEntityStorage.getOrCreateScriptLibrary( - jar: VirtualFileUrl, - sources: Collection + jar: VirtualFileUrl, sources: Collection ): KotlinScriptLibraryEntityId { val id = KotlinScriptLibraryEntityId(listOf(jar), sources.toList()) if (!contains(id)) { this addEntity KotlinScriptLibraryEntity( - id.classes, - id.sources, - KotlinGradleScriptEntitySource + id.classes, id.sources, KotlinGradleScriptEntitySource ) } @@ -216,9 +228,7 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati if (!contains(id)) { this addEntity KotlinScriptLibraryEntity( - id.classes, - id.sources, - KotlinGradleScriptEntitySource + id.classes, id.sources, KotlinGradleScriptEntitySource ) } @@ -275,9 +285,7 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati } private fun MutableEntityStorage.extractRootsByPredicate( - classes: MutableSet, - sources: MutableSet, - predicate: Predicate + classes: MutableSet, sources: MutableSet, predicate: Predicate ): KotlinScriptLibraryEntityId? { val groupedClasses = classes.removeOnMatch(predicate) if (groupedClasses.isEmpty()) return null @@ -305,29 +313,28 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati return removed } - private fun MutableEntityStorage.enrichWithDefinitions(definitions: List) { - definitions.forEach { - this addEntity GradleScriptDefinitionEntity( - it.definitionId, - it.compilationConfiguration.asEntity(), - it.hostConfiguration.asEntity(), - KotlinGradleScriptEntitySource - ) { - evaluationConfiguration = it.evaluationConfiguration?.asEntity() - } - } - } - @Suppress("unused") private class GradleWorkspaceModelListener(val project: Project, val scope: CoroutineScope) : WorkspaceModelChangeListener { - override fun changed(event: VersionedStorageChange) = scope.launchTracked { + override fun beforeChanged(event: VersionedStorageChange) { + val definitionChanges = event.getChanges(GradleScriptDefinitionEntity::class.java) + val configurationChanges = event.getChanges(KotlinScriptEntity::class.java) - if (event.getChanges(GradleScriptDefinitionEntity::class.java).any()) { + if (definitionChanges.any() || configurationChanges.any()) { + KotlinScriptResolutionService.dropKotlinScriptCaches(project) + } + } + + override fun changed(event: VersionedStorageChange) = scope.launchTracked { + val definitionChanges = event.getChanges(GradleScriptDefinitionEntity::class.java) + val configurationChanges = event.getChanges(KotlinScriptEntity::class.java) + if (definitionChanges.none() && configurationChanges.none()) return@launchTracked + + if (definitionChanges.any()) { ScriptDefinitionsModificationTracker.getInstance(project).incModificationCount() } val updatedData = mutableMapOf() - for (entityChange in event.getChanges(KotlinScriptEntity::class.java)) { + for (entityChange in configurationChanges) { if (entityChange is EntityChange.Removed) continue val entity = entityChange.newEntity ?: continue if (entity.entitySource !is KotlinGradleScriptEntitySource) continue @@ -340,7 +347,6 @@ class GradleKotlinScriptService(val project: Project) : ScriptRefinedConfigurati if (updatedData.any()) { getInstance(project).data.clear() - KotlinScriptResolutionService.dropKotlinScriptCaches(project) } } } @@ -360,10 +366,8 @@ fun KotlinScriptEntity.toConfigurationResult(): ScriptCompilationConfigurationRe } else { ResultWithDiagnostics.Success( ScriptCompilationConfigurationWrapper.FromCompilationConfiguration( - VirtualFileScriptSource(virtualFile), - configuration?.deserialize() - ), reports.map { report -> report.toScriptDiagnostic() } - ) + VirtualFileScriptSource(virtualFile), configuration?.deserialize() + ), reports.map { report -> report.toScriptDiagnostic() }) } return result diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/OpenAdvancedSettingsNotificationProvider.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/OpenAdvancedSettingsNotificationProvider.kt index 9b08d5e1dd5f..324685039ab3 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/OpenAdvancedSettingsNotificationProvider.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/OpenAdvancedSettingsNotificationProvider.kt @@ -13,13 +13,13 @@ import com.intellij.platform.backend.workspace.WorkspaceModel import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationPanel.Status import com.intellij.ui.EditorNotificationProvider -import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptEntitySource +import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptingBundle import org.jetbrains.kotlin.idea.core.script.k2.modules.KotlinScriptLibraryEntity import java.util.function.Function import javax.swing.JComponent -class OpenAdvancedSettingsNotificationProvider() : EditorNotificationProvider { +class OpenAdvancedSettingsNotificationProvider : EditorNotificationProvider { override fun collectNotificationData( project: Project, file: VirtualFile diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/ProjectGradleSettingsListener.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/ProjectGradleSettingsListener.kt index 9530cbf095e1..b9d1cef97287 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/ProjectGradleSettingsListener.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/ProjectGradleSettingsListener.kt @@ -2,13 +2,12 @@ package org.jetbrains.kotlin.gradle.scripting.k2 import com.intellij.openapi.application.edtWriteAction -import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl import com.intellij.openapi.project.Project import com.intellij.platform.backend.observation.launchTracked import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.suspendCancellableCoroutine import org.jetbrains.kotlin.gradle.scripting.shared.getGradleVersion import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootsLocator +import org.jetbrains.kotlin.idea.core.script.v1.awaitExternalSystemInitialization import org.jetbrains.plugins.gradle.service.GradleInstallationManager import org.jetbrains.plugins.gradle.settings.DistributionType import org.jetbrains.plugins.gradle.settings.GradleProjectSettings @@ -23,7 +22,7 @@ class ProjectGradleSettingsListener( override fun onProjectsLinked(settings: MutableCollection) { val buildRootsManager = GradleBuildRootsLocator.getInstance(project) coroutineScope.launchTracked { - awaitExternalSystemInitialization() + project.awaitExternalSystemInitialization() settings.forEach { val gradleVersion = getGradleVersion(project, it) edtWriteAction { @@ -37,7 +36,7 @@ class ProjectGradleSettingsListener( override fun onProjectsLoaded(settings: Collection) { val buildRootsManager = GradleBuildRootsLocator.getInstance(project) coroutineScope.launchTracked { - awaitExternalSystemInitialization() + project.awaitExternalSystemInitialization() settings.forEach { val gradleVersion = getGradleVersion(project, it) edtWriteAction { @@ -47,14 +46,6 @@ class ProjectGradleSettingsListener( } } - private suspend fun awaitExternalSystemInitialization() { - suspendCancellableCoroutine { continuation -> - ExternalProjectsManagerImpl.getInstance(project).runWhenInitialized { - continuation.resumeWith(Result.success(Unit)) - } - } - } - override fun onProjectsUnlinked(linkedProjectPaths: MutableSet) { val buildRootsManager = GradleBuildRootsLocator.getInstance(project) diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslBaseScriptSyncContributor.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslBaseScriptSyncContributor.kt new file mode 100644 index 000000000000..6e35f3bd2682 --- /dev/null +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslBaseScriptSyncContributor.kt @@ -0,0 +1,63 @@ +// 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.kotlin.gradle.scripting.k2.importing + +import com.intellij.openapi.application.readAction +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.platform.workspace.storage.ImmutableEntityStorage +import com.intellij.platform.workspace.storage.toBuilder +import org.gradle.tooling.model.dsl.GradleDslBaseScriptModel +import org.jetbrains.kotlin.gradle.scripting.k2.GradleKotlinScriptService +import org.jetbrains.kotlin.gradle.scripting.shared.definition.BaseScriptDefinition +import org.jetbrains.kotlin.gradle.scripting.shared.definition.ErrorGradleScriptDefinition +import org.jetbrains.kotlin.gradle.scripting.shared.definition.getGradleTemplatesNames +import org.jetbrains.kotlin.idea.core.script.shared.definition.loadDefinitionsFromTemplates +import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext +import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncContributor +import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncPhase +import kotlin.script.experimental.api.defaultImports +import kotlin.script.experimental.api.dependencies +import kotlin.script.experimental.jvm.JvmDependency + +internal class KotlinDslBaseScriptSyncContributor : GradleSyncContributor { + override val name: String = "Kotlin DSL Base Script" + + override val phase: GradleSyncPhase = GradleSyncPhase.BASE_SCRIPT_MODEL_PHASE + + override suspend fun createProjectModel( + context: ProjectResolverContext, storage: ImmutableEntityStorage + ): ImmutableEntityStorage { + + val baseModel = context.getRootModel(GradleDslBaseScriptModel::class.java)?.kotlinDslBaseScriptModel ?: return storage + val templateClasspath = (baseModel.scriptTemplatesClassPath + baseModel.compileClassPath).map { it.toPath() } + val templateClasses = getGradleTemplatesNames(context.gradleVersion) + + val definitions = loadDefinitionsFromTemplates(templateClasses, templateClasspath).map { + BaseScriptDefinition(it) + }.ifEmpty { sequenceOf(ErrorGradleScriptDefinition()) }.toList().map { + it.with { + dependencies(JvmDependency(baseModel.compileClassPath)) + defaultImports(baseModel.implicitImports) + } + } + + val builder = storage.toBuilder() + builder.also { storage -> + val models = readAction { + FileEditorManager.getInstance(context.project).allEditors + }.filter { + it.file.name.endsWith(".gradle.kts") + }.map { fileEditor -> + GradleScriptModel( + fileEditor.file, baseModel.compileClassPath.map { it.path }, listOf(), baseModel.implicitImports + ) + } + + GradleKotlinScriptService.getInstance(context.project).updateStorage( + storage, + models, + definitions, + ) + } + return builder.toSnapshot() + } +} \ No newline at end of file diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslScriptSyncContributor.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslScriptSyncContributor.kt index 797a04573d9b..1e4e57eb40c6 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslScriptSyncContributor.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslScriptSyncContributor.kt @@ -6,11 +6,9 @@ import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.platform.eel.provider.getEelDescriptor import com.intellij.platform.eel.provider.utils.asNio import com.intellij.platform.workspace.storage.ImmutableEntityStorage -import com.intellij.platform.workspace.storage.MutableEntityStorage import com.intellij.platform.workspace.storage.toBuilder import org.jetbrains.kotlin.gradle.scripting.k2.GradleKotlinScriptService -import org.jetbrains.kotlin.gradle.scripting.shared.GradleDefinitionsParams -import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptEntitySource +import org.jetbrains.kotlin.gradle.scripting.shared.definition.GradleDefinitionsParams import org.jetbrains.kotlin.gradle.scripting.shared.importing.collectErrors import org.jetbrains.kotlin.gradle.scripting.shared.importing.getKotlinDslScripts import org.jetbrains.kotlin.gradle.scripting.shared.importing.kotlinDslSyncListenerInstance @@ -18,24 +16,11 @@ import org.jetbrains.kotlin.gradle.scripting.shared.importing.saveGradleBuildEnv import org.jetbrains.plugins.gradle.model.GradleBuildScriptClasspathModel import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncContributor -import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncExtension import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncPhase import java.nio.file.Path import kotlin.io.path.pathString -internal class KotlinDslScriptSyncExtension : GradleSyncExtension { - - override fun updateProjectModel( - context: ProjectResolverContext, syncStorage: MutableEntityStorage, projectStorage: MutableEntityStorage, phase: GradleSyncPhase - ) { - if (phase == GradleSyncPhase.ADDITIONAL_MODEL_PHASE) { - projectStorage.replaceBySource({ it is KotlinGradleScriptEntitySource }, syncStorage) - } - } -} - internal class KotlinDslScriptSyncContributor : GradleSyncContributor { - override val name: String = "Kotlin DSL Script" override val phase: GradleSyncPhase = GradleSyncPhase.ADDITIONAL_MODEL_PHASE @@ -95,7 +80,7 @@ internal class KotlinDslScriptSyncContributor : GradleSyncContributor { ) ) - GradleKotlinScriptService.getInstance(project).processScripts(scriptData, builder) + GradleKotlinScriptService.getInstance(project).updateStorage(scriptData, builder) return builder.toSnapshot() } diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslScriptSyncExtension.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslScriptSyncExtension.kt new file mode 100644 index 000000000000..fbfcfbfee715 --- /dev/null +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/importing/KotlinDslScriptSyncExtension.kt @@ -0,0 +1,19 @@ +// 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.kotlin.gradle.scripting.k2.importing + +import com.intellij.platform.workspace.storage.MutableEntityStorage +import org.jetbrains.kotlin.gradle.scripting.k2.workspaceModel.KotlinGradleScriptEntitySource +import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext +import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncExtension +import org.jetbrains.plugins.gradle.service.syncAction.GradleSyncPhase + +internal class KotlinDslScriptSyncExtension : GradleSyncExtension { + + override fun updateProjectModel( + context: ProjectResolverContext, syncStorage: MutableEntityStorage, projectStorage: MutableEntityStorage, phase: GradleSyncPhase + ) { + if (phase == GradleSyncPhase.BASE_SCRIPT_MODEL_PHASE || phase == GradleSyncPhase.ADDITIONAL_MODEL_PHASE) { + projectStorage.replaceBySource({ it is KotlinGradleScriptEntitySource }, syncStorage) + } + } +} \ No newline at end of file diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/roots/GradleBuildRootDataSerializer.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/roots/GradleBuildRootDataSerializer.kt index 1962356b3d3d..7a4b84ad7c90 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/roots/GradleBuildRootDataSerializer.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/roots/GradleBuildRootDataSerializer.kt @@ -10,13 +10,9 @@ import java.io.DataInput import java.io.DataOutput class GradleBuildRootDataSerializer : AbstractGradleBuildRootDataSerializer() { - override val externalizer: DataExternalizer - get() = object : DataExternalizer { - override fun save(out: DataOutput, value: GradleBuildRootData) = - writeKotlinDslScriptModels(out, value) - - override fun read(`in`: DataInput): GradleBuildRootData = - readKotlinDslScriptModels(`in`) + override fun getExternalizer(): DataExternalizer = object : DataExternalizer { + override fun save(out: DataOutput, value: GradleBuildRootData) = writeKotlinDslScriptModels(out, value) + override fun read(`in`: DataInput): GradleBuildRootData = readKotlinDslScriptModels(`in`, currentBuildRoot.get().path) } companion object { @@ -31,18 +27,7 @@ class GradleBuildRootDataSerializer : AbstractGradleBuildRootDataSerializer() { strings.writeStringIds(data.projectRoots) strings.writeStringId(data.gradleHome) strings.writeStringId(data.javaHome) - } - - @IntellijInternalApi - fun readKotlinDslScriptModels(input: DataInput): GradleBuildRootData { - val strings = StringsPool.reader(input) - - val importTs = input.readLong() - val projectRoots = strings.readStrings() - val gradleHome = strings.readString() - val javaHome = strings.readNullableString() - - return GradleBuildRootData(importTs, projectRoots, gradleHome, javaHome, listOf()) + output.writeInt(0) } } } \ No newline at end of file diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/GradleScriptDefinitionEntity.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/GradleScriptDefinitionEntity.kt index 6050e12d79bd..8d4ecafd94ef 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/GradleScriptDefinitionEntity.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/src/org/jetbrains/kotlin/gradle/scripting/k2/workspaceModel/GradleScriptDefinitionEntity.kt @@ -20,3 +20,5 @@ interface GradleScriptDefinitionEntity : WorkspaceEntityWithSymbolicId { override val symbolicId: GradleScriptDefinitionEntityId get() = GradleScriptDefinitionEntityId(definitionId) } + +object KotlinGradleScriptEntitySource : EntitySource diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/tests/org/jetbrains/kotlin/gradle/scripting/k2/GradleBuildRootDataSerializerTest.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/tests/org/jetbrains/kotlin/gradle/scripting/k2/GradleBuildRootDataSerializerTest.kt index 31d4db3a7355..9edb617e00f1 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/tests/org/jetbrains/kotlin/gradle/scripting/k2/GradleBuildRootDataSerializerTest.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.k2/tests/org/jetbrains/kotlin/gradle/scripting/k2/GradleBuildRootDataSerializerTest.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.scripting.k2 import org.jetbrains.kotlin.gradle.scripting.k2.roots.GradleBuildRootDataSerializer import org.jetbrains.kotlin.gradle.scripting.shared.GradleKotlinScriptConfigurationInputs import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel +import org.jetbrains.kotlin.gradle.scripting.shared.roots.AbstractGradleBuildRootDataSerializer import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootData import org.junit.Test import org.junit.jupiter.api.Assertions.assertEquals @@ -52,7 +53,7 @@ class GradleBuildRootDataSerializerTest { val buffer = ByteArrayOutputStream() GradleBuildRootDataSerializer.writeKotlinDslScriptModels(DataOutputStream(buffer), dataToWrite) - val restored = GradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray()))) + val restored = AbstractGradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a") assertEquals(dataToRead.toString(), restored.toString()) } @@ -70,7 +71,7 @@ class GradleBuildRootDataSerializerTest { val buffer = ByteArrayOutputStream() GradleBuildRootDataSerializer.writeKotlinDslScriptModels(DataOutputStream(buffer), data) - val restored = GradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray()))) + val restored = AbstractGradleBuildRootDataSerializer.readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())), "a") assertEquals(data.toString(), restored.toString()) } diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/definition/definitions.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/definition/definitions.kt index 8abb5243f58f..172765675bc7 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/definition/definitions.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/definition/definitions.kt @@ -2,8 +2,6 @@ package org.jetbrains.kotlin.gradle.scripting.shared.definition import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptingBundle -import org.jetbrains.kotlin.gradle.scripting.shared.externalProjectPath -import org.jetbrains.kotlin.gradle.scripting.shared.gradle import org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate @@ -13,7 +11,7 @@ import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.templates.standard.ScriptTemplateWithArgs open class GradleScriptDefinition( - compilationConfiguration: ScriptCompilationConfiguration, + private val initialConfiguration: ScriptCompilationConfiguration, override val hostConfiguration: ScriptingHostConfiguration, override val evaluationConfiguration: ScriptEvaluationConfiguration?, override val defaultCompilerOptions: Iterable = emptyList(), @@ -27,15 +25,16 @@ open class GradleScriptDefinition( override val canDefinitionBeSwitchedOff: Boolean = false override val compilationConfiguration: ScriptCompilationConfiguration by lazy { - compilationConfiguration.with { - mutator() - gradle { - externalProjectPath(_externalProjectPath) + initialConfiguration + .with(configurationBody) + .with { + gradle { + externalProjectPath(_externalProjectPath) + } + ide { + acceptedLocations.put(listOf(ScriptAcceptedLocation.Project)) + } } - ide { - acceptedLocations.put(listOf(ScriptAcceptedLocation.Project)) - } - } } fun with(body: ScriptCompilationConfiguration.Builder.() -> Unit): GradleScriptDefinition { @@ -45,7 +44,20 @@ open class GradleScriptDefinition( ) } - open val mutator: ScriptCompilationConfiguration.Builder.() -> Unit = {} + protected open val configurationBody: ScriptCompilationConfiguration.Builder.() -> Unit = {} +} + +class BaseScriptDefinition(private val definition: ScriptDefinition) : GradleScriptDefinition( + definition.compilationConfiguration, + definition.hostConfiguration, + definition.evaluationConfiguration, + definition.defaultCompilerOptions, +) { + override val configurationBody: ScriptCompilationConfiguration.Builder.() -> Unit = { + displayName("${definition.name} (Base)") + } + + override val definitionId: String = "${super.definitionId}_base" } class LegacyGradleScriptDefinition( @@ -59,7 +71,7 @@ class LegacyGradleScriptDefinition( hostConfiguration, _legacyDefinition ), hostConfiguration, evaluationConfiguration, defaultCompilerOptions, externalProjectPath ) { - override val mutator: ScriptCompilationConfiguration.Builder.() -> Unit = { + override val configurationBody: ScriptCompilationConfiguration.Builder.() -> Unit = { @Suppress("DEPRECATION_ERROR") fileNamePattern.put(_legacyDefinition.scriptFilePattern.pattern) } } @@ -71,7 +83,7 @@ class ErrorGradleScriptDefinition : GradleScriptDefinition( hostConfiguration(ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration)) }) { - override val mutator: ScriptCompilationConfiguration.Builder.() -> Unit = { + override val configurationBody: ScriptCompilationConfiguration.Builder.() -> Unit = { fileExtension("gradle.kts") baseClass(KotlinType(ScriptTemplateWithArgs::class)) displayName(KotlinGradleScriptingBundle.message("text.default.kotlin.gradle.script")) diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/gradleScriptDefinitionsUtils.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/definition/gradleScriptDefinitionsUtils.kt similarity index 72% rename from plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/gradleScriptDefinitionsUtils.kt rename to plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/definition/gradleScriptDefinitionsUtils.kt index d20b5f3ebdcc..574bbce4fd17 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/gradleScriptDefinitionsUtils.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/definition/gradleScriptDefinitionsUtils.kt @@ -1,22 +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 org.jetbrains.kotlin.gradle.scripting.shared +package org.jetbrains.kotlin.gradle.scripting.shared.definition import com.intellij.gradle.toolingExtension.util.GradleVersionUtil.isGradleAtLeast -import com.intellij.platform.workspace.storage.EntitySource +import org.gradle.util.GradleVersion import org.jetbrains.kotlin.config.LanguageVersion -import org.jetbrains.kotlin.gradle.scripting.shared.definition.ErrorGradleScriptDefinition -import org.jetbrains.kotlin.gradle.scripting.shared.definition.GradleScriptDefinition -import org.jetbrains.kotlin.gradle.scripting.shared.definition.LegacyGradleScriptDefinition +import org.jetbrains.kotlin.gradle.scripting.shared.KotlinGradleScriptingBundle import org.jetbrains.kotlin.idea.core.script.shared.definition.loadDefinitionsFromTemplates import org.jetbrains.kotlin.idea.core.script.v1.scriptingDebugLog -import org.jetbrains.kotlin.idea.core.script.v1.scriptingInfoLog import org.jetbrains.kotlin.scripting.definitions.getEnvironment import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import java.io.File import java.nio.file.DirectoryStream import java.nio.file.Files import java.nio.file.Path -import kotlin.io.path.* +import kotlin.io.path.Path +import kotlin.io.path.exists +import kotlin.io.path.isDirectory +import kotlin.io.path.name import kotlin.script.experimental.api.ScriptCompilationConfigurationKeys import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration @@ -39,7 +39,28 @@ fun loadGradleDefinitions(params: GradleDefinitionsParams): List()?.let { legacyDefinition -> + LegacyGradleScriptDefinition( + legacyDefinition, it.hostConfiguration, it.evaluationConfiguration, it.defaultCompilerOptions, params.workingDir + ) + } ?: GradleScriptDefinition( + it.compilationConfiguration, it.hostConfiguration, it.evaluationConfiguration, it.defaultCompilerOptions, params.workingDir + ) + }.ifEmpty { sequenceOf(ErrorGradleScriptDefinition()) }.toList() +} + +fun getGradleTemplatesNames(gradleVersion: GradleVersion?): List { + val templateClasses = if (gradleVersion != null && isGradleAtLeast(gradleVersion, GRADLE_WITH_NEW_SCRIPTING_TEMPLATES)) { listOf( "org.gradle.kotlin.dsl.KotlinGradleScriptTemplate", "org.gradle.kotlin.dsl.KotlinSettingsScriptTemplate", @@ -52,33 +73,7 @@ fun loadGradleDefinitions(params: GradleDefinitionsParams): List()?.let { legacyDefinition -> - LegacyGradleScriptDefinition( - legacyDefinition, - it.hostConfiguration, - it.evaluationConfiguration, - it.defaultCompilerOptions, - params.workingDir - ) - } ?: GradleScriptDefinition( - it.compilationConfiguration, - it.hostConfiguration, - it.evaluationConfiguration, - it.defaultCompilerOptions, - params.workingDir - ) - }.ifEmpty { - sequenceOf(ErrorGradleScriptDefinition()) - }.toList() + return templateClasses } private fun findStdLibLanguageVersion(kotlinLibsClassPath: List): List { @@ -89,8 +84,7 @@ private fun findStdLibLanguageVersion(kotlinLibsClassPath: List): List { +fun getFullDefinitionsClasspath(gradleLibDir: Path): List { val templateClasspath = Files.newDirectoryStream(gradleLibDir) { kotlinDslDependencySelector.matches(it.name) } .use(DirectoryStream::toList) .ifEmpty { error(KotlinGradleScriptingBundle.message("error.text.missing.jars.in.gradle.directory")) } @@ -123,8 +121,7 @@ private fun getFullDefinitionsClasspath(gradleLibDir: Path): List { } private fun kotlinStdlibAndCompiler(gradleLibDir: Path): List { - val stdlibPath = gradleLibDir.findFirst("kotlin-stdlib-[1-9]*") - // additionally need compiler jar to load gradle resolver + val stdlibPath = gradleLibDir.findFirst("kotlin-stdlib-[1-9]*") // additionally need compiler jar to load gradle resolver val compilerPath = gradleLibDir.findFirst("kotlin-compiler-embeddable*") return listOfNotNull(stdlibPath, compilerPath) } @@ -133,25 +130,14 @@ private fun Path.findFirst(pattern: String): Path? = Files.newDirectoryStream(th private val kotlinDslDependencySelector = Regex("^gradle-(?:kotlin-dsl|core|base-services).*\\.jar\$") -fun getDefinitionsTemplateClasspath(gradleHome: String): List = try { - getFullDefinitionsClasspath(gradleHome.toGradleHomePath()).map { it.invariantSeparatorsPathString } -} catch (e: Throwable) { - scriptingInfoLog("cannot get classpath for Gradle Kotlin DSL scripts: ${e.message}") - - emptyList() -} - interface GradleScriptCompilationConfigurationKeys -open class GradleScriptCompilationConfigurationBuilder : PropertiesCollection.Builder(), - GradleScriptCompilationConfigurationKeys { +open class GradleScriptCompilationConfigurationBuilder : PropertiesCollection.Builder(), GradleScriptCompilationConfigurationKeys { companion object : GradleScriptCompilationConfigurationKeys } +@Suppress("UnusedReceiverParameter") val ScriptCompilationConfigurationKeys.gradle: GradleScriptCompilationConfigurationBuilder get() = GradleScriptCompilationConfigurationBuilder() val GradleScriptCompilationConfigurationKeys.externalProjectPath: PropertiesCollection.Key by PropertiesCollection.key() - - -object KotlinGradleScriptEntitySource : EntitySource diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/roots/AbstractGradleBuildRootDataSerializer.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/roots/AbstractGradleBuildRootDataSerializer.kt index ac293ddee091..a1a769b74fee 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/roots/AbstractGradleBuildRootDataSerializer.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/roots/AbstractGradleBuildRootDataSerializer.kt @@ -3,16 +3,19 @@ package org.jetbrains.kotlin.gradle.scripting.shared.roots import com.intellij.openapi.components.service +import com.intellij.openapi.util.IntellijInternalApi import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.gist.storage.GistStorage import com.intellij.util.io.DataExternalizer +import org.jetbrains.kotlin.gradle.scripting.shared.GradleKotlinScriptConfigurationInputs import org.jetbrains.kotlin.gradle.scripting.shared.LastModifiedFiles +import org.jetbrains.kotlin.gradle.scripting.shared.importing.KotlinDslScriptModel import org.jetbrains.kotlin.idea.core.script.v1.readString import org.jetbrains.kotlin.idea.core.script.v1.writeString import java.io.DataInput import java.io.DataOutput -private const val BINARY_FORMAT_VERSION = 1 +private const val BINARY_FORMAT_VERSION = 2 private const val NO_TRACK_GIST_STAMP = 0 abstract class AbstractGradleBuildRootDataSerializer { @@ -20,9 +23,9 @@ abstract class AbstractGradleBuildRootDataSerializer { protected val currentBuildRoot: ThreadLocal = ThreadLocal() private val buildRootDataGist = - GistStorage.getInstance().newGist("GradleBuildRootData", BINARY_FORMAT_VERSION, externalizer) + GistStorage.getInstance().newGist("GradleBuildRootData", BINARY_FORMAT_VERSION, getExternalizer()) - protected abstract val externalizer: DataExternalizer + protected abstract fun getExternalizer(): DataExternalizer fun read(buildRoot: VirtualFile): GradleBuildRootData? { currentBuildRoot.set(buildRoot) @@ -40,6 +43,28 @@ abstract class AbstractGradleBuildRootDataSerializer { } companion object { + @IntellijInternalApi + fun readKotlinDslScriptModels(input: DataInput, buildRoot: String): GradleBuildRootData { + val strings = StringsPool.reader(input) + + val importTs = input.readLong() + val projectRoots = strings.readStrings() + val gradleHome = strings.readString() + val javaHome = strings.readNullableString() + val models = input.readList { + KotlinDslScriptModel( + strings.readString(), + GradleKotlinScriptConfigurationInputs(input.readString(), input.readLong(), buildRoot), + strings.readStrings(), + strings.readStrings(), + strings.readStrings(), + listOf() + ) + } + + return GradleBuildRootData(importTs, projectRoots, gradleHome, javaHome, models) + } + @JvmStatic fun getInstance(): AbstractGradleBuildRootDataSerializer = service() } diff --git a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/utils.kt b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/utils.kt index 03dc49f56310..031760a14a40 100644 --- a/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/utils.kt +++ b/plugins/kotlin/gradle/scripting/kotlin.gradle.scripting.shared/src/org/jetbrains/kotlin/gradle/scripting/shared/utils.kt @@ -4,10 +4,12 @@ package org.jetbrains.kotlin.gradle.scripting.shared import com.intellij.gradle.toolingExtension.util.GradleVersionUtil import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.* import com.intellij.psi.impl.source.tree.LeafPsiElement +import kotlinx.coroutines.suspendCancellableCoroutine import org.jetbrains.kotlin.gradle.scripting.shared.roots.GradleBuildRootsLocator import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtFile diff --git a/plugins/kotlin/idea/tests/testData/gradle/highlighting/resilient/simple.test b/plugins/kotlin/idea/tests/testData/gradle/highlighting/resilient/simple.test new file mode 100644 index 000000000000..c50c192e722d --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/gradle/highlighting/resilient/simple.test @@ -0,0 +1,42 @@ +// FILE: build.gradle.kts +// ALLOW_ERRORS +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":module1")) +} + +// FILE: settings.gradle.kts +include("module1") + +// FILE: build.gradle.kts.highlighting.k2 +// ALLOW_ERRORS +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":module1")) +} + +// FILE: settings.gradle.kts +include("module1") + +// FILE: module1/build.gradle.kts +plugins { + java +} + +repositories { + mavenCentral() +}