[AA] KT-58257 Add API for subscription-directed invalidation

- The commit refactors some modification trackers previously provided by
  `KotlinModificationTrackerFactory` to a subscription-directed
  mechanism implemented via `MessageBus` and `KotlinTopics`. The
  following modification trackers are affected:
  - Module out-of-block modification: The FE10 Analysis API doesn't use
    these modification trackers, so the effect is limited to LL FIR.
  - Module state: Likewise, FE10 doesn't use this modification tracker,
    so again the effect is limited to LL FIR.
- Project and library modifications trackers remain, because many small
  objects in e.g. light classes depend on these trackers (making
  listener management unfeasible), and they are not relevant for
  `LLFirSession` invalidation.
- This new API paves the way for a session invalidation service to
  subscribe to out-of-block and module state changes as events. This
  removes the need to iterate through modification trackers.
  - Also note that the out-of-block modification provided by the new
    subscription mechanism is intended to work for _any_ `KtModule`, as
    long as it makes sense to have out-of-block modifications. For
    example, the subscription should work for script modules. The OOB
    modification tracker previously only supported `KtSourceModule`s,
    which required separate single-file modification trackers for script
    and not-under-content-root modules.
- `MessageBus` is a general utility provided by IntelliJ, but usually it
  is retrieved from `project`. To keep these two concerns decoupled,
  `project.messageBus` should not be used directly. Instead, the commit
  adds a `KotlinMessageBusProvider`, which for now just provides the
  project message bus with its standard implementation, but allows
  swapping out the message bus implementation later.
- Global changes are also supported by the new subscription API. Such
  global changes may for example occur during cache invalidation in
  tests, on global PSI tree changes, or when an SDK is removed.
- Test-only invalidation has been moved from
  `KotlinModificationTrackerFactory` to a new
  `KotlinGlobalModificationService`. This creates one central service
  for invalidation between tests, which is easier from an implementation
  and a usage perspective than calling multiple scattered services and
  providers.
