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.folding.AbstractKotlinFoldingTest
|
||||
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.symbols.AbstractSymbolFromLibraryPointerRestoreTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractSymbolsByFqNameBuildingTest
|
||||
@@ -1037,6 +1038,9 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractFirLazyDeclarationResolveTest> {
|
||||
model("lazyResolve")
|
||||
}
|
||||
testClass<AbstractProjectWideOutOfBlockKotlinModificationTrackerTest> {
|
||||
model("outOfBlockProjectWide")
|
||||
}
|
||||
testClass<AbstractFileStructureTest> {
|
||||
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.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.kotlin.fir.dependenciesWithoutSelf
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
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.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.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.getValue
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@@ -29,7 +21,10 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
internal class FirIdeResolveStateService(project: 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>()
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -68,7 +68,8 @@ private fun KtElement.getFirOfClosestParent(cache: Map<KtElement, FirElement>):
|
||||
return null
|
||||
}
|
||||
|
||||
fun KtElement.getNonLocalContainingOrThisDeclaration(): KtNamedDeclaration? {
|
||||
|
||||
internal inline fun PsiElement.getNonLocalContainingOrThisDeclaration(predicate: (KtDeclaration) -> Boolean = { true }): KtNamedDeclaration? {
|
||||
var container: PsiElement? = this
|
||||
while (container != null && container !is KtFile) {
|
||||
if (container is KtNamedDeclaration
|
||||
@@ -77,10 +78,19 @@ fun KtElement.getNonLocalContainingOrThisDeclaration(): KtNamedDeclaration? {
|
||||
&& !KtPsiUtil.isLocal(container)
|
||||
&& container !is KtEnumEntry
|
||||
&& container.containingClassOrObject !is KtEnumEntry
|
||||
&& predicate(container)
|
||||
) {
|
||||
return container
|
||||
}
|
||||
container = container.parent
|
||||
}
|
||||
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
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.fir.BuiltinTypes
|
||||
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.FirTransformerProvider
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinFirOutOfBlockModificationTrackerFactory
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.util.executeWithoutPCE
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
@@ -102,7 +103,9 @@ private class FromModuleViewSessionCache(
|
||||
private class FirSessionWithModificationTracker(
|
||||
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
|
||||
|
||||
@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"/>
|
||||
<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>
|
||||
|
||||
<!-- scripts -->
|
||||
|
||||
Reference in New Issue
Block a user