Introduce SourceType and use KotlinFacetSettings.isTestModule

Refactor dealing with production/test roots
Remove ad-hoc code in KotlinMultiplatformRunLocationsProvider
This commit is contained in:
Pavel V. Talanov
2018-09-25 18:18:34 +02:00
parent 1be491504a
commit cc1be5f559
5 changed files with 55 additions and 37 deletions
@@ -210,8 +210,6 @@ data class ModuleTestSourceInfo internal constructor(override val module: Module
override fun <T> createCachedValueProvider(f: () -> CachedValueProvider.Result<T>) = CachedValueProvider { f() } override fun <T> createCachedValueProvider(f: () -> CachedValueProvider.Result<T>) = CachedValueProvider { f() }
} }
internal fun ModuleSourceInfo.isTests() = this is ModuleTestSourceInfo
fun Module.productionSourceInfo(): ModuleProductionSourceInfo? = if (hasProductionRoots()) ModuleProductionSourceInfo(this) else null fun Module.productionSourceInfo(): ModuleProductionSourceInfo? = if (hasProductionRoots()) ModuleProductionSourceInfo(this) else null
fun Module.testSourceInfo(): ModuleTestSourceInfo? = if (hasTestRoots()) ModuleTestSourceInfo(this) else null fun Module.testSourceInfo(): ModuleTestSourceInfo? = if (hasTestRoots()) ModuleTestSourceInfo(this) else null
@@ -444,3 +442,10 @@ data class PlatformModuleInfo(
fun IdeaModuleInfo.projectSourceModules(): List<ModuleSourceInfo>? = fun IdeaModuleInfo.projectSourceModules(): List<ModuleSourceInfo>? =
(this as? ModuleSourceInfo)?.let(::listOf) ?: (this as? PlatformModuleInfo)?.containedModules (this as? ModuleSourceInfo)?.let(::listOf) ?: (this as? PlatformModuleInfo)?.containedModules
enum class SourceType {
PRODUCTION,
TEST
}
internal val ModuleSourceInfo.sourceType get() = if (this is ModuleTestSourceInfo) SourceType.TEST else SourceType.PRODUCTION
@@ -6,25 +6,24 @@
package org.jetbrains.kotlin.idea.caches.project package org.jetbrains.kotlin.idea.caches.project
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.util.containers.MultiMap
import com.intellij.openapi.project.Project
import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleOrderEntry import com.intellij.openapi.roots.ModuleOrderEntry
import gnu.trove.THashSet import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.util.containers.Queue
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.CachedValueProvider
import com.intellij.openapi.roots.ProjectRootModificationTracker import com.intellij.openapi.roots.ProjectRootModificationTracker
import java.util.HashSet import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.MultiMap
import com.intellij.util.containers.Queue
import gnu.trove.THashSet
import java.util.*
//NOTE: this is an approximation that may contain more module infos then the exact solution //NOTE: this is an approximation that may contain more module infos then the exact solution
fun ModuleSourceInfo.getDependentModules(): Set<ModuleSourceInfo> { fun ModuleSourceInfo.getDependentModules(): Set<ModuleSourceInfo> {
val dependents = getDependents(module) val dependents = getDependents(module)
return if (isTests()) { return when (sourceType) {
dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo) SourceType.TEST -> dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
} else { SourceType.PRODUCTION -> dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { it.correspondingModuleInfos() }
dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { it.correspondingModuleInfos() }
} }
} }
@@ -16,6 +16,8 @@ import com.intellij.psi.util.CachedValueProvider
import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.caches.project.SourceType.PRODUCTION
import org.jetbrains.kotlin.idea.caches.project.SourceType.TEST
import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.facet.KotlinFacetType import org.jetbrains.kotlin.idea.facet.KotlinFacetType
import org.jetbrains.kotlin.idea.facet.KotlinFacetType.Companion.ID import org.jetbrains.kotlin.idea.facet.KotlinFacetType.Companion.ID
@@ -26,19 +28,24 @@ import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.TargetPlatform
val Module.isNewMPPModule: Boolean val Module.isNewMPPModule: Boolean
get() = KotlinFacet.get(this)?.configuration?.settings?.kind?.isNewMPP ?: false get() = facetSettings?.kind?.isNewMPP ?: false
val Module.externalProjectId: String val Module.externalProjectId: String
get() = KotlinFacet.get(this)?.configuration?.settings?.externalProjectId ?: "" get() = facetSettings?.externalProjectId ?: ""
val Module.sourceType: SourceType?
get() = facetSettings?.isTestModule?.let { isTest -> if (isTest) SourceType.TEST else PRODUCTION }
val Module.isMPPModule: Boolean val Module.isMPPModule: Boolean
get() { get() {
val settings = KotlinFacet.get(this)?.configuration?.settings ?: return false val settings = facetSettings ?: return false
return settings.platform.isCommon || return settings.platform.isCommon ||
settings.implementedModuleNames.isNotEmpty() || settings.implementedModuleNames.isNotEmpty() ||
settings.kind.isNewMPP settings.kind.isNewMPP
} }
private val Module.facetSettings get() = KotlinFacet.get(this)?.configuration?.settings
val Module.implementingModules: List<Module> val Module.implementingModules: List<Module>
get() = cached(CachedValueProvider { get() = cached(CachedValueProvider {
val moduleManager = ModuleManager.getInstance(project) val moduleManager = ModuleManager.getInstance(project)
@@ -87,12 +94,15 @@ val ModuleDescriptor.implementingDescriptors: List<ModuleDescriptor>
return listOf(this) return listOf(this)
} }
val moduleSourceInfo = moduleInfo as? ModuleSourceInfo ?: return emptyList() val moduleSourceInfo = moduleInfo as? ModuleSourceInfo ?: return emptyList()
val implementingModuleInfos = moduleSourceInfo.module.implementingModules.mapNotNull { it.toInfo(moduleSourceInfo.isTests()) } val implementingModuleInfos = moduleSourceInfo.module.implementingModules.mapNotNull { it.toInfo(moduleSourceInfo.sourceType) }
return implementingModuleInfos.mapNotNull { it.toDescriptor() } return implementingModuleInfos.mapNotNull { it.toDescriptor() }
} }
private fun Module.toInfo(isTests: Boolean): ModuleSourceInfo? = private fun Module.toInfo(type: SourceType): ModuleSourceInfo? = when (type) {
if (isTests) testSourceInfo() else productionSourceInfo() PRODUCTION -> productionSourceInfo()
TEST -> testSourceInfo()
}
val ModuleDescriptor.implementedDescriptors: List<ModuleDescriptor> val ModuleDescriptor.implementedDescriptors: List<ModuleDescriptor>
get() { get() {
@@ -112,7 +122,7 @@ fun PsiElement.getPlatformModuleInfo(desiredPlatform: TargetPlatform): PlatformM
val moduleInfo = getModuleInfo() as? ModuleSourceInfo ?: return null val moduleInfo = getModuleInfo() as? ModuleSourceInfo ?: return null
return when (moduleInfo.platform) { return when (moduleInfo.platform) {
TargetPlatform.Common -> { TargetPlatform.Common -> {
val correspondingImplementingModule = moduleInfo.module.implementingModules.map { it.toInfo(moduleInfo.isTests()) } val correspondingImplementingModule = moduleInfo.module.implementingModules.map { it.toInfo(moduleInfo.sourceType) }
.firstOrNull { it?.platform == desiredPlatform } ?: return null .firstOrNull { it?.platform == desiredPlatform } ?: return null
PlatformModuleInfo(correspondingImplementingModule, correspondingImplementingModule.expectedBy) PlatformModuleInfo(correspondingImplementingModule, correspondingImplementingModule.expectedBy)
} }
@@ -12,12 +12,20 @@ import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.config.KotlinResourceRootType import org.jetbrains.kotlin.config.KotlinResourceRootType
import org.jetbrains.kotlin.config.KotlinSourceRootType import org.jetbrains.kotlin.config.KotlinSourceRootType
import org.jetbrains.kotlin.idea.caches.project.SourceType
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
private val testRootTypes: Set<JpsModuleSourceRootType<*>> = setOf( private val testRootTypes: Set<JpsModuleSourceRootType<*>> = setOf(
JavaSourceRootType.TEST_SOURCE, JavaSourceRootType.TEST_SOURCE,
JavaResourceRootType.TEST_RESOURCE, JavaResourceRootType.TEST_RESOURCE,
KotlinSourceRootType.TestSource, KotlinSourceRootType.TestSource,
KotlinResourceRootType.TestResource KotlinResourceRootType.TestResource
) )
fun FileIndex.isInTestSourceContentKotlinAware(fileOrDir: VirtualFile) = isUnderSourceRootOfType(fileOrDir, testRootTypes) fun FileIndex.isInTestSourceContentKotlinAware(fileOrDir: VirtualFile) = isUnderSourceRootOfType(fileOrDir, testRootTypes)
fun FileIndex.getSourceType(fileOrDir: VirtualFile): SourceType? = when {
isInTestSourceContentKotlinAware(fileOrDir) -> SourceType.TEST
isInSourceContentWithoutInjected(fileOrDir) -> SourceType.PRODUCTION
else -> null
}
@@ -9,13 +9,14 @@ import com.intellij.execution.Location
import com.intellij.execution.PsiLocation import com.intellij.execution.PsiLocation
import com.intellij.execution.actions.MultipleRunLocationsProvider import com.intellij.execution.actions.MultipleRunLocationsProvider
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ModuleRootManager
import org.jetbrains.kotlin.config.KotlinModuleKind import org.jetbrains.kotlin.config.KotlinModuleKind
import org.jetbrains.kotlin.idea.caches.project.SourceType
import org.jetbrains.kotlin.idea.caches.project.implementingModules import org.jetbrains.kotlin.idea.caches.project.implementingModules
import org.jetbrains.kotlin.idea.caches.project.isNewMPPModule import org.jetbrains.kotlin.idea.caches.project.isNewMPPModule
import org.jetbrains.kotlin.idea.caches.project.sourceType
import org.jetbrains.kotlin.idea.configuration.toModuleGroup import org.jetbrains.kotlin.idea.configuration.toModuleGroup
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware import org.jetbrains.kotlin.idea.core.getSourceType
import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.idea.facet.KotlinFacet
class KotlinMultiplatformRunLocationsProvider : MultipleRunLocationsProvider() { class KotlinMultiplatformRunLocationsProvider : MultipleRunLocationsProvider() {
@@ -27,8 +28,8 @@ class KotlinMultiplatformRunLocationsProvider : MultipleRunLocationsProvider() {
override fun getAlternativeLocations(originalLocation: Location<*>): List<Location<*>> { override fun getAlternativeLocations(originalLocation: Location<*>): List<Location<*>> {
val originalModule = originalLocation.module ?: return emptyList() val originalModule = originalLocation.module ?: return emptyList()
val virtualFile = originalLocation.virtualFile ?: return emptyList() val virtualFile = originalLocation.virtualFile ?: return emptyList()
val shouldRunAsTest = ModuleRootManager.getInstance(originalModule).fileIndex.isInTestSourceContentKotlinAware(virtualFile) val sourceType = ModuleRootManager.getInstance(originalModule).fileIndex.getSourceType(virtualFile) ?: return emptyList()
return modulesToRunFrom(originalModule, shouldRunAsTest).map { PsiLocation(originalLocation.project, it, originalLocation.psiElement) } return modulesToRunFrom(originalModule, sourceType).map { PsiLocation(originalLocation.project, it, originalLocation.psiElement) }
} }
} }
@@ -43,17 +44,12 @@ private fun compactedGradleProjectId(module: Module): String? {
private fun modulesToRunFrom( private fun modulesToRunFrom(
originalModule: Module, originalModule: Module,
shouldRunAsTest: Boolean originalSourceType: SourceType
): List<Module> { ): List<Module> {
val modules = originalModule.implementingModules val modules = originalModule.implementingModules
if (!originalModule.isNewMPPModule) return modules if (!originalModule.isNewMPPModule) return modules
val compilations = modules.filter { val compilations = modules.filter {
KotlinFacet.get(it)?.configuration?.settings?.kind == KotlinModuleKind.COMPILATION_AND_SOURCE_SET_HOLDER KotlinFacet.get(it)?.configuration?.settings?.kind == KotlinModuleKind.COMPILATION_AND_SOURCE_SET_HOLDER
} }
return compilations.filter { it.sourceType == originalSourceType }
val project = originalModule.project
// TODO: more robust way to get test/production compilations
val baseName = originalModule.toModuleGroup().baseModule.name
val commonTests = ModuleManager.getInstance(project).findModuleByName(baseName + "_commonTest") ?: return compilations
return compilations.filter { ModuleRootManager.getInstance(it).isDependsOn(commonTests) == shouldRunAsTest }
} }