This commit is contained in:
Marco Pennekamp
2023-04-21 19:21:12 +02:00
committed by Space Team
parent bf5f41a0ad
commit 0c94a3131c
21 changed files with 411 additions and 138 deletions
@@ -41,7 +41,7 @@ abstract class AbstractSymbolRestoreFromDifferentModuleTest : AbstractAnalysisAp
val pointer = symbol.createPointer()
Triple(DebugSymbolRenderer().render(symbol), symbol.render(defaultRenderer), pointer)
}
configurator.doOutOfBlockModification(declaration.containingKtFile)
configurator.doGlobalModuleStateModification(project)
val (debugRenderedRestored, prettyRenderedRestored) = analyseForTest(restoreAt) {
val symbol = pointer.restoreSymbol() as? KtDeclarationSymbol
@@ -132,7 +132,7 @@ abstract class AbstractSymbolTest : AbstractAnalysisApiSingleFileTest() {
compareResults(pointersWithRendered, testServices)
configurator.doOutOfBlockModification(ktFile)
configurator.doGlobalModuleStateModification(ktFile.project)
restoreSymbolsInOtherReadActionAndCompareResults(
directiveToIgnore = directiveToIgnoreSymbolRestore,
@@ -39,6 +39,9 @@ object AnalysisApiBaseTestServiceRegistrar: AnalysisApiTestServiceRegistrar() {
override fun registerProjectServices(project: MockProject, testServices: TestServices) {
project.apply {
registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java)
registerService(KotlinMessageBusProvider::class.java, KotlinProjectMessageBusProvider::class.java)
registerService(KotlinGlobalModificationService::class.java, KotlinStaticGlobalModificationService::class.java)
registerService(KtLifetimeTokenProvider::class.java, KtReadActionConfinementLifetimeTokenProvider::class.java)
//KotlinClassFileDecompiler is registered as application service so it's available for the tests run in parallel as well
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2023 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.analysis.providers
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.providers.topics.KotlinTopics
/**
* [KotlinGlobalModificationService] is a central service for the invalidation of caches during/between tests.
*
* All `publish` functions must be called in a write action because the events in [KotlinTopics] guarantee that the listener is called in a
* write action.
*
* Implementations of this service should publish global modification events to at least the following components:
* - [KotlinModificationTrackerFactory]
* - [KotlinTopics] via [analysisMessageBus]
*/
public abstract class KotlinGlobalModificationService {
/**
* Publishes an event of global modification of the module state of all [KtModule]s.
*/
@TestOnly
public abstract fun publishGlobalModuleStateModification()
/**
* Publishes an event of global out-of-block modification of all [KtModule]s, including binary modules. The event does not invalidate
* module state like [publishGlobalModuleStateModification], so some module structure-specific caches might persist.
*/
@TestOnly
public abstract fun publishGlobalOutOfBlockModification()
/**
* Publishes an event of global modification of the module state of all source [KtModule]s.
*/
@TestOnly
public abstract fun publishGlobalSourceModuleStateModification()
/**
* Publishes an event of global out-of-block modification of all source [KtModule]s. The event does not invalidate module state like
* [publishGlobalSourceModuleStateModification], so some module structure-specific caches might persist.
*/
@TestOnly
public abstract fun publishGlobalSourceOutOfBlockModification()
public companion object {
public fun getInstance(project: Project): KotlinGlobalModificationService =
project.getService(KotlinGlobalModificationService::class.java)
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2023 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.analysis.providers
import com.intellij.openapi.project.Project
import com.intellij.util.messages.MessageBus
import org.jetbrains.kotlin.analysis.providers.topics.KotlinTopics
/**
* [KotlinMessageBusProvider] allows Analysis API implementations to provide a custom [MessageBus]. When subscribing to or publishing to
* Analysis API topics ([KotlinTopics]), the message bus provided by [getMessageBus] should be used, not the [Project]'s message bus.
*/
public interface KotlinMessageBusProvider {
public fun getMessageBus(): MessageBus
public companion object {
public fun getInstance(project: Project): KotlinMessageBusProvider =
project.getService(KotlinMessageBusProvider::class.java)
}
}
/**
* The [MessageBus] used to subscribe to and publish to Analysis API topics ([KotlinTopics]).
*/
public val Project.analysisMessageBus: MessageBus
get() = KotlinMessageBusProvider.getInstance(this).getMessageBus()
@@ -7,126 +7,84 @@ package org.jetbrains.kotlin.analysis.providers
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.providers.topics.KotlinTopics
/**
* [KotlinModificationTrackerFactory] creates modification trackers for select modification events.
*
* Further modification tracking is implemented with a subscription-based mechanism via [KotlinTopics]. Modification trackers make the most
* sense when there are many, possibly short-lived objects that need to be notified of a change. In such cases, subscriber management in the
* message bus adds too much overhead.
*/
public abstract class KotlinModificationTrackerFactory {
/**
* Creates **OOBM** tracker which is incremented every time there is **OOB** change in some source project module.
* Creates an out-of-block modification tracker which is incremented every time there is an out-of-block change in some source project
* module.
*
* ### Out-of-block Modification (OOBM)
*
* Out-of-block modification is a source code modification which may change the resolution of other non-local declarations.
*
* #### Example 1
*
* ```
* val x = 10<caret>
* val z = x
* ```
*
* If we change the initializer of `x` to `"str"` the return type of `x` will become `String` instead of the initial `Int`. This will
* change the return type of `z` as it does not have an explicit type. So, it is an **OOBM**.
*
* #### Example 2
*
* ```
* val x: Int = 10<caret>
* val z = x
* ```
*
* If we change `10` to `"str"` as in the first example, it would not change the type of `z`, so it is not an **OOBM**.
*
* #### Examples of source code modifications which result in an **OOBM**
*
* - Modification inside non-local (i.e. accessible outside) declaration without explicit return type specified
* - Modification of a package
* - Creation of a new declaration
* - Moving a declaration to another package
*
* Generally, all modifications which happen outside the body of a callable declaration (functions, accessors, or properties) with an
* explicit type are considered **OOBM**.
*
* See [createModuleWithoutDependenciesOutOfBlockModificationTracker] for the definition of **OOBM**.
* @see ModificationTracker
*/
public abstract fun createProjectWideOutOfBlockModificationTracker(): ModificationTracker
/**
* Creates **OOBM** tracker which is incremented every time there is OOB change in the [module].
* This tracker should not consider changes in dependent modules.
* If you do not need incrementalliy in your tool (eg, it is okay to reanalyse the whole worked on every source code change) consider incrementing returned tracker on every source code change.
* If tour tool works with static code (so no codebase changes in possible) just return static modification tracker here.
* Creates a modification tracker which is incremented every time libraries in the project are changed.
*
* **Out Of Block Modification (OOBM)** is a such modification in source code which may change resolve of other non-local declarations.
*
* Consider the following example #1:
* ```
* val x = 10<caret>
* val z = x
* ```
* If we change initializer of `x` to `"str"` the return type of `x` will become 'String' instead of initial `Int`.
* This will change the return type of `z` as it does not have explicit type specified. So, it is an **OOBM**.
*
*
* Consider example #2:
* ```
* val x: Int = 10<caret>
* val z = x
* ```
* If we change 10 to "str" as in example #1, it would not change type of z, so it is not **OOBM**.
*
* Example of modifications in source code, which results **OOBM**
* - modification inside non-local (ie, accessible outside) declaration without explicit return type specified
* - modification o a package
* - creating new declaration
* - moving declaration to another package
*
* Generally, all modifications which happens outside callable declaration (function, accessor or property) body with explicit type are considered **OOBM**
* @see ModificationTracker
*/
public abstract fun createModuleWithoutDependenciesOutOfBlockModificationTracker(module: KtSourceModule): ModificationTracker
/**
* Creates modification tracker which is incremented every time libraries in project are changed.
*
* See [KotlinModificationTrackerFactory] for the definition of **OOBM**.
* @see ModificationTracker
*/
public abstract fun createLibrariesWideModificationTracker(): ModificationTracker
/**
* Creates [KtModuleStateTracker] which is incremented every time [module] roots changes.
*
* @see KtModuleStateTracker
*/
public abstract fun createModuleStateTracker(module: KtModule): KtModuleStateTracker
/**
* Increments modification trackers to invalidate caches. If [includeBinaryTrackers] is `false`, binary module-related modification
* trackers will not be incremented (such as library trackers, SDK tracker, built-ins tracker, and so on).
*/
@TestOnly
public abstract fun incrementModificationsCount(includeBinaryTrackers: Boolean = true)
public companion object {
public fun getService(project: Project): KotlinModificationTrackerFactory =
public fun getInstance(project: Project): KotlinModificationTrackerFactory =
project.getService(KotlinModificationTrackerFactory::class.java)
}
}
/**
* Represents current state of [KtModule] validity, can be created via [org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory.createModuleStateTracker]
*/
public interface KtModuleStateTracker {
/**
* If the module is still valid (i.e., it was not removed)
*/
public val isValid: Boolean
/**
* Represents modification tracker of modified roots similar to the [ModificationTracker].
*
* If the [isValid] == `false`, when behaviour is unspecified
*/
public val rootModificationCount: Long
}
/**
* Creates **OOBM** tracker which is incremented every time there is OOB change in some source project module.
* Creates an **OOBM** tracker which is incremented every time there is an OOB change in some source project module.
*
* See [KotlinModificationTrackerFactory.createModuleWithoutDependenciesOutOfBlockModificationTracker] for the definition of **OOBM**.
* See [KotlinModificationTrackerFactory.createProjectWideOutOfBlockModificationTracker] for the definition of **OOBM**.
* @see ModificationTracker
*/
public fun Project.createProjectWideOutOfBlockModificationTracker(): ModificationTracker =
this.getService(KotlinModificationTrackerFactory::class.java)
.createProjectWideOutOfBlockModificationTracker()
KotlinModificationTrackerFactory.getInstance(this).createProjectWideOutOfBlockModificationTracker()
/**
* Creates **OOBM** tracker which is incremented every time there is OOB change in the [module].
*
* See [KotlinModificationTrackerFactory.createModuleWithoutDependenciesOutOfBlockModificationTracker] for the definition of **OOBM**.
* @see ModificationTracker
*/
public fun KtSourceModule.createModuleWithoutDependenciesOutOfBlockModificationTracker(project: Project): ModificationTracker =
project.getService(KotlinModificationTrackerFactory::class.java)
.createModuleWithoutDependenciesOutOfBlockModificationTracker(this)
/**
* Creates modification tracker which is incremented every time libraries in project are changed.
* Creates a modification tracker which is incremented every time libraries in the project are changed.
*
* See [KotlinModificationTrackerFactory] for the definition of **OOBM**.
* @see ModificationTracker
*/
public fun Project.createAllLibrariesModificationTracker(): ModificationTracker =
this.getService(KotlinModificationTrackerFactory::class.java)
.createLibrariesWideModificationTracker()
KotlinModificationTrackerFactory.getInstance(this).createLibrariesWideModificationTracker()
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2023 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.analysis.providers.impl
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.analysis.providers.KotlinGlobalModificationService
import org.jetbrains.kotlin.analysis.providers.analysisMessageBus
import org.jetbrains.kotlin.analysis.providers.topics.KotlinTopics
public abstract class KotlinGlobalModificationServiceBase(private val project: Project) : KotlinGlobalModificationService() {
@TestOnly
protected abstract fun incrementModificationTrackers(includeBinaryTrackers: Boolean)
@TestOnly
override fun publishGlobalModuleStateModification() {
ApplicationManager.getApplication().assertWriteAccessAllowed()
incrementModificationTrackers(includeBinaryTrackers = true)
project.analysisMessageBus.syncPublisher(KotlinTopics.GLOBAL_MODULE_STATE_MODIFICATION).afterModification()
}
@TestOnly
override fun publishGlobalOutOfBlockModification() {
ApplicationManager.getApplication().assertWriteAccessAllowed()
incrementModificationTrackers(includeBinaryTrackers = true)
project.analysisMessageBus.syncPublisher(KotlinTopics.GLOBAL_OUT_OF_BLOCK_MODIFICATION).afterModification()
}
@TestOnly
override fun publishGlobalSourceModuleStateModification() {
ApplicationManager.getApplication().assertWriteAccessAllowed()
incrementModificationTrackers(includeBinaryTrackers = false)
project.analysisMessageBus.syncPublisher(KotlinTopics.GLOBAL_SOURCE_MODULE_STATE_MODIFICATION).afterModification()
}
@TestOnly
override fun publishGlobalSourceOutOfBlockModification() {
ApplicationManager.getApplication().assertWriteAccessAllowed()
incrementModificationTrackers(includeBinaryTrackers = false)
project.analysisMessageBus.syncPublisher(KotlinTopics.GLOBAL_SOURCE_OUT_OF_BLOCK_MODIFICATION).afterModification()
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2023 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.analysis.providers.impl
import com.intellij.openapi.project.Project
import com.intellij.util.messages.MessageBus
import org.jetbrains.kotlin.analysis.providers.KotlinMessageBusProvider
/**
* Provides the [project]'s [MessageBus] as the Analysis API message bus. This is the default implementation for both the standalone and the
* IDE Analysis API.
*
* [KotlinMessageBusProvider] exists so that this default may change in the future without breaking the API. Hence, it should not be assumed
* that the message bus provided by [KotlinMessageBusProvider] will always be equal to the project's message bus.
*/
public class KotlinProjectMessageBusProvider(private val project: Project) : KotlinMessageBusProvider {
override fun getMessageBus(): MessageBus = project.messageBus
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2023 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.analysis.providers.impl
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.TestOnly
public class KotlinStaticGlobalModificationService(private val project: Project) : KotlinGlobalModificationServiceBase(project) {
@TestOnly
override fun incrementModificationTrackers(includeBinaryTrackers: Boolean) {
KotlinStaticModificationTrackerFactory.getInstance(project).incrementModificationsCount(includeBinaryTrackers)
}
}
@@ -5,61 +5,32 @@
package org.jetbrains.kotlin.analysis.providers.impl
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.ModificationTracker
import com.intellij.openapi.util.SimpleModificationTracker
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory
import org.jetbrains.kotlin.analysis.providers.KtModuleStateTracker
public class KotlinStaticModificationTrackerFactory : KotlinModificationTrackerFactory() {
private val projectWide = SimpleModificationTracker()
private val librariesWide = SimpleModificationTracker()
private val moduleOutOfBlock = mutableMapOf<KtSourceModule, SimpleModificationTracker>()
private val moduleState = mutableMapOf<KtModule, KtModuleStateTrackerImpl>()
override fun createProjectWideOutOfBlockModificationTracker(): ModificationTracker {
return projectWide
}
override fun createModuleWithoutDependenciesOutOfBlockModificationTracker(module: KtSourceModule): ModificationTracker {
return moduleOutOfBlock.getOrPut(module) { SimpleModificationTracker() }
}
override fun createLibrariesWideModificationTracker(): ModificationTracker {
return librariesWide
}
override fun createModuleStateTracker(module: KtModule): KtModuleStateTracker {
return moduleState.getOrPut(module) { KtModuleStateTrackerImpl() }
}
@TestOnly
override fun incrementModificationsCount(includeBinaryTrackers: Boolean) {
internal fun incrementModificationsCount(includeBinaryTrackers: Boolean) {
projectWide.incModificationCount()
if (includeBinaryTrackers) {
librariesWide.incModificationCount()
}
moduleOutOfBlock.values.forEach { it.incModificationCount() }
moduleState.entries.forEach { (ktModule, tracker) ->
if (ktModule is KtBinaryModule && !includeBinaryTrackers) return@forEach
tracker.incModificationCount()
}
}
public companion object {
public fun getInstance(project: Project): KotlinStaticModificationTrackerFactory =
KotlinModificationTrackerFactory.getInstance(project) as KotlinStaticModificationTrackerFactory
}
}
private class KtModuleStateTrackerImpl: KtModuleStateTracker {
override val isValid: Boolean get() = true
private var _rootModificationCount = 0L
override val rootModificationCount: Long get() = _rootModificationCount
@TestOnly
fun incModificationCount() {
_rootModificationCount++
}
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2023 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.analysis.providers.topics
import org.jetbrains.kotlin.analysis.project.structure.KtModule
public fun interface KotlinGlobalModuleStateModificationListener {
/**
* [afterModification] is invoked in a write action after global modification of the module state of all [KtModule]s, including binary
* modules.
*
* This event is published after SDK removal and to invalidate caches during/between tests.
*/
public fun afterModification()
}
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2023 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.analysis.providers.topics
import org.jetbrains.kotlin.analysis.project.structure.KtModule
public fun interface KotlinGlobalOutOfBlockModificationListener {
/**
* [afterModification] is invoked in a write action after global out-of-block modification of all [KtModule]s, including binary modules.
*
* This event is published to invalidate caches during/between tests.
*/
public fun afterModification()
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2023 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.analysis.providers.topics
import org.jetbrains.kotlin.analysis.project.structure.KtModule
public fun interface KotlinGlobalSourceModuleStateModificationListener {
/**
* [afterModification] is invoked in a write action after global modification of the module state of all source [KtModule]s, excluding
* binary modules.
*
* This event is published to invalidate caches during/between tests.
*/
public fun afterModification()
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2023 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.analysis.providers.topics
import org.jetbrains.kotlin.analysis.project.structure.KtModule
public fun interface KotlinGlobalSourceOutOfBlockModificationListener {
/**
* [afterModification] is invoked in a write action after global out-of-block modification of all source [KtModule]s, excluding binary
* modules.
*
* This event is published on global PSI changes.
*/
public fun afterModification()
}
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2023 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.analysis.providers.topics
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory
public fun interface KotlinModuleOutOfBlockModificationListener {
/**
* [afterModification] is invoked in a write action after an out-of-block modification happened in [module]'s source code.
*
* See [KotlinModificationTrackerFactory.createProjectWideOutOfBlockModificationTracker] for an explanation of out-of-block
* modifications.
*
* This event may be published for any and all source code changes, not just out-of-block modifications, to simplify the implementation
* of modification detection.
*/
public fun afterModification(module: KtModule)
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2023 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.analysis.providers.topics
import org.jetbrains.kotlin.analysis.project.structure.KtModule
public fun interface KotlinModuleStateModificationListener {
/**
* [afterModification] is invoked in a write action after the [module] was moved, removed, or its structure changed. [isRemoval] signals
* if the module was removed.
*/
public fun afterModification(module: KtModule, isRemoval: Boolean)
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2023 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.analysis.providers.topics
import com.intellij.util.messages.Topic
import org.jetbrains.kotlin.analysis.providers.analysisMessageBus
/**
* [Topic]s for Analysis API event subscription and publication. These topics should be subscribed to and published to via the Analysis API
* message bus: [analysisMessageBus].
*
* See the individual listener interfaces for documentation about the events described by these topics:
* - [KotlinModuleStateModificationListener]
* - [KotlinModuleOutOfBlockModificationListener]
* - [KotlinGlobalModuleStateModificationListener]
* - [KotlinGlobalOutOfBlockModificationListener]
* - [KotlinGlobalSourceModuleStateModificationListener]
* - [KotlinGlobalSourceOutOfBlockModificationListener]
*
* Care needs to be taken with the lack of interplay between different types of topics: Publishing a global modification event, for example,
* does not imply the corresponding module-level event. Similarly, publishing a module state modification event does not imply out-of-block
* modification.
*
* #### Implementation Notes
*
* Analysis API implementations need to take care of publishing to these topics via the [analysisMessageBus]. In general, if your tool works
* with static code and static module structure, you do not need to publish any events. However, keep in mind the contracts of the various
* topics. For example, if your tool can guarantee a static module structure but source code can still change, module state modification
* events do not need to be published, but out-of-block modification events do.
*/
public object KotlinTopics {
public val MODULE_STATE_MODIFICATION: Topic<KotlinModuleStateModificationListener> =
Topic(KotlinModuleStateModificationListener::class.java, Topic.BroadcastDirection.TO_CHILDREN, true)
public val MODULE_OUT_OF_BLOCK_MODIFICATION: Topic<KotlinModuleOutOfBlockModificationListener> =
Topic(KotlinModuleOutOfBlockModificationListener::class.java, Topic.BroadcastDirection.TO_CHILDREN, true)
public val GLOBAL_MODULE_STATE_MODIFICATION: Topic<KotlinGlobalModuleStateModificationListener> =
Topic(KotlinGlobalModuleStateModificationListener::class.java, Topic.BroadcastDirection.TO_CHILDREN, true)
public val GLOBAL_OUT_OF_BLOCK_MODIFICATION: Topic<KotlinGlobalOutOfBlockModificationListener> =
Topic(KotlinGlobalOutOfBlockModificationListener::class.java, Topic.BroadcastDirection.TO_CHILDREN, true)
public val GLOBAL_SOURCE_MODULE_STATE_MODIFICATION: Topic<KotlinGlobalSourceModuleStateModificationListener> =
Topic(KotlinGlobalSourceModuleStateModificationListener::class.java, Topic.BroadcastDirection.TO_CHILDREN, true)
public val GLOBAL_SOURCE_OUT_OF_BLOCK_MODIFICATION: Topic<KotlinGlobalSourceOutOfBlockModificationListener> =
Topic(KotlinGlobalSourceOutOfBlockModificationListener::class.java, Topic.BroadcastDirection.TO_CHILDREN, true)
}
@@ -119,6 +119,8 @@ public class StandaloneAnalysisAPISessionBuilder(
FirStandaloneServiceRegistrar.registerProjectModelServices(project, kotlinCoreProjectEnvironment.parentDisposable)
registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java)
registerService(KotlinMessageBusProvider::class.java, KotlinProjectMessageBusProvider::class.java)
registerService(KotlinGlobalModificationService::class.java, KotlinStaticGlobalModificationService::class.java)
registerService(KtModuleScopeProvider::class.java, KtModuleScopeProviderImpl())
registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(ktFiles))
@@ -70,6 +70,8 @@ public fun configureApplicationEnvironment(app: MockApplication) {
* * [SymbolKotlinAsJavaSupport]
* * [ClsJavaStubByVirtualFileCache]
* * [KotlinModificationTrackerFactory]
* * [KotlinMessageBusProvider]
* * [KotlinGlobalModificationService]
* * [KotlinAnnotationsResolverFactory]
* * [org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirResolveSessionService]
* * [FirSealedClassInheritorsProcessorFactory]
@@ -112,6 +114,16 @@ internal fun configureProjectEnvironment(
KotlinStaticModificationTrackerFactory()
)
project.registerService(
KotlinMessageBusProvider::class.java,
KotlinProjectMessageBusProvider(project),
)
project.registerService(
KotlinGlobalModificationService::class.java,
KotlinStaticGlobalModificationService(project),
)
// FIR LC
project.registerService(
ClsJavaStubByVirtualFileCache::class.java,
@@ -13,8 +13,6 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure
import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.model.TestModule
@@ -25,6 +23,7 @@ import java.util.concurrent.locks.ReadWriteLock
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.withLock
import kotlin.reflect.KClass
import org.jetbrains.kotlin.analysis.providers.KotlinGlobalModificationService
abstract class AnalysisApiTestConfigurator {
open val testPrefix: String? get() = null
@@ -39,9 +38,8 @@ abstract class AnalysisApiTestConfigurator {
open fun prepareFilesInModule(files: List<PsiFile>, module: TestModule, testServices: TestServices) {}
open fun doOutOfBlockModification(file: KtFile) {
file.project.getService(KotlinModificationTrackerFactory::class.java)
.incrementModificationsCount()
open fun doGlobalModuleStateModification(project: Project) {
KotlinGlobalModificationService.getInstance(project).publishGlobalModuleStateModification()
}
open fun preprocessTestDataPath(path: Path): Path = path
@@ -9,7 +9,6 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.services.TestModuleStructure
import org.jetbrains.kotlin.test.services.TestServices
@@ -18,7 +17,6 @@ import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisAp
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleWithFiles
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.allDirectDependenciesOfType
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtSourceModuleByCompilerConfiguration
import org.jetbrains.kotlin.analysis.test.framework.project.structure.TestModuleStructureFactory
@@ -57,7 +55,7 @@ object FirLowLevelCompilerBasedTestConfigurator : AnalysisApiTestConfigurator()
)
}
override fun doOutOfBlockModification(file: KtFile) {
override fun doGlobalModuleStateModification(project: Project) {
error("Should not be called for compiler based tests")
}
}