Pluggable platforms in IDE plugin

This commit is contained in:
Dmitry Jemerov
2017-08-15 13:01:51 +02:00
parent f7c17d6a64
commit a2a018eadb
6 changed files with 149 additions and 109 deletions
@@ -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<IdePlatformSupport>("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
}
}
@@ -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<IdeaModuleInfo> {
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<Sdk> {
@@ -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")
}
}
}
@@ -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<CachedValue<TargetPlatform>> PLATFORM_FOR_MODULE = Key.create("PLATFORM_FOR_MODULE");
@@ -51,11 +39,8 @@ public class ProjectStructureUtil {
result = CachedValuesManager.getManager(module.getProject()).createCachedValue(new CachedValueProvider<TargetPlatform>() {
@Override
public Result<TargetPlatform> 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<Boolean>() {
@Override
public Boolean compute() {
final Ref<Library> jsLibrary = Ref.create();
ModuleRootManager.getInstance(module).orderEntries().librariesOnly().forEachLibrary(new Processor<Library>() {
@Override
public boolean process(Library library) {
if (JsLibraryStdDetectionUtil.INSTANCE.hasJsStdlibJar(library, false)) {
jsLibrary.set(library);
return false;
}
return true;
}
});
return jsLibrary.get() != null;
}
});
}
}
+7
View File
@@ -10,6 +10,9 @@
<extensionPoint name="androidDexer"
interface="org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer"
area="IDEA_PROJECT"/>
<extensionPoint name="idePlatformSupport"
interface="org.jetbrains.kotlin.caches.resolve.IdePlatformSupport"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
@@ -26,5 +29,9 @@
<scriptTemplatesProvider id="StandardKotlinScriptTemplateProvider"
order="last"
implementation="org.jetbrains.kotlin.idea.script.StandardKotlinScriptTemplateProvider"/>
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.JvmPlatformSupport"/>
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.JsPlatformSupport"/>
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.CommonPlatformSupport"/>
</extensions>
</idea-plugin>
@@ -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