MPP: Refactoring, extract IDE platform kinds, allow to add custom platforms

This commit is contained in:
Yan Zhulanow
2018-08-30 18:46:59 +05:00
parent cf1b2bedf4
commit d00f5b335a
81 changed files with 1327 additions and 844 deletions
@@ -29,7 +29,6 @@ import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.TargetPlatformKind
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
import org.jetbrains.kotlin.idea.facet.initializeIfNeeded
@@ -40,6 +39,7 @@ import org.jetbrains.kotlin.idea.util.projectStructure.allModules
import org.jetbrains.kotlin.idea.util.projectStructure.sdk
import org.jetbrains.kotlin.idea.util.projectStructure.version
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
@@ -113,7 +113,7 @@ open class KotlinJavaModuleConfigurator protected constructor() : KotlinWithLibr
try {
val facet = module.getOrCreateFacet(modelsProvider, useProjectSettings = false, commitModel = true)
val facetSettings = facet.configuration.settings
facetSettings.initializeIfNeeded(module, null, TargetPlatformKind.Jvm(JvmTarget.JVM_1_8))
facetSettings.initializeIfNeeded(module, null, JvmIdePlatformKind.Platform(JvmTarget.JVM_1_8))
(facetSettings.compilerArguments as? K2JVMCompilerArguments)?.jvmTarget = "1.8"
} finally {
modelsProvider.dispose()
@@ -25,10 +25,9 @@ import com.intellij.ide.IdeBundle
import com.intellij.openapi.roots.ui.configuration.libraries.AddCustomLibraryDialog
import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription
import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager
import org.jetbrains.kotlin.config.TargetPlatformKind
import org.jetbrains.kotlin.idea.framework.CommonStandardLibraryDescription
import org.jetbrains.kotlin.idea.framework.JSLibraryStdDescription
import org.jetbrains.kotlin.idea.framework.JavaRuntimeLibraryDescription
import org.jetbrains.kotlin.idea.platform.tooling
import org.jetbrains.kotlin.platform.IdePlatformKind
import org.jetbrains.kotlin.platform.impl.isCommon
import javax.swing.JComponent
// Based on com.intellij.facet.impl.ui.libraries.FrameworkLibraryValidatorImpl
@@ -36,27 +35,20 @@ class FrameworkLibraryValidatorWithDynamicDescription(
private val context: LibrariesValidatorContext,
private val validatorsManager: FacetValidatorsManager,
private val libraryCategoryName: String,
private val getTargetPlatform: () -> TargetPlatformKind<*>
private val getPlatform: () -> IdePlatformKind<*>
) : FrameworkLibraryValidator() {
private val TargetPlatformKind<*>.libraryDescription: CustomLibraryDescription
get() {
val project = context.module.project
return when (this) {
is TargetPlatformKind.Jvm -> JavaRuntimeLibraryDescription(project)
is TargetPlatformKind.JavaScript -> JSLibraryStdDescription(project)
is TargetPlatformKind.Common -> CommonStandardLibraryDescription(project)
}
}
private val IdePlatformKind<*>.libraryDescription: CustomLibraryDescription
get() = this.tooling.getLibraryDescription(context.module.project)
private fun checkLibraryIsConfigured(targetPlatform: TargetPlatformKind<*>): Boolean {
private fun checkLibraryIsConfigured(platform: IdePlatformKind<*>): Boolean {
// TODO: propose to configure kotlin-stdlib-common once it's available
if (targetPlatform == TargetPlatformKind.Common) return true
if (platform.isCommon) return true
if (KotlinVersionInfoProvider.EP_NAME.extensions.any {
it.getLibraryVersions(context.module, targetPlatform, context.rootModel).isNotEmpty()
it.getLibraryVersions(context.module, platform, context.rootModel).isNotEmpty()
}) return true
val libraryDescription = targetPlatform.libraryDescription
val libraryDescription = platform.libraryDescription
val libraryKinds = libraryDescription.suitableLibraryKinds
var found = false
val presentationManager = LibraryPresentationManager.getInstance()
@@ -75,12 +67,12 @@ class FrameworkLibraryValidatorWithDynamicDescription(
}
override fun check(): ValidationResult {
val targetPlatform = getTargetPlatform()
val targetPlatform = getPlatform()
if (checkLibraryIsConfigured(targetPlatform)) {
val conflictingPlatforms = TargetPlatformKind.ALL_PLATFORMS.filter {
it != TargetPlatformKind.Common && it.name != targetPlatform.name && checkLibraryIsConfigured(it)
}
val conflictingPlatforms = IdePlatformKind.getInstances()
.filter { !it.isCommon && it.name != targetPlatform.name && checkLibraryIsConfigured(it) }
if (conflictingPlatforms.isNotEmpty()) {
val platformText = conflictingPlatforms.mapTo(LinkedHashSet()) { it.name }.joinToString()
return ValidationResult("Libraries for the following platform are also present in the module dependencies: $platformText")
@@ -20,8 +20,8 @@ import com.intellij.facet.impl.ui.libraries.DelegatingLibrariesValidatorContext
import com.intellij.facet.ui.FacetEditorContext
import com.intellij.facet.ui.FacetEditorValidator
import com.intellij.facet.ui.FacetValidatorsManager
import org.jetbrains.kotlin.config.TargetPlatformKind
import org.jetbrains.kotlin.idea.facet.KotlinFacetEditorGeneralTab.EditorComponent
import org.jetbrains.kotlin.platform.IdePlatformKind
class KotlinLibraryValidatorCreator : KotlinFacetValidatorCreator() {
override fun create(editor: EditorComponent, validatorsManager: FacetValidatorsManager, editorContext: FacetEditorContext): FacetEditorValidator {
@@ -29,6 +29,6 @@ class KotlinLibraryValidatorCreator : KotlinFacetValidatorCreator() {
DelegatingLibrariesValidatorContext(editorContext),
validatorsManager,
"kotlin"
) { editor.targetPlatformComboBox.selectedItem as TargetPlatformKind<*> }
) { editor.targetPlatformComboBox.selectedItem as IdePlatformKind<*> }
}
}
@@ -30,7 +30,6 @@ import com.intellij.openapi.roots.libraries.ui.RootDetector
import com.intellij.openapi.roots.ui.configuration.libraryEditor.DefaultLibraryRootsComponentDescriptor
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.config.TargetPlatformKind
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.KotlinIcons
import javax.swing.JComponent
@@ -83,11 +82,4 @@ class JSLibraryType : LibraryType<DummyLibraryProperties>(JSLibraryKind) {
}
}
private fun isAcceptedForJsLibrary(extension: String?) = extension == "js" || extension == "kjsm"
val TargetPlatformKind<*>.libraryKind: PersistentLibraryKind<*>?
get() = when(this) {
TargetPlatformKind.JavaScript -> JSLibraryKind
TargetPlatformKind.Common -> CommonLibraryKind
else -> null
}
private fun isAcceptedForJsLibrary(extension: String?) = extension == "js" || extension == "kjsm"
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.core.platform.impl
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.libraries.Library
import org.jetbrains.kotlin.analyzer.common.CommonAnalyzerFacade
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.project.implementingModules
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
import org.jetbrains.kotlin.idea.framework.CommonStandardLibraryDescription
import org.jetbrains.kotlin.idea.framework.getCommonRuntimeLibraryVersion
import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling
import org.jetbrains.kotlin.idea.platform.tooling
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind
import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import javax.swing.Icon
object CommonIdePlatformKindTooling : IdePlatformKindTooling() {
const val MAVEN_COMMON_STDLIB_ID = "kotlin-stdlib-common" // TODO: KotlinCommonMavenConfigurator
override val kind = CommonIdePlatformKind
override fun compilerArgumentsForProject(project: Project): CommonCompilerArguments? = null
override val resolverForModuleFactory = CommonAnalyzerFacade
override val mavenLibraryIds = listOf(MAVEN_COMMON_STDLIB_ID)
override val gradlePluginId = "kotlin-platform-common"
override val libraryKind = CommonLibraryKind
override fun getLibraryDescription(project: Project) = CommonStandardLibraryDescription(project)
override fun getLibraryVersionProvider(project: Project): (Library) -> String? {
return ::getCommonRuntimeLibraryVersion
}
override fun getTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? {
return IdePlatformKindTooling.getInstances()
.asSequence()
.filter { it != this }
.mapNotNull { it.getTestIcon(declaration, descriptor) }
.distinct()
.singleOrNull()
}
override fun acceptsAsEntryPoint(function: KtFunction): Boolean {
val module = function.containingKtFile.module ?: return false
return module.implementingModules.any { implementingModule ->
implementingModule.platform?.kind?.takeIf { !it.isCommon }?.tooling?.acceptsAsEntryPoint(function) ?: false
}
}
}
@@ -0,0 +1,115 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.core.platform.impl
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.JsAnalyzerFacade
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumentsHolder
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.framework.JSLibraryStdDescription
import org.jetbrains.kotlin.idea.framework.JsLibraryStdDetectionUtil
import org.jetbrains.kotlin.idea.highlighter.KotlinTestRunLineMarkerContributor.Companion.getTestStateIcon
import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider
import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling
import org.jetbrains.kotlin.idea.util.string.joinWithEscape
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.utils.PathUtil
import javax.swing.Icon
class JsIdePlatformKindTooling : IdePlatformKindTooling() {
companion object {
private const val MAVEN_OLD_JS_STDLIB_ID = "kotlin-js-library"
private val TEST_FQ_NAME = FqName("kotlin.test.Test")
private val IGNORE_FQ_NAME = FqName("kotlin.test.Ignore")
}
override val kind = JsIdePlatformKind
override fun compilerArgumentsForProject(project: Project) = Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings
override val resolverForModuleFactory = JsAnalyzerFacade
override val mavenLibraryIds = listOf(PathUtil.JS_LIB_NAME, MAVEN_OLD_JS_STDLIB_ID)
override val gradlePluginId = "kotlin-platform-js"
override val libraryKind = JSLibraryKind
override fun getLibraryDescription(project: Project) = JSLibraryStdDescription(project)
override fun getLibraryVersionProvider(project: Project) = { library: Library ->
JsLibraryStdDetectionUtil.getJsLibraryStdVersion(library, project)
}
override fun getTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? {
if (!descriptor.isTest()) return null
val runConfigData = RunConfigurationProducer
.getProducers(declaration.project)
.asSequence()
.filterIsInstance<KotlinJSRunConfigurationDataProvider<*>>()
.filter { it.isForTests }
.mapNotNull { it.getConfigurationData(declaration) }
.firstOrNull() ?: return null
val locations = ArrayList<String>()
locations += FileUtil.toSystemDependentName(runConfigData.jsOutputFilePath)
val klass = when (declaration) {
is KtClassOrObject -> declaration
is KtNamedFunction -> declaration.containingClassOrObject ?: return null
else -> return null
}
locations += klass.parentsWithSelf.filterIsInstance<KtNamedDeclaration>().mapNotNull { it.name }.toList().asReversed()
val testName = (declaration as? KtNamedFunction)?.name
if (testName != null) {
locations += "$testName"
}
val prefix = if (testName != null) "test://" else "suite://"
val url = prefix + locations.joinWithEscape('.')
return getTestStateIcon(url, declaration.project)
}
override fun acceptsAsEntryPoint(function: KtFunction): Boolean {
return RunConfigurationProducer
.getProducers(function.project)
.asSequence()
.filterIsInstance<KotlinJSRunConfigurationDataProvider<*>>()
.filter { !it.isForTests }
.mapNotNull { it.getConfigurationData(function) }
.firstOrNull() != null
}
private fun DeclarationDescriptor.isIgnored(): Boolean =
annotations.any { it.fqName == IGNORE_FQ_NAME } || ((containingDeclaration as? ClassDescriptor)?.isIgnored() ?: false)
private fun DeclarationDescriptor.isTest(): Boolean {
if (isIgnored()) return false
if (annotations.any { it.fqName == TEST_FQ_NAME }) return true
if (this is ClassDescriptorWithResolutionScopes) {
return declaredCallableMembers.any { it.isTest() }
}
return false
}
}
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.core.platform.impl
import com.intellij.codeInsight.TestFrameworks
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder
import org.jetbrains.kotlin.idea.framework.JavaRuntimeDetectionUtil
import org.jetbrains.kotlin.idea.framework.JavaRuntimeLibraryDescription
import org.jetbrains.kotlin.idea.highlighter.KotlinTestRunLineMarkerContributor.Companion.getTestStateIcon
import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade
import org.jetbrains.kotlin.utils.PathUtil
import javax.swing.Icon
class JvmIdePlatformKindTooling : IdePlatformKindTooling() {
override val kind = JvmIdePlatformKind
override fun compilerArgumentsForProject(project: Project) = Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings
override val resolverForModuleFactory = JvmAnalyzerFacade
override val mavenLibraryIds = listOf(
PathUtil.KOTLIN_JAVA_STDLIB_NAME,
PathUtil.KOTLIN_JAVA_RUNTIME_JRE7_NAME,
PathUtil.KOTLIN_JAVA_RUNTIME_JDK7_NAME,
PathUtil.KOTLIN_JAVA_RUNTIME_JRE8_NAME,
PathUtil.KOTLIN_JAVA_RUNTIME_JDK8_NAME
)
override val gradlePluginId = "kotlin-platform-jvm"
override val libraryKind: PersistentLibraryKind<*>? = null
override fun getLibraryDescription(project: Project) = JavaRuntimeLibraryDescription(project)
override fun getLibraryVersionProvider(project: Project): (Library) -> String? {
return JavaRuntimeDetectionUtil::getJavaRuntimeVersion
}
override fun getTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? {
val (url, framework) = when (declaration) {
is KtClassOrObject -> {
val lightClass = declaration.toLightClass() ?: return null
val framework = TestFrameworks.detectFramework(lightClass) ?: return null
if (!framework.isTestClass(lightClass)) return null
val qualifiedName = lightClass.qualifiedName ?: return null
"java:suite://$qualifiedName" to framework
}
is KtNamedFunction -> {
val lightMethod = declaration.toLightMethods().firstOrNull() ?: return null
val lightClass = lightMethod.containingClass as? KtLightClass ?: return null
val framework = TestFrameworks.detectFramework(lightClass) ?: return null
if (!framework.isTestMethod(lightMethod)) return null
"java:test://${lightClass.qualifiedName}.${lightMethod.name}" to framework
}
else -> return null
}
return getTestStateIcon(url, declaration.project) ?: framework.icon
}
override fun acceptsAsEntryPoint(function: KtFunction) = true
}
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.run
import com.intellij.execution.JUnitRecognizer
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.config.TargetPlatformKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
@@ -27,8 +26,9 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.idea.caches.project.implementingDescriptors
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.project.targetPlatform
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@@ -38,7 +38,7 @@ class KotlinMultiplatformJUnitRecognizer : JUnitRecognizer() {
override fun isTestAnnotated(method: PsiMethod): Boolean {
if (method !is KtLightMethod) return false
val origin = method.kotlinOrigin ?: return false
if (origin.module?.targetPlatform !is TargetPlatformKind.Common) return false
if (!origin.module?.platform.isCommon) return false
val moduleDescriptor = origin.containingKtFile.findModuleDescriptor()
val implModules = moduleDescriptor.implementingDescriptors
@@ -26,14 +26,15 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.util.ClassUtil
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.config.TargetPlatformKind
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.idea.caches.project.implementingModules
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.targetPlatform
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.platform.impl.isJvm
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
@@ -127,6 +128,6 @@ class KotlinRunConfigurationProducer : RunConfigurationProducer<KotlinRunConfigu
}
fun Module.findJvmImplementationModule(): Module? {
if (targetPlatform != TargetPlatformKind.Common) return null
return implementingModules.firstOrNull { it.targetPlatform is TargetPlatformKind.Jvm }
if (!platform.isCommon) return null
return implementingModules.firstOrNull { it.platform.isJvm }
}