FIR IDE: introduce FIR IDE specific out of block modification tracker
This commit is contained in:
@@ -86,6 +86,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleLazyRes
|
|||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureTest
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureTest
|
||||||
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
|
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
|
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.AbstractProjectWideOutOfBlockKotlinModificationTrackerTest
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.AbstractMemberScopeByFqNameTest
|
import org.jetbrains.kotlin.idea.frontend.api.scopes.AbstractMemberScopeByFqNameTest
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractSymbolFromLibraryPointerRestoreTest
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractSymbolFromLibraryPointerRestoreTest
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractSymbolsByFqNameBuildingTest
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractSymbolsByFqNameBuildingTest
|
||||||
@@ -1037,6 +1038,9 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractFirLazyDeclarationResolveTest> {
|
testClass<AbstractFirLazyDeclarationResolveTest> {
|
||||||
model("lazyResolve")
|
model("lazyResolve")
|
||||||
}
|
}
|
||||||
|
testClass<AbstractProjectWideOutOfBlockKotlinModificationTrackerTest> {
|
||||||
|
model("outOfBlockProjectWide")
|
||||||
|
}
|
||||||
testClass<AbstractFileStructureTest> {
|
testClass<AbstractFileStructureTest> {
|
||||||
model("fileStructure")
|
model("fileStructure")
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-10
@@ -7,21 +7,13 @@ package org.jetbrains.kotlin.idea.fir.low.level.api
|
|||||||
|
|
||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.ModificationTracker
|
|
||||||
import com.intellij.psi.util.CachedValue
|
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.fir.dependenciesWithoutSelf
|
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionFactory
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionProvider
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionProviderStorage
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionProviderStorage
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSourcesSession
|
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinFirOutOfBlockModificationTrackerFactory
|
||||||
import org.jetbrains.kotlin.idea.util.cachedValue
|
import org.jetbrains.kotlin.idea.util.cachedValue
|
||||||
import org.jetbrains.kotlin.idea.util.getValue
|
import org.jetbrains.kotlin.idea.util.getValue
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@@ -29,7 +21,10 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
internal class FirIdeResolveStateService(project: Project) {
|
internal class FirIdeResolveStateService(project: Project) {
|
||||||
private val sessionProviderStorage = FirIdeSessionProviderStorage(project)
|
private val sessionProviderStorage = FirIdeSessionProviderStorage(project)
|
||||||
|
|
||||||
private val stateCache by cachedValue(project, KotlinCodeBlockModificationListener.getInstance(project).kotlinOutOfCodeBlockTracker) {
|
private val stateCache by cachedValue(
|
||||||
|
project,
|
||||||
|
project.service<KotlinFirOutOfBlockModificationTrackerFactory>().createProjectWideOutOfBlockModificationTracker(),
|
||||||
|
) {
|
||||||
ConcurrentHashMap<IdeaModuleInfo, FirModuleResolveStateImpl>()
|
ConcurrentHashMap<IdeaModuleInfo, FirModuleResolveStateImpl>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -68,7 +68,8 @@ private fun KtElement.getFirOfClosestParent(cache: Map<KtElement, FirElement>):
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtElement.getNonLocalContainingOrThisDeclaration(): KtNamedDeclaration? {
|
|
||||||
|
internal inline fun PsiElement.getNonLocalContainingOrThisDeclaration(predicate: (KtDeclaration) -> Boolean = { true }): KtNamedDeclaration? {
|
||||||
var container: PsiElement? = this
|
var container: PsiElement? = this
|
||||||
while (container != null && container !is KtFile) {
|
while (container != null && container !is KtFile) {
|
||||||
if (container is KtNamedDeclaration
|
if (container is KtNamedDeclaration
|
||||||
@@ -77,10 +78,19 @@ fun KtElement.getNonLocalContainingOrThisDeclaration(): KtNamedDeclaration? {
|
|||||||
&& !KtPsiUtil.isLocal(container)
|
&& !KtPsiUtil.isLocal(container)
|
||||||
&& container !is KtEnumEntry
|
&& container !is KtEnumEntry
|
||||||
&& container.containingClassOrObject !is KtEnumEntry
|
&& container.containingClassOrObject !is KtEnumEntry
|
||||||
|
&& predicate(container)
|
||||||
) {
|
) {
|
||||||
return container
|
return container
|
||||||
}
|
}
|
||||||
container = container.parent
|
container = container.parent
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun PsiElement.getNonLocalContainingInBodyDeclarationWith(): KtNamedDeclaration? =
|
||||||
|
getNonLocalContainingOrThisDeclaration { declaration ->
|
||||||
|
when (declaration) {
|
||||||
|
is KtNamedFunction -> declaration.bodyExpression?.isAncestor(this) == true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+5
-2
@@ -5,12 +5,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api.sessions
|
package org.jetbrains.kotlin.idea.fir.low.level.api.sessions
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.fir.BuiltinTypes
|
import org.jetbrains.kotlin.fir.BuiltinTypes
|
||||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModificationTracker
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.FirPhaseRunner
|
import org.jetbrains.kotlin.idea.fir.low.level.api.FirPhaseRunner
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.FirTransformerProvider
|
import org.jetbrains.kotlin.idea.fir.low.level.api.FirTransformerProvider
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinFirOutOfBlockModificationTrackerFactory
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.util.executeWithoutPCE
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.executeWithoutPCE
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
@@ -102,7 +103,9 @@ private class FromModuleViewSessionCache(
|
|||||||
private class FirSessionWithModificationTracker(
|
private class FirSessionWithModificationTracker(
|
||||||
val firSession: FirIdeSourcesSession,
|
val firSession: FirIdeSourcesSession,
|
||||||
) {
|
) {
|
||||||
private val modificationTracker = KotlinModuleOutOfCodeBlockModificationTracker(firSession.moduleInfo.module)
|
private val modificationTracker = firSession.project.service<KotlinFirOutOfBlockModificationTrackerFactory>()
|
||||||
|
.createModuleOutOfBlockModificationTracker(firSession.moduleInfo.module)
|
||||||
|
|
||||||
private val timeStamp = modificationTracker.modificationCount
|
private val timeStamp = modificationTracker.modificationCount
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
|
|||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.fir.low.level.api.trackers
|
||||||
|
|
||||||
|
import com.intellij.ProjectTopics
|
||||||
|
import com.intellij.lang.ASTNode
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.ModuleRootEvent
|
||||||
|
import com.intellij.openapi.roots.ModuleRootListener
|
||||||
|
import com.intellij.pom.PomManager
|
||||||
|
import com.intellij.pom.PomModelAspect
|
||||||
|
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 org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingInBodyDeclarationWith
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingOrThisDeclaration
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileElementFactory
|
||||||
|
import org.jetbrains.kotlin.idea.util.module
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
internal class KotlinFirModificationTrackerService(project: Project) : Disposable {
|
||||||
|
init {
|
||||||
|
val model = PomManager.getModel(project)
|
||||||
|
model.addModelListener(Listener())
|
||||||
|
|
||||||
|
val connection = project.messageBus.connect(this)
|
||||||
|
connection.subscribe(ProjectTopics.PROJECT_ROOTS, object : ModuleRootListener {
|
||||||
|
override fun rootsChanged(event: ModuleRootEvent) {
|
||||||
|
projectGlobalOutOfBlockInKotlinFilesModificationCount++
|
||||||
|
|
||||||
|
// todo increase modificationCountForModule
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var projectGlobalOutOfBlockInKotlinFilesModificationCount = 0L
|
||||||
|
private set
|
||||||
|
|
||||||
|
internal fun getOutOfBlockModificationCountForModules(module: Module): Long =
|
||||||
|
modificationCountForModule[module] ?: 0L
|
||||||
|
|
||||||
|
private val modificationCountForModule = WeakHashMap<Module, Long>()
|
||||||
|
private val treeAspect = TreeAspect.getInstance(project)
|
||||||
|
|
||||||
|
override fun dispose() {}
|
||||||
|
|
||||||
|
private inner class Listener : PomModelListener {
|
||||||
|
override fun modelChanged(event: PomModelEvent) {
|
||||||
|
val changeSet = event.getChangeSet(treeAspect) as TreeChangeEvent? ?: return
|
||||||
|
if (changeSet.rootElement.psi.language != KotlinLanguage.INSTANCE) return
|
||||||
|
val changedElements = changeSet.changedElements
|
||||||
|
|
||||||
|
var isOutOfBlockChangeInAnyModule = false
|
||||||
|
|
||||||
|
changedElements.forEach { element ->
|
||||||
|
val isOutOfBlock = element.isOutOfBlockChange(changeSet)
|
||||||
|
isOutOfBlockChangeInAnyModule = isOutOfBlockChangeInAnyModule || isOutOfBlock
|
||||||
|
if (isOutOfBlock) {
|
||||||
|
element.psi.module?.let { module ->
|
||||||
|
modificationCountForModule.compute(module) { _, value -> (value ?: 0) + 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOutOfBlockChangeInAnyModule) {
|
||||||
|
projectGlobalOutOfBlockInKotlinFilesModificationCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ASTNode.isOutOfBlockChange(changeSet: TreeChangeEvent): Boolean {
|
||||||
|
val nodes = changeSet.getChangesByElement(this).affectedChildren
|
||||||
|
return nodes.any { node ->
|
||||||
|
val psi = node.psi ?: return@any true
|
||||||
|
val container = psi.getNonLocalContainingInBodyDeclarationWith() ?: return@any true
|
||||||
|
!FileElementFactory.isReanalyzableContainer(container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isAspectChangeInteresting(aspect: PomModelAspect): Boolean =
|
||||||
|
treeAspect == aspect
|
||||||
|
}
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.fir.low.level.api.trackers
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.ModificationTracker
|
||||||
|
|
||||||
|
class KotlinFirOutOfBlockModificationTrackerFactory(private val project: Project) {
|
||||||
|
fun createProjectWideOutOfBlockModificationTracker(): ModificationTracker =
|
||||||
|
KotlinFirOutOfBlockModificationTracker(project)
|
||||||
|
|
||||||
|
fun createModuleOutOfBlockModificationTracker(module: Module): ModificationTracker =
|
||||||
|
KotlinFirOutOfBlockModuleModificationTracker(module)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KotlinFirOutOfBlockModificationTracker(project: Project) : ModificationTracker {
|
||||||
|
private val trackerService = project.service<KotlinFirModificationTrackerService>()
|
||||||
|
|
||||||
|
override fun getModificationCount(): Long =
|
||||||
|
trackerService.projectGlobalOutOfBlockInKotlinFilesModificationCount
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KotlinFirOutOfBlockModuleModificationTracker(private val module: Module) : ModificationTracker {
|
||||||
|
private val trackerService = module.project.service<KotlinFirModificationTrackerService>()
|
||||||
|
|
||||||
|
override fun getModificationCount(): Long =
|
||||||
|
trackerService.getOutOfBlockModificationCountForModules(module)
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun x() {
|
||||||
|
fun a() = <caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: false
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo(): Int = <caret>42
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: false
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo() = <caret>42
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun foo(): Int {
|
||||||
|
<caret>println("")
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: false
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo() {
|
||||||
|
<caret>println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: false
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
@Ann<caret>
|
||||||
|
fun foo() {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
@Ann(<caret>)
|
||||||
|
fun foo() {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<caret> fun foo() {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(<caret>) {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionParams.kt
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun fo<caret>o() {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(x: In<caret>) {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(): In<caret> {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun <X<caret>>foo() {
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUT_OF_BLOCK: true
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.fir.low.level.api.file.structure
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.util.parentOfType
|
||||||
|
import junit.framework.Assert
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade
|
||||||
|
import org.jetbrains.kotlin.idea.search.getKotlinFqName
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractReanalyzableFileStructureElementCreationTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
override fun isFirPlugin(): Boolean = true
|
||||||
|
|
||||||
|
fun doTest(path: String) {
|
||||||
|
val testDataFile = File(path)
|
||||||
|
val initialFileText = FileUtil.loadFile(testDataFile)
|
||||||
|
val ktFile = myFixture.configureByText(testDataFile.name, initialFileText) as KtFile
|
||||||
|
val expectedFqName =
|
||||||
|
InTextDirectivesUtils.findStringWithPrefixes(initialFileText, STRUCTURE_ELEMENT_FQ_NAME_DIRECTIVE)
|
||||||
|
?: error("Please specify // STRUCTURE_ELEMENT directive")
|
||||||
|
val shouldStructureElementBeRecreated =
|
||||||
|
InTextDirectivesUtils.getPrefixedBoolean(initialFileText, SHOULD_ELEMENT_BE_RECREATED)
|
||||||
|
?: error("Please specify // SHOULD_ELEMENT_BE_RECREATED directive")
|
||||||
|
|
||||||
|
val elementAtCaret = ktFile.findElementAtCaret()
|
||||||
|
val (initialStructureElement, initialFileStructure, initialModuleResolveState) = getStructureElementForKtElement(elementAtCaret)
|
||||||
|
Assert.assertEquals(expectedFqName, initialStructureElement.psi.getKotlinFqName()?.asString())
|
||||||
|
|
||||||
|
myFixture.type("hello")
|
||||||
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
|
||||||
|
val newElementAtCaret = ktFile.findElementAtCaret()
|
||||||
|
val (newStructureElement, newFileStructure, newModuleResolveState) = getStructureElementForKtElement(newElementAtCaret)
|
||||||
|
Assert.assertEquals(
|
||||||
|
"Structure elements should be build by the same KtDeclaration's",
|
||||||
|
expectedFqName,
|
||||||
|
newStructureElement.psi.getKotlinFqName()?.asString()
|
||||||
|
)
|
||||||
|
Assert.assertTrue("Structure elements should be different after typing", newStructureElement !== initialStructureElement)
|
||||||
|
Assert.assertEquals(
|
||||||
|
"FirModuleResolveState should change only of out of block modification",
|
||||||
|
shouldStructureElementBeRecreated,
|
||||||
|
newModuleResolveState !== initialModuleResolveState
|
||||||
|
)
|
||||||
|
Assert.assertEquals(
|
||||||
|
"FileStructure state should change only of out of block modification",
|
||||||
|
shouldStructureElementBeRecreated,
|
||||||
|
initialFileStructure !== newFileStructure
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtFile.findElementAtCaret(): KtElement {
|
||||||
|
val elementAtCaret = findElementAt(myFixture.caretOffset)!!.parentOfType<KtElement>()!!
|
||||||
|
if (elementAtCaret is KtDeclaration) {
|
||||||
|
error("Expected element inside declaration but was\n${elementAtCaret.getElementTextInContext()}")
|
||||||
|
}
|
||||||
|
return elementAtCaret
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStructureElementForKtElement(element: KtElement): Triple<FileStructureElement, FileStructure, FirModuleResolveState> {
|
||||||
|
val moduleResolveState = LowLevelFirApiFacade.getResolveStateFor(element) as FirModuleResolveStateImpl
|
||||||
|
val fileStructure =
|
||||||
|
moduleResolveState.fileStructureCache.getFileStructure(element.containingKtFile, moduleResolveState.rootModuleSession.cache)
|
||||||
|
val fileStructureElement = fileStructure.getStructureElementFor(element)
|
||||||
|
return Triple(fileStructureElement, fileStructure, moduleResolveState)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val STRUCTURE_ELEMENT_FQ_NAME_DIRECTIVE = "// STRUCTURE_ELEMENT:"
|
||||||
|
private const val SHOULD_ELEMENT_BE_RECREATED = "// SHOULD_ELEMENT_BE_RECREATED:"
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.fir.low.level.api.file.structure;
|
||||||
|
|
||||||
|
import com.intellij.testFramework.TestDataPath;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.TestMetadata;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
@TestMetadata("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class ReanalyzableFileStructureElementCreationTestGenerated extends AbstractReanalyzableFileStructureElementCreationTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInElementBuilder() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelExpressionBodyFunWithType.kt")
|
||||||
|
public void testTopLevelExpressionBodyFunWithType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder/topLevelExpressionBodyFunWithType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelExpressionBodyFunWithoutType.kt")
|
||||||
|
public void testTopLevelExpressionBodyFunWithoutType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder/topLevelExpressionBodyFunWithoutType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelFunWithType.kt")
|
||||||
|
public void testTopLevelFunWithType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder/topLevelFunWithType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelUnitFun.kt")
|
||||||
|
public void testTopLevelUnitFun() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/elementBuilder/topLevelUnitFun.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.fir.low.level.api.trackers
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import junit.framework.Assert
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractProjectWideOutOfBlockKotlinModificationTrackerTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
override fun isFirPlugin(): Boolean = true
|
||||||
|
|
||||||
|
fun doTest(path: String) {
|
||||||
|
val testDataFile = File(path)
|
||||||
|
val fileText = FileUtil.loadFile(testDataFile)
|
||||||
|
myFixture.configureByText(testDataFile.name, fileText)
|
||||||
|
val textToType = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// TYPE:") ?: DEFAULT_TEXT_TO_TYPE
|
||||||
|
val outOfBlock = InTextDirectivesUtils.getPrefixedBoolean(fileText, "// OUT_OF_BLOCK:")
|
||||||
|
?: error("Please, specify should out of block change happen or not by `// OUT_OF_BLOCK:` directive")
|
||||||
|
val tracker = project.service<KotlinFirOutOfBlockModificationTrackerFactory>().createProjectWideOutOfBlockModificationTracker()
|
||||||
|
val initialModificationCount = tracker.modificationCount
|
||||||
|
myFixture.type(textToType)
|
||||||
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
val afterTypingModificationCount = tracker.modificationCount
|
||||||
|
Assert.assertEquals(outOfBlock, initialModificationCount != afterTypingModificationCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val DEFAULT_TEXT_TO_TYPE = "hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.fir.low.level.api.trackers;
|
||||||
|
|
||||||
|
import com.intellij.testFramework.TestDataPath;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.TestMetadata;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
@TestMetadata("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class ProjectWideOutOfBlockKotlinModificationTrackerTestGenerated extends AbstractProjectWideOutOfBlockKotlinModificationTrackerTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInOutOfBlockProjectWide() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFun.kt")
|
||||||
|
public void testLocalFun() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/localFun.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelExpressionBodyFunWithType.kt")
|
||||||
|
public void testTopLevelExpressionBodyFunWithType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/topLevelExpressionBodyFunWithType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelExpressionBodyFunWithoutType.kt")
|
||||||
|
public void testTopLevelExpressionBodyFunWithoutType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/topLevelExpressionBodyFunWithoutType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelFunWithType.kt")
|
||||||
|
public void testTopLevelFunWithType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/topLevelFunWithType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelUnitFun.kt")
|
||||||
|
public void testTopLevelUnitFun() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/topLevelUnitFun.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeInFunctionAnnotation.kt")
|
||||||
|
public void testTypeInFunctionAnnotation() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeInFunctionAnnotationParameter.kt")
|
||||||
|
public void testTypeInFunctionAnnotationParameter() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionAnnotationParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeInFunctionModifiers.kt")
|
||||||
|
public void testTypeInFunctionModifiers() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionModifiers.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeInFunctionName.kt")
|
||||||
|
public void testTypeInFunctionName() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionName.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeInFunctionParams.kt")
|
||||||
|
public void testTypeInFunctionParams() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionParams.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeInFunctionParamsType.kt")
|
||||||
|
public void testTypeInFunctionParamsType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionParamsType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeInFunctionReturnType.kt")
|
||||||
|
public void testTypeInFunctionReturnType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionReturnType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("typeInFunctionTypeParams.kt")
|
||||||
|
public void testTypeInFunctionTypeParams() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/outOfBlockProjectWide/typeInFunctionTypeParams.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,7 +111,8 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
|
|||||||
implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinHighLevelExpressionTypeProvider"/>
|
implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinHighLevelExpressionTypeProvider"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.fir.highlighter.KotlinDiagnosticHighlightingPassFactory"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.fir.highlighter.KotlinDiagnosticHighlightingPassFactory"/>
|
||||||
|
|
||||||
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinFirModificationTrackerService"/>
|
||||||
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinFirOutOfBlockModificationTrackerFactory"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<!-- scripts -->
|
<!-- scripts -->
|
||||||
|
|||||||
Reference in New Issue
Block a user