Recreate module descriptors for affected modules only on a change
This commit is contained in:
@@ -18,7 +18,9 @@ package org.jetbrains.kotlin.analyzer
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
@@ -85,9 +87,17 @@ class ResolverForProjectImpl<M : ModuleInfo>(
|
||||
private val packagePartProviderFactory: (M, ModuleContent) -> PackagePartProvider = { _, _ -> PackagePartProvider.Empty },
|
||||
private val firstDependency: M? = null,
|
||||
private val modulePlatforms: (M) -> MultiTargetPlatform?,
|
||||
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory
|
||||
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory,
|
||||
private val invalidateOnOOCB: Boolean = true
|
||||
) : ResolverForProject<M>() {
|
||||
private val descriptorByModule = mutableMapOf<M, ModuleDescriptorImpl>()
|
||||
|
||||
private class ModuleData(
|
||||
val moduleDescriptor: ModuleDescriptorImpl,
|
||||
val modificationTracker: ModificationTracker?,
|
||||
val modificationCount: Long?
|
||||
)
|
||||
|
||||
private val descriptorByModule = mutableMapOf<M, ModuleData>()
|
||||
private val moduleInfoByDescriptor = mutableMapOf<ModuleDescriptorImpl, M>()
|
||||
val modules = modules.toSet()
|
||||
|
||||
@@ -156,18 +166,42 @@ class ResolverForProjectImpl<M : ModuleInfo>(
|
||||
private fun doGetDescriptorForModule(module: M): ModuleDescriptorImpl {
|
||||
if (module in modules) {
|
||||
return projectContext.storageManager.compute {
|
||||
descriptorByModule.getOrPut(module) {
|
||||
ModuleDescriptorImpl(module.name,
|
||||
projectContext.storageManager, builtIns, modulePlatforms(module), module.capabilities).apply {
|
||||
moduleInfoByDescriptor[this] = module
|
||||
setupModuleDescriptor(module, this)
|
||||
}
|
||||
var moduleData = descriptorByModule.getOrPut(module) {
|
||||
createModuleDescriptor(module)
|
||||
}
|
||||
val currentModCount = moduleData.modificationTracker?.modificationCount
|
||||
if (currentModCount != null && currentModCount > moduleData.modificationCount!!) {
|
||||
moduleData = recreateModuleDescriptor(module)
|
||||
}
|
||||
moduleData.moduleDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
return delegateResolver.descriptorForModule(module) as ModuleDescriptorImpl
|
||||
}
|
||||
|
||||
private fun recreateModuleDescriptor(module: M): ModuleData {
|
||||
val oldDescriptor = descriptorByModule[module]?.moduleDescriptor
|
||||
if (oldDescriptor != null) {
|
||||
oldDescriptor.isValid = false
|
||||
moduleInfoByDescriptor.remove(oldDescriptor)
|
||||
resolverByModuleDescriptor.remove(oldDescriptor)
|
||||
}
|
||||
|
||||
val moduleData = createModuleDescriptor(module)
|
||||
descriptorByModule[module] = moduleData
|
||||
return moduleData
|
||||
}
|
||||
|
||||
private fun createModuleDescriptor(module: M): ModuleData {
|
||||
val moduleDescriptor = ModuleDescriptorImpl(module.name,
|
||||
projectContext.storageManager, builtIns, modulePlatforms(module), module.capabilities)
|
||||
moduleInfoByDescriptor[moduleDescriptor] = module
|
||||
setupModuleDescriptor(module, moduleDescriptor)
|
||||
val modificationTracker = (module as? TrackableModuleInfo)?.createModificationTracker() ?:
|
||||
(PsiModificationTracker.SERVICE.getInstance(projectContext.project).outOfCodeBlockModificationTracker.takeIf { invalidateOnOOCB })
|
||||
return ModuleData(moduleDescriptor, modificationTracker, modificationTracker?.modificationCount)
|
||||
}
|
||||
}
|
||||
|
||||
data class ModuleContent(
|
||||
@@ -207,6 +241,10 @@ interface ModuleInfo {
|
||||
}
|
||||
}
|
||||
|
||||
interface TrackableModuleInfo : ModuleInfo {
|
||||
fun createModificationTracker(): ModificationTracker
|
||||
}
|
||||
|
||||
abstract class AnalyzerFacade {
|
||||
abstract fun <M : ModuleInfo> createResolverForModule(
|
||||
moduleInfo: M,
|
||||
|
||||
@@ -48,4 +48,6 @@ interface ModuleDescriptor : DeclarationDescriptor {
|
||||
class Capability<T>(val name: String) {
|
||||
override fun toString() = name
|
||||
}
|
||||
|
||||
val isValid: Boolean
|
||||
}
|
||||
|
||||
@@ -47,6 +47,14 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
|
||||
private var dependencies: ModuleDependencies? = null
|
||||
private var packageFragmentProviderForModuleContent: PackageFragmentProvider? = null
|
||||
|
||||
override var isValid: Boolean = true
|
||||
|
||||
private fun assertValid() {
|
||||
if (!isValid) {
|
||||
throw IllegalStateException("Accessing invalid module descriptor $this")
|
||||
}
|
||||
}
|
||||
|
||||
private val packages = storageManager.createMemoizedFunction<FqName, PackageViewDescriptor> {
|
||||
fqName: FqName -> LazyPackageViewDescriptorImpl(this, fqName, storageManager)
|
||||
}
|
||||
@@ -60,9 +68,13 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
|
||||
override val allImplementingModules: Set<ModuleDescriptor>
|
||||
get() = this.dependencies.sure { "Dependencies of module $id were not set" }.allImplementingModules
|
||||
|
||||
override fun getPackage(fqName: FqName): PackageViewDescriptor = packages(fqName)
|
||||
override fun getPackage(fqName: FqName): PackageViewDescriptor {
|
||||
assertValid()
|
||||
return packages(fqName)
|
||||
}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
|
||||
assertValid()
|
||||
return packageFragmentProvider.getSubPackagesOf(fqName, nameFilter)
|
||||
}
|
||||
|
||||
@@ -117,7 +129,10 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
val packageFragmentProvider: PackageFragmentProvider
|
||||
get() = packageFragmentProviderForWholeModuleWithDependencies
|
||||
get() {
|
||||
assertValid()
|
||||
return packageFragmentProviderForWholeModuleWithDependencies
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T> getCapability(capability: ModuleDescriptor.Capability<T>) = capabilities[capability] as? T
|
||||
|
||||
@@ -122,6 +122,11 @@ public class ErrorUtils {
|
||||
public KotlinBuiltIns getBuiltIns() {
|
||||
return DefaultBuiltIns.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.*
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
@@ -30,9 +31,11 @@ import com.intellij.psi.util.CachedValuesManager
|
||||
import com.intellij.util.PathUtil
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.TrackableModuleInfo
|
||||
import org.jetbrains.kotlin.caches.resolve.LibraryModuleInfo
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.framework.getLibraryPlatform
|
||||
import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
|
||||
@@ -97,7 +100,7 @@ private fun ideaModelDependencies(module: Module, productionOnly: Boolean): List
|
||||
return result.toList()
|
||||
}
|
||||
|
||||
interface ModuleSourceInfo : IdeaModuleInfo {
|
||||
interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo {
|
||||
val module: Module
|
||||
|
||||
override val displayedName get() = module.name
|
||||
@@ -107,6 +110,9 @@ interface ModuleSourceInfo : IdeaModuleInfo {
|
||||
|
||||
override val platform: TargetPlatform
|
||||
get() = TargetPlatformDetector.getPlatform(module)
|
||||
|
||||
override fun createModificationTracker(): ModificationTracker =
|
||||
KotlinModuleModificationTracker(module)
|
||||
}
|
||||
|
||||
data class ModuleProductionSourceInfo(override val module: Module) : ModuleSourceInfo {
|
||||
|
||||
+6
-1
@@ -125,6 +125,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
LibraryModificationTracker.getInstance(project),
|
||||
ProjectRootModificationTracker.getInstance(project)
|
||||
),
|
||||
invalidateOnOOCB = false,
|
||||
reuseDataFrom = null
|
||||
)
|
||||
|
||||
@@ -134,6 +135,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
project, librariesContext, settings,
|
||||
reuseDataFrom = facadeForSdk,
|
||||
moduleFilter = { it is LibraryInfo },
|
||||
invalidateOnOOCB = false,
|
||||
dependencies = listOf(
|
||||
LibraryModificationTracker.getInstance(project),
|
||||
ProjectRootModificationTracker.getInstance(project)
|
||||
@@ -146,7 +148,10 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
project, modulesContext, settings,
|
||||
reuseDataFrom = facadeForLibraries,
|
||||
moduleFilter = { !it.isLibraryClasses() },
|
||||
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
||||
dependencies = listOf(
|
||||
LibraryModificationTracker.getInstance(project),
|
||||
ProjectRootModificationTracker.getInstance(project)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -52,7 +52,8 @@ fun createModuleResolverProvider(
|
||||
moduleFilter: (IdeaModuleInfo) -> Boolean,
|
||||
allModules: Collection<IdeaModuleInfo>?,
|
||||
providedBuiltIns: KotlinBuiltIns?, // null means create new builtins based on SDK
|
||||
dependencies: Collection<Any>
|
||||
dependencies: Collection<Any>,
|
||||
invalidateOnOOCB: Boolean = true
|
||||
): ModuleResolverProvider {
|
||||
val builtIns = providedBuiltIns ?: createBuiltIns(analysisSettings, globalContext)
|
||||
|
||||
@@ -82,7 +83,8 @@ fun createModuleResolverProvider(
|
||||
delegateResolver, { _, c -> IDEPackagePartProvider(c.moduleContentScope) },
|
||||
analysisSettings.sdk?.let { SdkInfo(project, it) },
|
||||
modulePlatforms = { module -> module.platform?.multiTargetPlatform },
|
||||
packageOracleFactory = project.service<IdePackageOracleFactory>()
|
||||
packageOracleFactory = project.service<IdePackageOracleFactory>(),
|
||||
invalidateOnOOCB = invalidateOnOOCB
|
||||
)
|
||||
|
||||
if (providedBuiltIns == null && builtIns is JvmBuiltIns) {
|
||||
|
||||
+6
-4
@@ -30,14 +30,15 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CompositeBindingContext
|
||||
|
||||
internal class ProjectResolutionFacade(
|
||||
val debugString: String,
|
||||
val resolverDebugName: String,
|
||||
private val debugString: String,
|
||||
private val resolverDebugName: String,
|
||||
val project: Project,
|
||||
val globalContext: GlobalContextImpl,
|
||||
val settings: PlatformAnalysisSettings,
|
||||
val reuseDataFrom: ProjectResolutionFacade?,
|
||||
val moduleFilter: (IdeaModuleInfo) -> Boolean,
|
||||
val dependencies: List<Any>,
|
||||
private val invalidateOnOOCB: Boolean = true,
|
||||
val syntheticFiles: Collection<KtFile> = listOf(),
|
||||
val allModules: Collection<IdeaModuleInfo>? = null // null means create resolvers for modules from idea model
|
||||
) {
|
||||
@@ -61,11 +62,12 @@ internal class ProjectResolutionFacade(
|
||||
delegateResolver = delegateResolverForProject, moduleFilter = moduleFilter,
|
||||
allModules = allModules,
|
||||
providedBuiltIns = delegateResolverProvider?.builtIns,
|
||||
dependencies = dependencies
|
||||
dependencies = dependencies,
|
||||
invalidateOnOOCB = invalidateOnOOCB
|
||||
)
|
||||
}
|
||||
|
||||
val moduleResolverProvider: ModuleResolverProvider
|
||||
private val moduleResolverProvider: ModuleResolverProvider
|
||||
get() = globalContext.storageManager.compute { cachedValue.value }
|
||||
|
||||
fun resolverForModuleInfo(moduleInfo: IdeaModuleInfo) = moduleResolverProvider.resolverForProject.resolverForModule(moduleInfo)
|
||||
|
||||
+73
-3
@@ -16,8 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.project
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.pom.PomManager
|
||||
import com.intellij.pom.PomModelAspect
|
||||
import com.intellij.pom.event.PomModelEvent
|
||||
@@ -28,6 +32,7 @@ import com.intellij.psi.PsiCodeFragment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import com.intellij.util.CommonProcessors
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getTopmostParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
@@ -41,10 +46,27 @@ class KotlinCodeBlockModificationListener(
|
||||
project: Project,
|
||||
private val treeAspect: TreeAspect
|
||||
) {
|
||||
private val perModuleModCount = mutableMapOf<Module, Long>()
|
||||
private val modificationTrackerImpl = modificationTracker as PsiModificationTrackerImpl
|
||||
|
||||
private var lastAffectedModule: Module? = null
|
||||
private var lastAffectedModuleModCount = -1L
|
||||
|
||||
// All modifications since that count are known to be single-module modifications reflected in
|
||||
// perModuleModCount map
|
||||
private var perModuleChangesHighwatermark: Long? = null
|
||||
|
||||
fun getModificationCount(module: Module): Long {
|
||||
return perModuleModCount[module] ?:
|
||||
perModuleChangesHighwatermark ?:
|
||||
modificationTrackerImpl.outOfCodeBlockModificationCount
|
||||
}
|
||||
|
||||
fun hasPerModuleModificationCounts() = perModuleChangesHighwatermark != null
|
||||
|
||||
init {
|
||||
val model = PomManager.getModel(project)
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val modificationTracker = modificationTracker as PsiModificationTrackerImpl
|
||||
val messageBusConnection = project.messageBus.connect()
|
||||
model.addModelListener(object: PomModelListener {
|
||||
override fun isAspectChangeInteresting(aspect: PomModelAspect): Boolean {
|
||||
return aspect == treeAspect
|
||||
@@ -58,13 +80,31 @@ class KotlinCodeBlockModificationListener(
|
||||
// contents to be replaced, which is represented in a POM event as an empty list of changed elements
|
||||
if (changedElements.any { getInsideCodeBlockModificationScope(it.psi) == null } ||
|
||||
(file is PsiCodeFragment && changedElements.isEmpty())) {
|
||||
messageBusConnection.deliverImmediately()
|
||||
if (file.isPhysical) {
|
||||
modificationTracker.incCounter()
|
||||
lastAffectedModule = ModuleUtil.findModuleForPsiElement(file)
|
||||
lastAffectedModuleModCount = modificationTrackerImpl.outOfCodeBlockModificationCount
|
||||
modificationTrackerImpl.incCounter()
|
||||
}
|
||||
incOutOfBlockModificationCount(file)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
messageBusConnection.subscribe(PsiModificationTracker.TOPIC, PsiModificationTracker.Listener {
|
||||
val newModCount = modificationTrackerImpl.outOfCodeBlockModificationCount
|
||||
val affectedModule = lastAffectedModule
|
||||
if (affectedModule != null && newModCount == lastAffectedModuleModCount + 1) {
|
||||
if (perModuleChangesHighwatermark== null) {
|
||||
perModuleChangesHighwatermark = lastAffectedModuleModCount
|
||||
}
|
||||
perModuleModCount[affectedModule] = newModCount
|
||||
}
|
||||
else {
|
||||
perModuleChangesHighwatermark = null
|
||||
perModuleModCount.clear()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -117,6 +157,8 @@ class KotlinCodeBlockModificationListener(
|
||||
KtProperty::class.java,
|
||||
KtNamedFunction::class.java
|
||||
)
|
||||
|
||||
fun getInstance(project: Project) = project.getComponent(KotlinCodeBlockModificationListener::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,3 +166,31 @@ private val FILE_OUT_OF_BLOCK_MODIFICATION_COUNT = Key<Long>("FILE_OUT_OF_BLOCK_
|
||||
|
||||
val KtFile.outOfBlockModificationCount: Long
|
||||
get() = getUserData(FILE_OUT_OF_BLOCK_MODIFICATION_COUNT) ?: 0
|
||||
|
||||
class KotlinModuleModificationTracker(val module: Module): ModificationTracker {
|
||||
private val kotlinModCountListener = KotlinCodeBlockModificationListener.getInstance(module.project)
|
||||
private val psiModificationTracker = PsiModificationTracker.SERVICE.getInstance(module.project)
|
||||
private val dependencies by lazy {
|
||||
HashSet<Module>().apply {
|
||||
ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule(
|
||||
CommonProcessors.CollectProcessor(this))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getModificationCount(): Long {
|
||||
val currentGlobalCount = psiModificationTracker.outOfCodeBlockModificationCount
|
||||
|
||||
if (kotlinModCountListener.hasPerModuleModificationCounts()) {
|
||||
val selfCount = kotlinModCountListener.getModificationCount(module)
|
||||
if (selfCount == currentGlobalCount) return selfCount
|
||||
var maxCount = selfCount
|
||||
for (dependency in dependencies) {
|
||||
val depCount = kotlinModCountListener.getModificationCount(dependency)
|
||||
if (depCount == currentGlobalCount) return currentGlobalCount
|
||||
if (depCount > maxCount) maxCount = depCount
|
||||
}
|
||||
return maxCount
|
||||
}
|
||||
return currentGlobalCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package foo.bar.used
|
||||
|
||||
public fun m1() {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import foo.bar.used.*
|
||||
|
||||
public fun m2() {
|
||||
m1()
|
||||
}
|
||||
+87
-19
@@ -17,8 +17,16 @@
|
||||
package org.jetbrains.kotlin.idea.caches.resolve
|
||||
|
||||
import com.intellij.facet.FacetManager
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.ResolverForModuleComputationTracker
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
@@ -30,8 +38,12 @@ import org.jetbrains.kotlin.idea.completion.test.withServiceRegistered
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||
import org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener
|
||||
import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.projectStructure.sdk
|
||||
import org.jetbrains.kotlin.test.TestJdkKind.FULL_JDK
|
||||
|
||||
open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
@@ -66,20 +78,9 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
}
|
||||
|
||||
fun testLazyResolvers() {
|
||||
val resolversComputed = mutableSetOf<Module>()
|
||||
val tracker = ResolverTracker()
|
||||
|
||||
val resolversTracker = object : ResolverForModuleComputationTracker {
|
||||
override fun onResolverComputed(moduleInfo: ModuleInfo) {
|
||||
(moduleInfo as IdeaModuleInfo).let {
|
||||
if (it is ModuleSourceInfo) {
|
||||
val module = it.module
|
||||
resolversComputed.add(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.withServiceRegistered<ResolverForModuleComputationTracker, Unit>(resolversTracker) {
|
||||
project.withServiceRegistered<ResolverForModuleComputationTracker, Unit>(tracker) {
|
||||
val module1 = module("m1")
|
||||
val module2 = module("m2")
|
||||
val module3 = module("m3")
|
||||
@@ -87,15 +88,82 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
module3.addDependency(module2)
|
||||
module3.addDependency(module1)
|
||||
|
||||
assertTrue(module1 !in resolversComputed)
|
||||
assertTrue(module2 !in resolversComputed)
|
||||
assertTrue(module3 !in resolversComputed)
|
||||
assertTrue(module1 !in tracker.moduleResolversComputed)
|
||||
assertTrue(module2 !in tracker.moduleResolversComputed)
|
||||
assertTrue(module3 !in tracker.moduleResolversComputed)
|
||||
|
||||
checkHighlightingInAllFiles { "m3" in file.name }
|
||||
|
||||
assertTrue(module1 in resolversComputed)
|
||||
assertTrue(module2 !in resolversComputed)
|
||||
assertTrue(module3 in resolversComputed)
|
||||
assertTrue(module1 in tracker.moduleResolversComputed)
|
||||
assertTrue(module2 !in tracker.moduleResolversComputed)
|
||||
assertTrue(module3 in tracker.moduleResolversComputed)
|
||||
}
|
||||
}
|
||||
|
||||
class ResolverTracker : ResolverForModuleComputationTracker {
|
||||
val moduleResolversComputed = mutableListOf<Module>()
|
||||
val sdkResolversComputed = mutableListOf<Sdk>()
|
||||
|
||||
override fun onResolverComputed(moduleInfo: ModuleInfo) {
|
||||
when (moduleInfo) {
|
||||
is ModuleSourceInfo -> moduleResolversComputed.add(moduleInfo.module)
|
||||
is SdkInfo -> sdkResolversComputed.add(moduleInfo.sdk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun testRecomputeResolversOnChange() {
|
||||
val tracker = ResolverTracker()
|
||||
|
||||
project.withServiceRegistered<ResolverForModuleComputationTracker, Unit>(tracker) {
|
||||
val module1 = module("m1")
|
||||
val module2 = module("m2")
|
||||
val module3 = module("m3")
|
||||
|
||||
module2.addDependency(module1)
|
||||
module3.addDependency(module2)
|
||||
// Ensure modules have the same SDK instance, and not two distinct SDKs with the same path
|
||||
ModuleRootModificationUtil.setModuleSdk(module2, module1.sdk)
|
||||
|
||||
assertEquals(0, tracker.sdkResolversComputed.size)
|
||||
|
||||
checkHighlightingInAllFiles { "m2" in file.name }
|
||||
|
||||
assertEquals(2, tracker.moduleResolversComputed.size)
|
||||
|
||||
tracker.sdkResolversComputed.clear()
|
||||
tracker.moduleResolversComputed.clear()
|
||||
|
||||
val module1ModCount = KotlinCodeBlockModificationListener.getInstance(myProject).getModificationCount(module1)
|
||||
|
||||
val module1ModTracker = KotlinModuleModificationTracker(module1)
|
||||
val module2ModTracker = KotlinModuleModificationTracker(module2)
|
||||
val module3ModTracker = KotlinModuleModificationTracker(module3)
|
||||
|
||||
val contentRoot = ModuleRootManager.getInstance(module2).contentRoots.single()
|
||||
val m2 = contentRoot.findChild("m2.kt")!!
|
||||
val m2doc = FileDocumentManager.getInstance().getDocument(m2)!!
|
||||
project.executeWriteCommand("a") {
|
||||
m2doc.insertString(m2doc.textLength , "fun foo() = 1")
|
||||
PsiDocumentManager.getInstance(myProject).commitAllDocuments()
|
||||
}
|
||||
val currentModCount = PsiManager.getInstance(project).modificationTracker.outOfCodeBlockModificationCount
|
||||
|
||||
assertEquals(module1ModCount, KotlinCodeBlockModificationListener.getInstance(myProject).getModificationCount(module1))
|
||||
assertEquals(module1ModCount, module1ModTracker.modificationCount)
|
||||
assertEquals(currentModCount, module2ModTracker.modificationCount)
|
||||
assertEquals(currentModCount, module3ModTracker.modificationCount)
|
||||
|
||||
checkHighlightingInAllFiles { "m2" in file.name }
|
||||
|
||||
assertEquals(0, tracker.sdkResolversComputed.size)
|
||||
assertEquals(1, tracker.moduleResolversComputed.size)
|
||||
|
||||
tracker.moduleResolversComputed.clear()
|
||||
(PsiModificationTracker.SERVICE.getInstance(myProject) as PsiModificationTrackerImpl).incOutOfCodeBlockModificationCounter()
|
||||
checkHighlightingInAllFiles { "m2" in file.name }
|
||||
assertEquals(0, tracker.sdkResolversComputed.size)
|
||||
assertEquals(2, tracker.moduleResolversComputed.size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user