Compare commits

...

9 Commits

Author SHA1 Message Date
Nikolay Krasko
2569bfe67c -- another temp 2019-05-30 18:38:08 +03:00
Nikolay Krasko
dcd20a6fac -- modification tracker 2019-05-30 16:38:29 +03:00
Nikolay Krasko
9faea77ec4 -- some 2019-05-30 16:38:29 +03:00
Nikolay Krasko
6749d408e7 Minor: reading changes 2019-05-30 16:38:29 +03:00
Nikolay Krasko
b4f6dbe6d2 -- minor 2019-05-30 16:38:29 +03:00
Nikolay Krasko
f35bed9791 Extract KotlinModuleModificationTracker to separate file 2019-05-30 16:38:29 +03:00
Nikolay Krasko
edb220db51 Move trackers to caches.trackers package 2019-05-30 16:38:28 +03:00
Nikolay Krasko
0c7c725064 Minor: cleanup and reformat KotlinCodeBlockModificationListener.kt 2019-05-30 16:37:16 +03:00
Nikolay Krasko
e8c79eff0e -- temp 2019-05-30 16:37:16 +03:00
11 changed files with 168 additions and 135 deletions

View File

@@ -35,7 +35,7 @@ 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.project.KotlinModuleModificationTracker
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

View File

@@ -47,7 +47,7 @@ import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesModificationTracker
import org.jetbrains.kotlin.idea.core.script.dependencies.ScriptAdditionalIdeaDependenciesProvider
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.outOfBlockModificationCount
import org.jetbrains.kotlin.idea.caches.trackers.outOfBlockModificationCount
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.platform.DefaultIdeTargetPlatformKindProvider

View File

