From a2a018eadb5d862b2adbe0832a2909905d757059 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 15 Aug 2017 13:01:51 +0200 Subject: [PATCH] Pluggable platforms in IDE plugin --- .../caches/resolve/IdePlatformSupport.kt | 126 ++++++++++++++++++ .../caches/resolve/ModuleDependencyMapper.kt | 17 ++- .../idea/project/AnalyzerFacadeProvider.kt | 38 ------ .../idea/project/ProjectStructureUtil.java | 60 +-------- idea/src/META-INF/extensions/ide.xml | 7 + .../jetbrains/kotlin/idea/facet/facetUtils.kt | 10 +- 6 files changed, 149 insertions(+), 109 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/IdePlatformSupport.kt delete mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/AnalyzerFacadeProvider.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/IdePlatformSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/IdePlatformSupport.kt new file mode 100644 index 00000000000..8530181f265 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/IdePlatformSupport.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.caches.resolve + +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.libraries.PersistentLibraryKind +import org.jetbrains.kotlin.analyzer.AnalyzerFacade +import org.jetbrains.kotlin.analyzer.common.DefaultAnalyzerFacade +import org.jetbrains.kotlin.builtins.DefaultBuiltIns +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider +import org.jetbrains.kotlin.config.TargetPlatformKind +import org.jetbrains.kotlin.context.GlobalContextImpl +import org.jetbrains.kotlin.idea.caches.resolve.JsAnalyzerFacade +import org.jetbrains.kotlin.idea.caches.resolve.PlatformAnalysisSettings +import org.jetbrains.kotlin.idea.framework.CommonLibraryKind +import org.jetbrains.kotlin.idea.framework.JSLibraryKind +import org.jetbrains.kotlin.js.resolve.JsPlatform +import org.jetbrains.kotlin.platform.JvmBuiltIns +import org.jetbrains.kotlin.resolve.TargetPlatform +import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform + +abstract class IdePlatformSupport { + abstract val platform: TargetPlatform + abstract val analyzerFacade: AnalyzerFacade + abstract val libraryKind: PersistentLibraryKind<*>? + + abstract fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl): KotlinBuiltIns + + abstract fun isModuleForPlatform(module: Module): Boolean + + companion object { + val EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.idePlatformSupport") + + val platformSupport by lazy { + Extensions.getExtensions(EP_NAME).associateBy { it.platform } + } + + val facades by lazy { + Extensions.getExtensions(EP_NAME).map { it.platform to it.analyzerFacade }.toMap() + } + + @JvmStatic + fun getPlatformForModule(module: Module): TargetPlatform { + return Extensions.getExtensions(EP_NAME).find { it.isModuleForPlatform(module) }?.platform ?: JvmPlatform + } + } + +} + +class JvmPlatformSupport : IdePlatformSupport() { + override val platform: TargetPlatform + get() = JvmPlatform + + override val analyzerFacade: AnalyzerFacade + get() = JvmAnalyzerFacade + + override val libraryKind: PersistentLibraryKind<*>? + get() = null + + override fun isModuleForPlatform(module: Module): Boolean { + val settings = KotlinFacetSettingsProvider.getInstance(module.project).getInitializedSettings(module) + return settings.targetPlatformKind is TargetPlatformKind.Jvm + } + + override fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl): KotlinBuiltIns { + return if (settings.sdk != null) JvmBuiltIns(sdkContext.storageManager) else DefaultBuiltIns.Instance + } +} + +class JsPlatformSupport : IdePlatformSupport() { + override val platform: TargetPlatform + get() = JsPlatform + + override val analyzerFacade: AnalyzerFacade + get() = JsAnalyzerFacade + + override val libraryKind: PersistentLibraryKind<*>? + get() = JSLibraryKind + + override fun isModuleForPlatform(module: Module): Boolean { + val settings = KotlinFacetSettingsProvider.getInstance(module.project).getInitializedSettings(module) + return settings.targetPlatformKind is TargetPlatformKind.JavaScript + } + + override fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl): KotlinBuiltIns { + return JsPlatform.builtIns + } +} + +class CommonPlatformSupport : IdePlatformSupport() { + override val platform: TargetPlatform + get() = TargetPlatform.Default + + override val analyzerFacade: AnalyzerFacade + get() = DefaultAnalyzerFacade + + override val libraryKind: PersistentLibraryKind<*>? + get() = CommonLibraryKind + + override fun isModuleForPlatform(module: Module): Boolean { + val settings = KotlinFacetSettingsProvider.getInstance(module.project).getInitializedSettings(module) + return settings.targetPlatformKind is TargetPlatformKind.Common + } + + override fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl): KotlinBuiltIns { + return DefaultBuiltIns.Instance + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt index c7c2d397c8d..a0211cd194f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt @@ -29,18 +29,16 @@ import org.jetbrains.kotlin.analyzer.ResolverForProject import org.jetbrains.kotlin.analyzer.ResolverForProjectImpl import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.caches.resolve.IdePlatformSupport import org.jetbrains.kotlin.context.GlobalContextImpl import org.jetbrains.kotlin.context.withProject import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider -import org.jetbrains.kotlin.idea.project.AnalyzerFacadeProvider import org.jetbrains.kotlin.idea.project.IdeaEnvironment -import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl import org.jetbrains.kotlin.platform.JvmBuiltIns import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters -import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform fun createModuleResolverProvider( debugName: String, @@ -76,7 +74,10 @@ fun createModuleResolverProvider( val resolverForProject = ResolverForProjectImpl( debugName, globalContext.withProject(project), modulesToCreateResolversFor, - { module -> AnalyzerFacadeProvider.getAnalyzerFacade(module.platform ?: analysisSettings.platform) }, + { module -> + val platform = module.platform ?: analysisSettings.platform + IdePlatformSupport.facades[platform] ?: throw UnsupportedOperationException("Unsupported platform $platform") + }, modulesContent, jvmPlatformParameters, IdeaEnvironment, builtIns, delegateResolver, { _, c -> IDEPackagePartProvider(c.moduleContentScope) }, @@ -123,11 +124,9 @@ fun collectAllModuleInfosFromIdeaModel(project: Project): List { return modulesSourcesInfos + librariesInfos + sdksInfos } -private fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl): KotlinBuiltIns = when { - settings.platform is JsPlatform -> JsPlatform.builtIns - settings.platform is JvmPlatform && settings.sdk != null -> JvmBuiltIns(sdkContext.storageManager) - else -> DefaultBuiltIns.Instance - +private fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl): KotlinBuiltIns { + val supportInstance = IdePlatformSupport.platformSupport[settings.platform] ?: return DefaultBuiltIns.Instance + return supportInstance.createBuiltIns(settings, sdkContext) } fun getAllProjectSdks(): Collection { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/AnalyzerFacadeProvider.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/AnalyzerFacadeProvider.kt deleted file mode 100644 index 5cc9209f14f..00000000000 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/AnalyzerFacadeProvider.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.project - -import org.jetbrains.kotlin.analyzer.AnalyzerFacade -import org.jetbrains.kotlin.analyzer.common.DefaultAnalyzerFacade -import org.jetbrains.kotlin.idea.caches.resolve.JsAnalyzerFacade -import org.jetbrains.kotlin.js.resolve.JsPlatform -import org.jetbrains.kotlin.resolve.TargetPlatform -import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade -import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform - -object AnalyzerFacadeProvider { - //NOTE: it's convenient that JS backend doesn't have platform parameters (for now) - // otherwise we would be forced to add casts on the call site of setupResolverForProject - fun getAnalyzerFacade(targetPlatform: TargetPlatform): AnalyzerFacade { - return when (targetPlatform) { - JvmPlatform -> JvmAnalyzerFacade - JsPlatform -> JsAnalyzerFacade - TargetPlatform.Default -> DefaultAnalyzerFacade - else -> throw IllegalArgumentException("Unsupported platform: $targetPlatform") - } - } -} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ProjectStructureUtil.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ProjectStructureUtil.java index f777e90c25d..722735d54ca 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ProjectStructureUtil.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ProjectStructureUtil.java @@ -16,27 +16,15 @@ package org.jetbrains.kotlin.idea.project; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; -import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectRootModificationTracker; -import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Ref; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; -import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.config.KotlinFacetSettings; -import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider; -import org.jetbrains.kotlin.config.TargetPlatformKind; -import org.jetbrains.kotlin.idea.framework.JsLibraryStdDetectionUtil; -import org.jetbrains.kotlin.js.resolve.JsPlatform; +import org.jetbrains.kotlin.caches.resolve.IdePlatformSupport; import org.jetbrains.kotlin.resolve.TargetPlatform; -import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform; public class ProjectStructureUtil { private static final Key> PLATFORM_FOR_MODULE = Key.create("PLATFORM_FOR_MODULE"); @@ -51,11 +39,8 @@ public class ProjectStructureUtil { result = CachedValuesManager.getManager(module.getProject()).createCachedValue(new CachedValueProvider() { @Override public Result compute() { - TargetPlatform configuredInFacet = getPlatformConfiguredInFacet(module); - TargetPlatform platform = - configuredInFacet != null ? configuredInFacet : - hasJsStandardLibraryInDependencies(module) ? JsPlatform.INSTANCE : JvmPlatform.INSTANCE; - return Result.create(platform, ProjectRootModificationTracker.getInstance(module.getProject())); + return Result.create(IdePlatformSupport.getPlatformForModule(module), + ProjectRootModificationTracker.getInstance(module.getProject())); } }, false); @@ -64,43 +49,4 @@ public class ProjectStructureUtil { return result.getValue(); } - - @Nullable - private static TargetPlatform getPlatformConfiguredInFacet(@NotNull Module module) { - KotlinFacetSettings settings = KotlinFacetSettingsProvider.Companion.getInstance(module.getProject()).getInitializedSettings(module); - TargetPlatformKind kind = settings.getTargetPlatformKind(); - if (kind instanceof TargetPlatformKind.Jvm) { - return JvmPlatform.INSTANCE; - } - if (kind instanceof TargetPlatformKind.JavaScript) { - return JsPlatform.INSTANCE; - } - if (kind instanceof TargetPlatformKind.Common) { - return TargetPlatform.Default.INSTANCE; - } - return null; - } - - private static boolean hasJsStandardLibraryInDependencies(@NotNull final Module module) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Boolean compute() { - final Ref jsLibrary = Ref.create(); - - ModuleRootManager.getInstance(module).orderEntries().librariesOnly().forEachLibrary(new Processor() { - @Override - public boolean process(Library library) { - if (JsLibraryStdDetectionUtil.INSTANCE.hasJsStdlibJar(library, false)) { - jsLibrary.set(library); - return false; - } - - return true; - } - }); - - return jsLibrary.get() != null; - } - }); - } } diff --git a/idea/src/META-INF/extensions/ide.xml b/idea/src/META-INF/extensions/ide.xml index d83cc27d7ce..50f583e5d6c 100644 --- a/idea/src/META-INF/extensions/ide.xml +++ b/idea/src/META-INF/extensions/ide.xml @@ -10,6 +10,9 @@ + + @@ -26,5 +29,9 @@ + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt index ed49a1cfedf..a520588029b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt @@ -38,11 +38,11 @@ import org.jetbrains.kotlin.idea.versions.* import kotlin.reflect.KProperty1 private fun getDefaultTargetPlatform(module: Module, rootModel: ModuleRootModel?): TargetPlatformKind<*> { - if (getRuntimeLibraryVersions(module, rootModel, TargetPlatformKind.JavaScript).isNotEmpty()) { - return TargetPlatformKind.JavaScript - } - if (getRuntimeLibraryVersions(module, rootModel, TargetPlatformKind.Common).isNotEmpty()) { - return TargetPlatformKind.Common + for (platform in TargetPlatformKind.ALL_PLATFORMS) { + if (platform.version == TargetPlatformVersion.NoVersion && + getRuntimeLibraryVersions(module, rootModel, platform).isNotEmpty()) { + return platform + } } val sdk = ((rootModel ?: ModuleRootManager.getInstance(module))).sdk