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() }
}
internal fun ModuleSourceInfo.isTests() = this is ModuleTestSourceInfo
fun Module.productionSourceInfo(): ModuleProductionSourceInfo? = if (hasProductionRoots()) ModuleProductionSourceInfo(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>? =
(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
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.roots.ModuleRootManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleOrderEntry
import gnu.trove.THashSet
import com.intellij.util.containers.Queue
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.CachedValueProvider
import com.intellij.openapi.roots.ModuleRootManager
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
fun ModuleSourceInfo.getDependentModules(): Set<ModuleSourceInfo> {
val dependents = getDependents(module)
return if (isTests()) {
dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
} else {
dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { it.correspondingModuleInfos() }
return when (sourceType) {
SourceType.TEST -> dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
SourceType.PRODUCTION -> 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.caches.resolve.KotlinCacheService
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.KotlinFacetType
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
val Module.isNewMPPModule: Boolean
get() = KotlinFacet.get(this)?.configuration?.settings?.kind?.isNewMPP ?: false
get() = facetSettings?.kind?.isNewMPP ?: false
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
get() {
val settings = KotlinFacet.get(this)?.configuration?.settings ?: return false
val settings = facetSettings ?: return false
return settings.platform.isCommon ||
settings.implementedModuleNames.isNotEmpty() ||
settings.kind.isNewMPP
}
private val Module.facetSettings get() = KotlinFacet.get(this)?.configuration?.settings
val Module.implementingModules: List<Module>
get() = cached(CachedValueProvider {
val moduleManager = ModuleManager.getInstance(project)
@@ -87,12 +94,15 @@ val ModuleDescriptor.implementingDescriptors: List<ModuleDescriptor>
return listOf(this)
}
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() }
}
private fun Module.toInfo(isTests: Boolean): ModuleSourceInfo? =
if (isTests) testSourceInfo() else productionSourceInfo()
private fun Module.toInfo(type: SourceType): ModuleSourceInfo? = when (type) {
PRODUCTION -> productionSourceInfo()
TEST -> testSourceInfo()
}
val ModuleDescriptor.implementedDescriptors: List<ModuleDescriptor>
get() {
@@ -112,7 +122,7 @@ fun PsiElement.getPlatformModuleInfo(desiredPlatform: TargetPlatform): PlatformM
val moduleInfo = getModuleInfo() as? ModuleSourceInfo ?: return null
return when (moduleInfo.platform) {
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
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.kotlin.config.KotlinResourceRootType
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(
JavaSourceRootType.TEST_SOURCE,
JavaResourceRootType.TEST_RESOURCE,
KotlinSourceRootType.TestSource,
KotlinResourceRootType.TestResource
JavaSourceRootType.TEST_SOURCE,
JavaResourceRootType.TEST_RESOURCE,
KotlinSourceRootType.TestSource,
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.actions.MultipleRunLocationsProvider
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.ModuleRootManager
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.isNewMPPModule
import org.jetbrains.kotlin.idea.caches.project.sourceType
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
class KotlinMultiplatformRunLocationsProvider : MultipleRunLocationsProvider() {
@@ -27,8 +28,8 @@ class KotlinMultiplatformRunLocationsProvider : MultipleRunLocationsProvider() {
override fun getAlternativeLocations(originalLocation: Location<*>): List<Location<*>> {
val originalModule = originalLocation.module ?: return emptyList()
val virtualFile = originalLocation.virtualFile ?: return emptyList()
val shouldRunAsTest = ModuleRootManager.getInstance(originalModule).fileIndex.isInTestSourceContentKotlinAware(virtualFile)
return modulesToRunFrom(originalModule, shouldRunAsTest).map { PsiLocation(originalLocation.project, it, originalLocation.psiElement) }
val sourceType = ModuleRootManager.getInstance(originalModule).fileIndex.getSourceType(virtualFile) ?: return emptyList()
return modulesToRunFrom(originalModule, sourceType).map { PsiLocation(originalLocation.project, it, originalLocation.psiElement) }
}
}
@@ -43,17 +44,12 @@ private fun compactedGradleProjectId(module: Module): String? {
private fun modulesToRunFrom(
originalModule: Module,
shouldRunAsTest: Boolean
originalSourceType: SourceType
): List<Module> {
val modules = originalModule.implementingModules
if (!originalModule.isNewMPPModule) return modules
val compilations = modules.filter {
KotlinFacet.get(it)?.configuration?.settings?.kind == KotlinModuleKind.COMPILATION_AND_SOURCE_SET_HOLDER
}
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 }
return compilations.filter { it.sourceType == originalSourceType }
}