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