Revise per module out-of-code-block (KT-32364)

This commit is contained in:
Nikolay Krasko
2019-06-03 13:18:09 +03:00
parent 73eea6f4b6
commit f605d4e73b
4 changed files with 116 additions and 57 deletions
@@ -31,11 +31,11 @@ import org.jetbrains.kotlin.config.SourceKotlinRootType
import org.jetbrains.kotlin.config.TestSourceKotlinRootType
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.util.enlargedSearchScope
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModificationTracker
import org.jetbrains.kotlin.idea.configuration.BuildSystemType
import org.jetbrains.kotlin.idea.configuration.getBuildSystemType
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
import org.jetbrains.kotlin.idea.framework.getLibraryPlatform
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleModificationTracker
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.findAnalyzerServices
import org.jetbrains.kotlin.idea.project.getStableName
@@ -44,15 +44,15 @@ import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
import org.jetbrains.kotlin.idea.util.rootManager
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.DefaultIdeTargetPlatformKindProvider
import org.jetbrains.kotlin.platform.idePlatformKind
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.compat.toOldPlatform
import org.jetbrains.kotlin.platform.idePlatformKind
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
@@ -167,7 +167,7 @@ interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo {
get() = platform.findAnalyzerServices
override fun createModificationTracker(): ModificationTracker =
KotlinModuleModificationTracker(module)
KotlinModuleOutOfCodeBlockModificationTracker(module)
}
sealed class ModuleSourceInfoWithExpectedBy(private val forProduction: Boolean) : ModuleSourceInfo {
@@ -5,8 +5,6 @@
package org.jetbrains.kotlin.idea.caches.trackers
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.ModificationTracker
@@ -44,15 +42,7 @@ class KotlinCodeBlockModificationListener(
project: Project,
private val treeAspect: TreeAspect
): PsiTreeChangePreprocessor {
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
@Volatile
private var kotlinModificationTracker: Long = 0
@@ -65,11 +55,7 @@ class KotlinCodeBlockModificationListener(
val kotlinOutOfCodeBlockTracker: ModificationTracker = kotlinOutOfCodeBlockTrackerImpl
fun getModificationCount(module: Module): Long {
return perModuleModCount[module] ?: perModuleChangesHighWatermark ?: kotlinOutOfCodeBlockTracker.modificationCount
}
fun hasPerModuleModificationCounts() = perModuleChangesHighWatermark != null
internal val perModuleOutOfCodeBlockTrackerUpdater = KotlinModuleOutOfCodeBlockModificationTracker.Updater(project)
init {
val model = PomManager.getModel(project)
@@ -94,10 +80,7 @@ class KotlinCodeBlockModificationListener(
if (ktFile.isPhysical && !isReplLine(ktFile.virtualFile)) {
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
lastAffectedModule = ModuleUtil.findModuleForPsiElement(ktFile)
lastAffectedModuleModCount = kotlinOutOfCodeBlockTracker.modificationCount
// TODO: Revise perModuleModCount
// modificationTrackerImpl.incCounter()
perModuleOutOfCodeBlockTrackerUpdater.onKotlinPhysicalFileOutOfBlockChange(ktFile)
}
incOutOfBlockModificationCount(ktFile)
@@ -115,17 +98,7 @@ class KotlinCodeBlockModificationListener(
kotlinModificationTracker = kotlinTrackerInternalIDECount
}
val newModCount = kotlinOutOfCodeBlockTracker.modificationCount
val affectedModule = lastAffectedModule
if (affectedModule != null && newModCount == lastAffectedModuleModCount + 1) {
if (perModuleChangesHighWatermark == null) {
perModuleChangesHighWatermark = lastAffectedModuleModCount
}
perModuleModCount[affectedModule] = newModCount
} else {
perModuleChangesHighWatermark = null
perModuleModCount.clear()
}
perModuleOutOfCodeBlockTrackerUpdater.onPsiModificationTrackerUpdate()
})
}
@@ -6,23 +6,34 @@
package org.jetbrains.kotlin.idea.caches.trackers
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.roots.ProjectRootModificationTracker
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.util.CachedValueProvider
import com.intellij.util.CommonProcessors
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.caches.project.cached
import org.jetbrains.kotlin.psi.KtFile
class KotlinModuleModificationTracker(val module: Module) : ModificationTracker {
private val kotlinModCountListener =
KotlinCodeBlockModificationListener.getInstance(module.project)
class KotlinModuleOutOfCodeBlockModificationTracker private constructor(private val module: Module, private val updater: Updater) :
ModificationTracker {
constructor(module: Module) :
this(module, KotlinCodeBlockModificationListener.getInstance(module.project).perModuleOutOfCodeBlockTrackerUpdater)
private val kotlinOutOfCodeBlockTracker = KotlinCodeBlockModificationListener.getInstance(module.project).kotlinOutOfCodeBlockTracker
private val dependencies by lazy {
// Avoid implicit capturing for this to make CachedValueStabilityChecker happy
val module = module
module.cached(CachedValueProvider {
CachedValueProvider.Result.create(
HashSet<Module>().apply {
HashSet<Module>().also { resultModuleSet ->
ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule(
CommonProcessors.CollectProcessor(this)
CommonProcessors.CollectProcessor(resultModuleSet)
)
},
ProjectRootModificationTracker.getInstance(module.project)
@@ -31,15 +42,15 @@ class KotlinModuleModificationTracker(val module: Module) : ModificationTracker
}
override fun getModificationCount(): Long {
val currentGlobalCount = kotlinModCountListener.kotlinOutOfCodeBlockTracker.modificationCount
val currentGlobalCount = kotlinOutOfCodeBlockTracker.modificationCount
if (kotlinModCountListener.hasPerModuleModificationCounts()) {
val selfCount = kotlinModCountListener.getModificationCount(module)
if (updater.hasPerModuleModificationCounts()) {
val selfCount = updater.getModificationCount(module)
if (selfCount == currentGlobalCount) return selfCount
var maxCount = selfCount
for (dependency in dependencies) {
val depCount = kotlinModCountListener.getModificationCount(dependency)
val depCount = updater.getModificationCount(dependency)
if (depCount == currentGlobalCount) return currentGlobalCount
if (depCount > maxCount) maxCount = depCount
}
@@ -48,4 +59,61 @@ class KotlinModuleModificationTracker(val module: Module) : ModificationTracker
return currentGlobalCount
}
companion object {
@TestOnly
fun getModificationCount(module: Module): Long {
val updater = KotlinCodeBlockModificationListener.getInstance(module.project).perModuleOutOfCodeBlockTrackerUpdater
return updater.getModificationCount(module)
}
}
internal class Updater(project: Project) {
private val kotlinOfOfCodeBlockTracker by lazy {
KotlinCodeBlockModificationListener.getInstance(project).kotlinOutOfCodeBlockTracker
}
private val perModuleModCount = mutableMapOf<Module, Long>()
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
internal fun getModificationCount(module: Module): Long {
return perModuleModCount[module] ?: perModuleChangesHighWatermark ?: kotlinOfOfCodeBlockTracker.modificationCount
}
internal fun hasPerModuleModificationCounts() = perModuleChangesHighWatermark != null
internal fun onKotlinPhysicalFileOutOfBlockChange(ktFile: KtFile) {
lastAffectedModule = ModuleUtil.findModuleForPsiElement(ktFile)
lastAffectedModuleModCount = kotlinOfOfCodeBlockTracker.modificationCount
onPsiModificationTrackerUpdate()
}
internal fun onPsiModificationTrackerUpdate() {
val newModCount = kotlinOfOfCodeBlockTracker.modificationCount
val affectedModule = lastAffectedModule
if (affectedModule != null && newModCount == lastAffectedModuleModCount) {
if (perModuleChangesHighWatermark == null) {
perModuleChangesHighWatermark = lastAffectedModuleModCount
}
perModuleModCount[affectedModule] = newModCount
} else {
// Some updates were not processed in our code so they probably came from other languages. Invalidate all.
clean()
}
}
private fun clean() {
perModuleChangesHighWatermark = null
lastAffectedModule = null
perModuleModCount.clear()
}
}
}
@@ -13,7 +13,6 @@ 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
@@ -25,14 +24,14 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModificationTracker
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
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.caches.trackers.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleModificationTracker
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.allKotlinFiles
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
@@ -43,6 +42,7 @@ import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor.
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.test.TestJdkKind.FULL_JDK
import org.junit.Assert.assertNotEquals
import org.junit.runner.RunWith
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
@@ -134,30 +134,48 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
tracker.sdkResolversComputed.clear()
tracker.moduleResolversComputed.clear()
val module1ModCount = KotlinCodeBlockModificationListener.getInstance(myProject).getModificationCount(module1)
val module1ModTracker = KotlinModuleOutOfCodeBlockModificationTracker(module1)
val module2ModTracker = KotlinModuleOutOfCodeBlockModificationTracker(module2)
val module3ModTracker = KotlinModuleOutOfCodeBlockModificationTracker(module3)
val module1ModTracker = KotlinModuleModificationTracker(module1)
val module2ModTracker = KotlinModuleModificationTracker(module2)
val module3ModTracker = KotlinModuleModificationTracker(module3)
val m2ContentRoot = ModuleRootManager.getInstance(module1).contentRoots.single()
val m1 = m2ContentRoot.findChild("m1.kt")!!
val m1doc = FileDocumentManager.getInstance().getDocument(m1)!!
project.executeWriteCommand("a") {
m1doc.insertString(m1doc.textLength , "fun foo() = 1")
PsiDocumentManager.getInstance(myProject).commitAllDocuments()
}
val contentRoot = ModuleRootManager.getInstance(module2).contentRoots.single()
val m2 = contentRoot.findChild("m2.kt")!!
// Internal counters should be ready after modifications in m1
val afterFirstModification = KotlinModuleOutOfCodeBlockModificationTracker.getModificationCount(module1)
assertEquals(afterFirstModification, module1ModTracker.modificationCount)
assertEquals(afterFirstModification, module2ModTracker.modificationCount)
assertEquals(afterFirstModification, module3ModTracker.modificationCount)
val m1ContentRoot = ModuleRootManager.getInstance(module2).contentRoots.single()
val m2 = m1ContentRoot.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)
val currentModCount = KotlinCodeBlockModificationListener.getInstance(project).kotlinOutOfCodeBlockTracker.modificationCount
// Counter for m1 module should be unaffected by modification in m2
assertEquals(afterFirstModification, KotlinModuleOutOfCodeBlockModificationTracker.getModificationCount(module1))
assertEquals(afterFirstModification, module1ModTracker.modificationCount)
// Counters for m2 and m3 should be changed
assertNotEquals(afterFirstModification, currentModCount)
assertEquals(currentModCount, module2ModTracker.modificationCount)
assertEquals(currentModCount, module3ModTracker.modificationCount)
checkHighlightingInProject { project.allKotlinFiles().filter { "m2" in it.name } }
assertEquals(0, tracker.sdkResolversComputed.size)
assertEquals(1, tracker.moduleResolversComputed.size)
assertEquals(2, tracker.moduleResolversComputed.size)
tracker.moduleResolversComputed.clear()
(PsiModificationTracker.SERVICE.getInstance(myProject) as PsiModificationTrackerImpl).incOutOfCodeBlockModificationCounter()