[workspace model] IDEA-298714 Add documentation for the introduced extension points

GitOrigin-RevId: 5303d767859c4e417e83824aca48fbd1fa015063
This commit is contained in:
Mikhail Mazurkevich
2022-09-08 20:27:29 +00:00
committed by intellij-monorepo-bot
parent 131873fdfe
commit 26e7978aa8
5 changed files with 137 additions and 13 deletions
@@ -59,7 +59,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage,
else -> moduleSource
}
if (facet is FacetBridge<*>) {
facet.addNewModuleSettings(diff, moduleEntity, source)
facet.addToStorage(diff, moduleEntity, source)
} else {
val facetConfigurationXml = FacetUtil.saveFacetConfiguration(facet)?.let { JDOMUtil.write(it) }
val underlyingEntity = facet.underlyingFacet?.let { diff.facetMapping().getEntities(it).single() as FacetEntity }
@@ -74,7 +74,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage,
override fun removeFacet(facet: Facet<*>?) {
if (facet == null) return
if (facet is FacetBridge<*>) {
facet.removeModuleSettings(diff, moduleEntity)
facet.removeFromStorage(diff, moduleEntity)
} else {
val facetEntity = diff.facetMapping().getEntities(facet).singleOrNull() as? FacetEntity ?: return
removeFacetEntityWithSubFacets(facetEntity)
@@ -99,7 +99,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage,
override fun rename(facet: Facet<*>, newName: String) {
if (facet is FacetBridge<*>) {
facet.renameModuleSettings(diff, moduleEntity, newName)
facet.rename(diff, moduleEntity, newName)
} else {
val entity = diff.facetMapping().getEntities(facet).single() as FacetEntity
val newEntity = diff.modifyEntity(entity) {
@@ -113,7 +113,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage,
override fun getNewName(facet: Facet<*>): String {
if (facet is FacetBridge<*>) {
return facet.getNewModuleSettingsName(diff, moduleEntity)
return facet.getNewName(diff, moduleEntity)
} else {
val entity = diff.facetMapping().getEntities(facet).single() as FacetEntity
return entity.name
@@ -174,7 +174,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage,
override fun isNewFacet(facet: Facet<*>): Boolean {
if (facet is FacetBridge<*>) {
return facet.isNewModuleSettings(diff, moduleEntity)
return facet.isNew(diff, moduleEntity)
} else {
val entity = diff.facetMapping().getEntities(facet).singleOrNull() as FacetEntity?
return entity != null && entity.persistentId !in initialStorage
@@ -7,16 +7,65 @@ import com.intellij.workspaceModel.storage.WorkspaceEntity
import com.intellij.workspaceModel.storage.bridgeEntities.api.ModuleEntity
import org.jetbrains.annotations.ApiStatus
/**
* Originally, [com.intellij.facet.Facet] were made to support custom setting for the module, but this solution is not
* rather flexible and thanks to the workspace model we have an opportunity to make custom entities describing additional
* module settings. To have a sort of bridge between new approach with declaring custom module settings and [com.intellij.facet.Facet]
* several extension points were introduced:
* 1) [com.intellij.workspaceModel.ide.impl.jps.serialization.CustomFacetRelatedEntitySerializer] to add support custom entity
* serialization/deserialization as facet tag.
* 2) [WorkspaceFacetContributor] to have an option to fire different sorts of event related to the [com.intellij.facet.Facet] during
* the changes of your custom entity this extension point should be implemented.
*
* If you want to use your custom module setting entity under the hood of your facet you also need to implement
* [com.intellij.workspaceModel.ide.legacyBridge.FacetBridge] to be properly updated.
*
* **N.B. Most of the time you need to implement them all to have a correct support all functionality relying on Facets.**
*
* Particularly this extension point was introduced to fire all related to [com.intellij.facet.FacetManagerListener] events.
* We have a universal listener [com.intellij.workspaceModel.ide.impl.legacyBridge.facet.FacetEntityChangeListener] which base
* on changes in your entities and data calculated by this EP can fire all these events for us.
*/
@ApiStatus.Internal
interface WorkspaceFacetContributor<T: WorkspaceEntity> {
/**
* Declare class for the main entity associated with [com.intellij.facet.Facet].
*/
val rootEntityType: Class<T>
/**
* Get the name for the associated with entity [com.intellij.facet.Facet]
*/
fun getFacetName(entity: T): String
/**
* Method return the module to which this entity belongs
*/
fun getRelatedModuleEntity(entity: T): ModuleEntity
/**
* Method return the entity of type declared in [rootEntityType], associated with this module if any
*/
fun getRootEntityByModuleEntity(moduleEntity: ModuleEntity): T?
/**
* Method for creating [com.intellij.facet.Facet] from the given entity of root type
*/
fun createFacetFromEntity(entity: T, project: Project): Facet<*>
/**
* This field should be overridden if root entity can have children which changes can affect e.g. facet configuration
* otherwise [com.intellij.facet.FacetManagerListener.facetConfigurationChanged] will not be fired correctly. In the same time
* [getRootEntityByChild] should be implemented
*/
val childEntityTypes: List<Class<out WorkspaceEntity>>
get() = emptyList()
/**
* Method for getting entity of root type by child.
*
* **This method should be overridden only if the root entity have children([childEntityTypes] returns not null list), otherwise it shouldn't be touched.**
*/
fun getRootEntityByChild(childEntity: WorkspaceEntity): T {
error("Implementation of the method should be overridden because root entity contains children")
}
@@ -5,13 +5,50 @@ import com.intellij.workspaceModel.storage.MutableEntityStorage
import com.intellij.workspaceModel.storage.WorkspaceEntity
import com.intellij.workspaceModel.storage.bridgeEntities.api.ModuleEntity
/**
* Bridge interface for facet which uses custom module settings entity under the hood
*/
interface FacetBridge<T: WorkspaceEntity> {
fun isNewModuleSettings(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity): Boolean
fun getNewModuleSettingsName(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity): String
fun addNewModuleSettings(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity, entitySource: EntitySource)
fun removeModuleSettings(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity)
fun renameModuleSettings(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity, newName: String)
/**
* Check if it's a newly created facet bridge, and it's root entity doesn't exist in main storage
*/
fun isNew(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity): Boolean
/**
* Returns the new name associated with given facet bridge
*/
fun getNewName(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity): String
/**
* Add root entity which [FacetBridge] uses under the hood, into the storage
* @param mutableStorage for saving root entity and it's children in it
* @param moduleEntity corresponds to this [FacetBridge]
* @param entitySource which should be used for such entities
*/
fun addToStorage(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity, entitySource: EntitySource)
/**
* Removes all associated with this bridge entities from the storage
*/
fun removeFromStorage(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity)
/**
* Rename entity associated with this bridge
*/
fun rename(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity, newName: String)
/**
* Apply changes from the entity which used under the hood into the builder passed as a parameter
*/
fun applyChangesToStorage(mutableStorage: MutableEntityStorage, module: ModuleBridge)
/**
* Update facet configuration base on the data from the related entity
*/
fun updateFacetConfiguration(rootEntity: T)
/**
* Method returns the entity which is used under the hood of this [FacetBridge]
*/
fun getRootEntity(): T
}
@@ -11,16 +11,55 @@ import org.jetbrains.annotations.ApiStatus
import org.jetbrains.jps.model.serialization.facet.FacetState
/**
* The goal of this extension point is supporting serialization/deserialization of custom entities
* related to the module and located at the same .iml file
* Originally, [com.intellij.facet.Facet] were made to support custom setting for the module, but this solution is not
* rather flexible and thanks to the workspace model we have an opportunity to make custom entities describing additional
* module settings. To have a sort of bridge between new approach with declaring custom module settings and [com.intellij.facet.Facet]
* several extension points were introduced:
* 1) [CustomFacetRelatedEntitySerializer] to add support custom entity
* serialization/deserialization as facet tag.
* 2) [com.intellij.workspaceModel.ide.legacyBridge.WorkspaceFacetContributor] to have an option to fire different sorts of event related to the [com.intellij.facet.Facet] during
* the changes of your custom entity this extension point should be implemented.
*
* If you want to use your custom module setting entity under the hood of your facet you also need to implement
* [com.intellij.workspaceModel.ide.legacyBridge.FacetBridge] to be properly updated.
*
* **N.B. Most of the time you need to implement them all to have a correct support all functionality relying on Facets.**
*/
@ApiStatus.Internal
interface CustomFacetRelatedEntitySerializer<T: WorkspaceEntity> {
/**
* Declare class for the main entity associated with [com.intellij.facet.Facet].
*/
val rootEntityType: Class<T>
/**
* Facet type this extension point can serialization/deserialization. The result of deserialization, entities of the type declared
* at [rootEntityType] in the [com.intellij.workspaceModel.storage.EntitySource]
*/
val supportedFacetType: String
/**
* Method for deserialization [org.jetbrains.jps.model.serialization.facet.FacetState] read from external source. Facet state is
* an intermediate representation to avoid core communication with tags directly.
* @param builder storage to add read from facet state entities
* @param moduleEntity module to which these settings belong
* @param facetState intermediate representation of facet related data read out from external sources
* @param evaluateExternalSystemIdAndEntitySource function which should be invoked to get [com.intellij.workspaceModel.storage.EntitySource]
* for your entities and externalSystemId which should be stored somewhere in your entities
*/
fun loadEntitiesFromFacetState(builder: MutableEntityStorage, moduleEntity: ModuleEntity, facetState: FacetState,
evaluateExternalSystemIdAndEntitySource: (FacetState) -> Pair<String?, EntitySource>)
/**
* Create intermediate representation from entities of declared at [rootEntityType] type which will be used for serialization on disk.
* @param entities list of certain type entities
* @param storeExternally indicator which tells where this data will be store, under `.idea` or in external system folder
*/
fun createFacetStateFromEntities(entities: List<T>, storeExternally: Boolean): List<FacetState>
/**
* Method for creation facet XML tag from root type entity passed as a parameter
*/
fun serializeIntoXml(entity: T): Element
companion object {
@@ -10,7 +10,6 @@ import com.intellij.workspaceModel.storage.bridgeEntities.addFacetEntity
import com.intellij.workspaceModel.storage.bridgeEntities.api.*
import org.jdom.Element
import org.jetbrains.jps.model.serialization.facet.FacetState
import java.util.function.Consumer
class DefaultFacetEntitySerializer: CustomFacetRelatedEntitySerializer<FacetEntity> {
override val rootEntityType: Class<FacetEntity>