Avoid creating ModuleSourceInfo instances with empty content
This is overall desirable because it means less ModuleDescriptor
instances which is good for IDE analysis performance
This commit is contained in:
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.caches.resolve
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.impl.scopes.LibraryScopeBase
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.rootManager
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.*
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
@@ -30,6 +31,7 @@ import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import com.intellij.util.PathUtil
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.TrackableModuleInfo
|
||||
import org.jetbrains.kotlin.caches.resolve.LibraryModuleInfo
|
||||
@@ -42,6 +44,7 @@ import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.jvm.GlobalSearchScopeWithModuleSources
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.util.*
|
||||
|
||||
interface IdeaModuleInfo : ModuleInfo {
|
||||
@@ -56,7 +59,7 @@ interface IdeaModuleInfo : ModuleInfo {
|
||||
}
|
||||
|
||||
private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, productionOnly: Boolean): List<IdeaModuleInfo> {
|
||||
fun Module.toInfos() = if (productionOnly) listOf(productionSourceInfo()) else listOf(testSourceInfo(), productionSourceInfo())
|
||||
fun Module.toInfos() = correspondingModuleInfos().filter { !productionOnly || it is ModuleProductionSourceInfo }
|
||||
|
||||
if (!orderEntry.isValid) return emptyList()
|
||||
|
||||
@@ -115,7 +118,7 @@ interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo {
|
||||
KotlinModuleModificationTracker(module)
|
||||
}
|
||||
|
||||
data class ModuleProductionSourceInfo(override val module: Module) : ModuleSourceInfo {
|
||||
data class ModuleProductionSourceInfo internal constructor(override val module: Module) : ModuleSourceInfo {
|
||||
override val name = Name.special("<production sources for module ${module.name}>")
|
||||
|
||||
override fun contentScope(): GlobalSearchScope = ModuleProductionSourceScope(module)
|
||||
@@ -128,7 +131,7 @@ data class ModuleProductionSourceInfo(override val module: Module) : ModuleSourc
|
||||
}
|
||||
|
||||
//TODO: (module refactoring) do not create ModuleTestSourceInfo when there are no test roots for module
|
||||
data class ModuleTestSourceInfo(override val module: Module) : ModuleSourceInfo {
|
||||
data class ModuleTestSourceInfo internal constructor(override val module: Module) : ModuleSourceInfo {
|
||||
override val name = Name.special("<test sources for module ${module.name}>")
|
||||
|
||||
override val displayedName get() = module.name + " (test)"
|
||||
@@ -142,7 +145,9 @@ data class ModuleTestSourceInfo(override val module: Module) : ModuleSourceInfo
|
||||
})
|
||||
|
||||
override fun modulesWhoseInternalsAreVisible() = module.cached(CachedValueProvider {
|
||||
val list = SmartList<ModuleInfo>(module.productionSourceInfo())
|
||||
val list = SmartList<ModuleInfo>()
|
||||
|
||||
list.addIfNotNull(module.productionSourceInfo())
|
||||
|
||||
TestModuleProperties.getInstance(module).productionModule?.let {
|
||||
list.add(it.productionSourceInfo())
|
||||
@@ -154,8 +159,16 @@ data class ModuleTestSourceInfo(override val module: Module) : ModuleSourceInfo
|
||||
|
||||
internal fun ModuleSourceInfo.isTests() = this is ModuleTestSourceInfo
|
||||
|
||||
fun Module.productionSourceInfo(): ModuleProductionSourceInfo = ModuleProductionSourceInfo(this)
|
||||
fun Module.testSourceInfo(): ModuleTestSourceInfo = ModuleTestSourceInfo(this)
|
||||
fun Module.productionSourceInfo(): ModuleProductionSourceInfo? = if (hasProductionRoots()) ModuleProductionSourceInfo(this) else null
|
||||
fun Module.testSourceInfo(): ModuleTestSourceInfo? = if (hasTestRoots()) ModuleTestSourceInfo(this) else null
|
||||
|
||||
internal fun Module.correspondingModuleInfos(): List<ModuleSourceInfo> = listOf(testSourceInfo(), productionSourceInfo()).filterNotNull()
|
||||
|
||||
private fun Module.hasProductionRoots() = hasRootsOfType(JavaSourceRootType.SOURCE)
|
||||
private fun Module.hasTestRoots() = hasRootsOfType(JavaSourceRootType.TEST_SOURCE)
|
||||
|
||||
private fun Module.hasRootsOfType(sourceRootType: JavaSourceRootType): Boolean =
|
||||
rootManager.contentEntries.any { it.getSourceFolders(sourceRootType).isNotEmpty() }
|
||||
|
||||
private abstract class ModuleSourceScope(val module: Module) : GlobalSearchScope(module.project), GlobalSearchScopeWithModuleSources {
|
||||
override fun compare(file1: VirtualFile, file2: VirtualFile) = 0
|
||||
@@ -266,7 +279,7 @@ data class SdkInfo(val project: Project, val sdk: Sdk) : IdeaModuleInfo {
|
||||
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
|
||||
}
|
||||
|
||||
internal object NotUnderContentRootModuleInfo : IdeaModuleInfo {
|
||||
object NotUnderContentRootModuleInfo : IdeaModuleInfo {
|
||||
override val moduleOrigin: ModuleOrigin
|
||||
get() = ModuleOrigin.OTHER
|
||||
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.idea.caches.resolve
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
@@ -102,7 +103,7 @@ fun createModuleResolverProvider(
|
||||
|
||||
fun collectAllModuleInfosFromIdeaModel(project: Project): List<IdeaModuleInfo> {
|
||||
val ideaModules = ModuleManager.getInstance(project).modules.toList()
|
||||
val modulesSourcesInfos = ideaModules.flatMap { listOf(it.productionSourceInfo(), it.testSourceInfo()) }
|
||||
val modulesSourcesInfos = ideaModules.flatMap(Module::correspondingModuleInfos)
|
||||
|
||||
//TODO: (module refactoring) include libraries that are not among dependencies of any module
|
||||
val ideaLibraries = ideaModules.flatMap {
|
||||
|
||||
@@ -33,10 +33,10 @@ import java.util.HashSet
|
||||
fun ModuleSourceInfo.getDependentModules(): Set<ModuleSourceInfo> {
|
||||
val dependents = getDependents(module)
|
||||
return if (isTests()) {
|
||||
dependents.mapTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
|
||||
dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
|
||||
}
|
||||
else {
|
||||
dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { listOf(it.productionSourceInfo(), it.testSourceInfo()) }
|
||||
dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { it.correspondingModuleInfos() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,8 +54,7 @@ import org.jetbrains.kotlin.console.gutter.ConsoleIndicatorRenderer
|
||||
import org.jetbrains.kotlin.console.gutter.IconWithTooltip
|
||||
import org.jetbrains.kotlin.console.gutter.ReplIcons
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ModuleTestSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
||||
import org.jetbrains.kotlin.idea.project.KOTLIN_CONSOLE_KEY
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -160,7 +159,7 @@ class KotlinConsoleRunner(
|
||||
|
||||
private fun enableCompletion(consoleView: LanguageConsoleView) {
|
||||
val consoleKtFile = PsiManager.getInstance(project).findFile(consoleView.virtualFile) as? KtFile ?: return
|
||||
consoleKtFile.moduleInfo = ModuleTestSourceInfo(module)
|
||||
configureFileDependencies(consoleKtFile)
|
||||
}
|
||||
|
||||
override fun createProcessHandler(process: Process): OSProcessHandler {
|
||||
@@ -268,7 +267,7 @@ class KotlinConsoleRunner(
|
||||
?: error("Failed to setup PSI for file:\n$text")
|
||||
|
||||
replState.submitLine(psiFile)
|
||||
psiFile.moduleInfo = ModuleTestSourceInfo(module)
|
||||
configureFileDependencies(psiFile)
|
||||
val scriptDescriptor = psiFile.script!!.resolveToDescriptor() as? LazyScriptDescriptor ?: error("Failed to analyze line:\n$text")
|
||||
ForceResolveUtil.forceResolveAllContents(scriptDescriptor)
|
||||
replState.lineSuccess(psiFile, scriptDescriptor)
|
||||
@@ -282,4 +281,8 @@ class KotlinConsoleRunner(
|
||||
val consoleFile = consoleView.virtualFile
|
||||
return PsiManager.getInstance(project).findFile(consoleFile) as KtFile
|
||||
}
|
||||
|
||||
private fun configureFileDependencies(psiFile: KtFile) {
|
||||
psiFile.moduleInfo = module.testSourceInfo() ?: module.productionSourceInfo() ?: NotUnderContentRootModuleInfo
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import com.intellij.openapi.module.StdModuleTypes
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.testFramework.ModuleTestCase
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
||||
@@ -271,6 +273,33 @@ class IdeaModuleInfoTest : ModuleTestCase() {
|
||||
lib3.classes.assertAdditionalLibraryDependencies(lib1.classes, lib2.classes)
|
||||
}
|
||||
|
||||
fun testRoots() {
|
||||
val a = module("a", hasProductionRoot = true, hasTestRoot = false)
|
||||
|
||||
val empty = module("empty", hasProductionRoot = false, hasTestRoot = false)
|
||||
a.addDependency(empty)
|
||||
|
||||
val b = module("b", hasProductionRoot = false, hasTestRoot = true)
|
||||
b.addDependency(a)
|
||||
|
||||
val c = module("c")
|
||||
c.addDependency(b)
|
||||
c.addDependency(a)
|
||||
|
||||
assertNotNull(a.productionSourceInfo())
|
||||
assertNull(a.testSourceInfo())
|
||||
|
||||
assertNull(empty.productionSourceInfo())
|
||||
assertNull(empty.testSourceInfo())
|
||||
|
||||
assertNull(b.productionSourceInfo())
|
||||
assertNotNull(b.testSourceInfo())
|
||||
|
||||
b.test.assertDependenciesEqual(b.test, a.production)
|
||||
c.test.assertDependenciesEqual(c.test, c.production, b.test, a.production)
|
||||
c.production.assertDependenciesEqual(c.production, a.production)
|
||||
}
|
||||
|
||||
fun testCommonLibraryDoesNotDependOnPlatform() {
|
||||
val stdlibCommon = stdlibCommon()
|
||||
val stdlibJvm = stdlibJvm()
|
||||
@@ -296,18 +325,25 @@ class IdeaModuleInfoTest : ModuleTestCase() {
|
||||
) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported)
|
||||
|
||||
private val Module.production: ModuleProductionSourceInfo
|
||||
get() = productionSourceInfo()
|
||||
get() = productionSourceInfo()!!
|
||||
|
||||
private val Module.test: ModuleTestSourceInfo
|
||||
get() = testSourceInfo()
|
||||
get() = testSourceInfo()!!
|
||||
|
||||
private val Library.classes: LibraryInfo
|
||||
get() = LibraryInfo(project!!, this)
|
||||
|
||||
private fun module(name: String): Module {
|
||||
return createModuleFromTestData(createTempDirectory()!!.absolutePath, name, StdModuleTypes.JAVA, false)!!
|
||||
private fun module(name: String, hasProductionRoot: Boolean = true, hasTestRoot: Boolean = true): Module {
|
||||
return createModuleFromTestData(createTempDirectory()!!.absolutePath, name, StdModuleTypes.JAVA, false)!!.apply {
|
||||
if (hasProductionRoot)
|
||||
PsiTestUtil.addSourceContentToRoots(this, dir(), false)
|
||||
if (hasTestRoot)
|
||||
PsiTestUtil.addSourceContentToRoots(this, dir(), true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dir() = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(createTempDirectory())!!
|
||||
|
||||
private fun modules(name1: String = "a", name2: String = "b", name3: String = "c") = Triple(module(name1), module(name2), module(name3))
|
||||
|
||||
private fun IdeaModuleInfo.assertDependenciesEqual(vararg expected: IdeaModuleInfo) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.repl
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
|
||||
@@ -28,6 +29,7 @@ import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescrip
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractIdeReplCompletionTest : KotlinFixtureCompletionBaseTestCase() {
|
||||
@@ -36,9 +38,11 @@ abstract class AbstractIdeReplCompletionTest : KotlinFixtureCompletionBaseTestCa
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
consoleRunner = KotlinConsoleKeeper.getInstance(project).run(myModule)!!
|
||||
VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory())
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory())
|
||||
consoleRunner?.dispose()
|
||||
consoleRunner = null
|
||||
super.tearDown()
|
||||
|
||||
Reference in New Issue
Block a user