@@ -1,28 +1,15 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.project
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.Key
import com.intellij.openapi.util.ModificationTracker
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.pom.PomManager
import com.intellij.pom.PomModelAspect
@@ -30,12 +17,18 @@ import com.intellij.pom.event.PomModelEvent
import com.intellij.pom.event.PomModelListener
import com.intellij.pom.tree.TreeAspect
import com.intellij.pom.tree.events.TreeChangeEvent
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiTreeChangeEvent
import com.intellij.psi.impl.PsiManagerImpl
import com.intellij.psi.impl.PsiModificationTrackerImpl
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.impl.PsiTreeChangeEventImpl
import com.intellij.psi.impl.PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED
import com.intellij.psi.impl.PsiTreeChangeEventImpl.PsiEventType.PROPERTY_CHANGED
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.CommonProcessors
import org.jetbrains.kotlin.idea.caches.project.cached
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getTopmostParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
@@ -46,70 +39,127 @@ val KOTLIN_CONSOLE_KEY = Key.create<Boolean>("kotlin.console")
* Tested in OutOfBlockModificationTestGenerated
*/
class KotlinCodeBlockModificationListener(
modificationTracker: PsiModificationTracker,
project: Project,
private val treeAspect: TreeAspect
) {
private val modificationTracker: PsiModificationTracker,
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
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
private var perModuleChangesHighWatermark: Long? = null
fun getModificationCount(module: Module): Long {
return perModuleModCount[module] ?:
perModuleChangesHighwatermark ?:
modificationTrackerImpl.outOfCodeBlockModificationCount
@Volatile
private var kotlinModificationTracker: Long = -1
@Volatile
private var hasOOCBChanges: Boolean = false
internal val kotlinOOCBTracker = object : SimpleModificationTracker() {
override fun incModificationCount() {
super.incModificationCount()
}
}
fun hasPerModuleModificationCounts() = perModuleChangesHighwatermark != null
fun getModificationCount(module: Module): Long {
return perModuleModCount[module] ?: perModuleChangesHighWatermark ?: modificationTracker.outOfCodeBlockModificationCount
}
fun hasPerModuleModificationCounts() = perModuleChangesHighWatermark != null
init {
val model = PomManager.getModel(project)
val messageBusConnection = project.messageBus.connect()
model.addModelListener(object: PomModelListener {
(PsiManager.getInstance(project) as PsiManagerImpl).addTreeChangePreprocessor(this)
model.addModelListener(object : PomModelListener {
override fun isAspectChangeInteresting(aspect: PomModelAspect): Boolean {
return aspect == treeAspect
}
override fun modelChanged(event: PomModelEvent) {
val changeSet = event.getChangeSet(treeAspect) as TreeChangeEvent? ?: return
val file = changeSet.rootElement.psi.containingFile as? KtFile ?: return
val file = changeSet.rootElement.psi.containingFile ?: return
val ktFile = file as? KtFile
if (ktFile == null) {
if (file.isPhysical) {
// Change in other language
println("NO!")
}
return
}
val changedElements = changeSet.changedElements
// When a code fragment is reparsed, IntelliJ doesn't do an AST diff and considers the entire
// When a code fragment is reparsed, Intellij doesn't do an AST diff and considers the entire
// 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 } || changedElements.isEmpty()) {
messageBusConnection.deliverImmediately()
if (file.isPhysical && !isReplLine(file.virtualFile)) {
lastAffectedModule = ModuleUtil.findModuleForPsiElement(file)
lastAffectedModuleModCount = modificationTrackerImpl.outOfCodeBlockModificationCount
modificationTrackerImpl.incCounter()
if (ktFile.isPhysical && !isReplLine(ktFile.virtualFile)) {
hasOOCBChanges = true
lastAffectedModule = ModuleUtil.findModuleForPsiElement(ktFile)
lastAffectedModuleModCount = modificationTracker.outOfCodeBlockModificationCount
// modificationTrackerImpl.incCounter()
}
incOutOfBlockModificationCount(file)
incOutOfBlockModificationCount(ktFile)
}
}
})
messageBusConnection.subscribe(PsiModificationTracker.TOPIC, PsiModificationTracker.Listener {
val newModCount = modificationTrackerImpl.outOfCodeBlockModificationCount
@Suppress("UnstableApiUsage") val kotlinTrackerInternalCount =
modificationTrackerImpl.forLanguage(KotlinLanguage.INSTANCE).modificationCount
if (kotlinModificationTracker == kotlinTrackerInternalCount) {
// Some update that we are not sure is from Kotlin language
kotlinOOCBTracker.incModificationCount()
} else {
if (hasOOCBChanges) {
kotlinOOCBTracker.incModificationCount()
}
hasOOCBChanges = false
}
kotlinModificationTracker = kotlinTrackerInternalCount
val newModCount = kotlinOOCBTracker.modificationCount
val affectedModule = lastAffectedModule
if (affectedModule != null && newModCount == lastAffectedModuleModCount + 1) {
if (perModuleChangesHighwatermark == null) {
perModuleChangesHighwatermark = lastAffectedModuleModCount
if (perModuleChangesHighWatermark == null) {
perModuleChangesHighWatermark = lastAffectedModuleModCount
}
perModuleModCount[affectedModule] = newModCount
}
else {
perModuleChangesHighwatermark = null
} else {
perModuleChangesHighWatermark = null
perModuleModCount.clear()
}
})
}
override fun treeChanged(event: PsiTreeChangeEventImpl) {
if (!PsiModificationTrackerImpl.canAffectPsi(event)) {
return
}
val code = event.code
val outOfCodeBlock = when (code) {
PROPERTY_CHANGED ->
event.propertyName === PsiTreeChangeEvent.PROP_UNLOADED_PSI || event.propertyName === PsiTreeChangeEvent.PROP_ROOTS
CHILD_MOVED -> event.oldParent is PsiDirectory || event.newParent is PsiDirectory
else -> event.parent is PsiDirectory
}
if (outOfCodeBlock) {
kotlinOOCBTracker.incModificationCount()
}
}
companion object {
private fun isReplLine(file: VirtualFile): Boolean {
return file.getUserData(KOTLIN_CONSOLE_KEY) == true
@@ -123,8 +173,9 @@ class KotlinCodeBlockModificationListener(
fun getInsideCodeBlockModificationScope(element: PsiElement): KtElement? {
val lambda = element.getTopmostParentOfType<KtLambdaExpression>()
if (lambda is KtLambdaExpression) {
lambda.getTopmostParentOfType<KtSuperTypeCallEntry>()
?.let { return it }
lambda.getTopmostParentOfType<KtSuperTypeCallEntry>()?.let {
return it
}
}
val blockDeclaration = KtPsiUtil.getTopmostParentOfTypes(element, *BLOCK_DECLARATION_TYPES) as? KtDeclaration ?: return null
@@ -134,8 +185,7 @@ class KotlinCodeBlockModificationListener(
is KtNamedFunction -> {
if (blockDeclaration.hasBlockBody()) {
return blockDeclaration.bodyExpression?.takeIf { it.isAncestor(element) }
}
else if (blockDeclaration.hasDeclaredReturnType()) {
} else if (blockDeclaration.hasDeclaredReturnType()) {
return blockDeclaration.initializer?.takeIf { it.isAncestor(element) }
}
}
@@ -144,8 +194,8 @@ class KotlinCodeBlockModificationListener(
if (blockDeclaration.typeReference != null) {
for (accessor in blockDeclaration.accessors) {
(accessor.initializer ?: accessor.bodyExpression)
?.takeIf { it.isAncestor(element) }
?.let { return it }
?.takeIf { it.isAncestor(element) }
?.let { return it }
}
}
}
@@ -169,12 +219,13 @@ class KotlinCodeBlockModificationListener(
}
private val BLOCK_DECLARATION_TYPES = arrayOf<Class<out KtDeclaration>>(
KtProperty::class.java,
KtNamedFunction::class.java,
KtScriptInitializer::class.java
KtProperty::class.java,
KtNamedFunction::class.java,
KtScriptInitializer::class.java
)
fun getInstance(project: Project) = project.getComponent(KotlinCodeBlockModificationListener::class.java)
fun getInstance(project: Project): KotlinCodeBlockModificationListener =
project.getComponent(KotlinCodeBlockModificationListener::class.java)
}
}
@@ -183,35 +234,3 @@ 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 {
module.cached(CachedValueProvider {
CachedValueProvider.Result.create(
HashSet<Module>().apply {
ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule(
CommonProcessors.CollectProcessor(this))
},
ProjectRootModificationTracker.getInstance(module.project)
)
})
}
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
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.caches.trackers
import com.intellij.openapi.module.Module
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.psi.util.PsiModificationTracker
import com.intellij.util.CommonProcessors
import org.jetbrains.kotlin.idea.caches.project.cached
class KotlinModuleModificationTracker(val module: Module) : ModificationTracker {
private val kotlinModCountListener =
KotlinCodeBlockModificationListener.getInstance(module.project)
private val dependencies by lazy {
module.cached(CachedValueProvider {
CachedValueProvider.Result.create(
HashSet<Module>().apply {
ModuleRootManager.getInstance(module).orderEntries().recursively().forEachModule(
CommonProcessors.CollectProcessor(this)
)
},
ProjectRootModificationTracker.getInstance(module.project)
)
})
}
override fun getModificationCount(): Long {
val currentGlobalCount = kotlinModCountListener.kotlinOOCBTracker.modificationCount
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
}
}

View File

@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.frontend.di.createContainerForBodyResolve
import org.jetbrains.kotlin.idea.caches.resolve.CodeFragmentAnalyzer
import org.jetbrains.kotlin.idea.caches.resolve.util.analyzeControlFlow
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType

View File

@@ -29,7 +29,7 @@ import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.psi.KtBlockExpression

View File

@@ -63,7 +63,7 @@ import org.jetbrains.kotlin.idea.caches.project.testSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.project.KOTLIN_CONSOLE_KEY
import org.jetbrains.kotlin.idea.caches.trackers.KOTLIN_CONSOLE_KEY
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parsing.KotlinParserDefinition

View File

@@ -17,7 +17,7 @@
<implementation-class>org.jetbrains.kotlin.idea.completion.LookupCancelWatcher</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener</implementation-class>
<implementation-class>org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.caches.KotlinPackageContentModificationListener</implementation-class>

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("DEPRECATION")
package org.jetbrains.kotlin.idea
import com.intellij.featureStatistics.FeatureStatisticsBundleProvider
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.Extensions
import java.lang.IllegalStateException
private const val CIDR_FEATURE_STATISTICS_PROVIDER_FQNAME = "com.jetbrains.cidr.lang.OCFeatureStatisticsBundleProvider"
// Remove the function, when there's no dependency to cidr during running Kotlin tests.
fun registerAdditionalResourceBundleInTests() {
if (!ApplicationManager.getApplication().isUnitTestMode) {
return
}
val isAlreadyRegistered = FeatureStatisticsBundleProvider.EP_NAME.extensions.any { provider ->
provider.javaClass.name == CIDR_FEATURE_STATISTICS_PROVIDER_FQNAME
}
if (isAlreadyRegistered) {
throw IllegalStateException("Remove this registration for the current platform: bundle is already registered.")
}
val cidrFSBundleProviderClass = try {
Class.forName(CIDR_FEATURE_STATISTICS_PROVIDER_FQNAME)
} catch (_: ClassNotFoundException) {
return
}
val cidrFSBundleProvider = cidrFSBundleProviderClass.newInstance() as FeatureStatisticsBundleProvider
Extensions.getRootArea().getExtensionPoint(FeatureStatisticsBundleProvider.EP_NAME).registerExtension(cidrFSBundleProvider)
}

View File

@@ -31,8 +31,8 @@ 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.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

View File

@@ -14,7 +14,7 @@ import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.PsiModificationTrackerImpl;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListenerKt;
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListenerKt;
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade;
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;