From 7f48fa3c8ff38d1d26e6a6e13de2447e73a6c351 Mon Sep 17 00:00:00 2001 From: "Andrei.Kuznetsov" Date: Wed, 6 Aug 2025 12:48:01 +0200 Subject: [PATCH] IJPL-196089 annotate and document ModuleRootListener GitOrigin-RevId: f01b3424f2f8686115e69f5175047b7344ab79bf --- .../openapi/roots/ModuleRootListener.java | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootListener.java b/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootListener.java index f0eb2646657e..1e1742c94a5b 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootListener.java +++ b/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootListener.java @@ -3,6 +3,7 @@ package com.intellij.openapi.roots; import com.intellij.openapi.project.RootsChangeRescanningInfo; import com.intellij.openapi.roots.ex.ProjectRootManagerEx; +import com.intellij.util.concurrency.annotations.RequiresWriteLock; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -12,26 +13,44 @@ import java.util.EventListener; /** * Project root changes. *

- * Instead of events with {@link ModuleRootEvent#isCausedByWorkspaceModelChangesOnly()} one may use - * {@link com.intellij.platform.backend.workspace.WorkspaceModelChangeListener} and get more fine-grained incremental events. + * For more fine-grained, incremental updates related specifically to workspace model changes, + * consider using {@link com.intellij.platform.backend.workspace.WorkspaceModelChangeListener}. *

- * {@link ModuleRootEvent#isCausedByWorkspaceModelChangesOnly()} is always {@code false} in the {@linkplain #beforeRootsChange(ModuleRootEvent)}. - * This is because {@linkplain ModuleRootListener} will de-duplicate nested {@linkplain ModuleRootEvent} events, and at the moment of the - * {@linkplain #beforeRootsChange(ModuleRootEvent)} invocation we don't know if all the events (including de-duplicated) are from WSM or not. - *

- * {@linkplain com.intellij.platform.backend.workspace.WorkspaceModelChangeListener} is not a direct replacement for {@linkplain ModuleRootListener}, - * because {@linkplain ModuleRootListener} may generate events that are not related to the workspace model. For example, there will be an - * event on a filetype change, roots validity change (VirtualFileUrl validity change like when a jar file is downloaded), and - * out of thin air events caused by {@link ProjectRootManagerEx#makeRootsChange(Runnable, RootsChangeRescanningInfo)} + * Note that unlike {@link com.intellij.platform.backend.workspace.WorkspaceModelChangeListener} {@link ModuleRootListener} + * may produce events unrelated to the workspace model. + * For example, events may be triggered by: + *

+ * + * Both {@link com.intellij.platform.backend.workspace.WorkspaceModelChangeListener} and {@link ModuleRootListener} will + * generate events when roots validity changes (e.g., when a JAR file is downloaded and the corresponding VirtualFile becomes valid) */ @ApiStatus.OverrideOnly public interface ModuleRootListener extends EventListener { @Topic.ProjectLevel Topic TOPIC = new Topic<>(ModuleRootListener.class); + /** + * Called within the same write action that triggers the change, but before the change is actually applied. + *

+ * @param event An approximate representation of the upcoming "roots change" event, as estimated by the IDE. + * In most cases, the IDE cannot provide precise information in this "before" event. + *

+ * Note that {@link ModuleRootEvent#isCausedByWorkspaceModelChangesOnly()} always returns {@code false} in this method. + * This is because nested {@linkplain ModuleRootEvent} events are de-duplicated by the IDE, and at the time + * {@code beforeRootsChange} is called, it's not yet known whether all events (including de-duplicated ones) + * were caused exclusively by workspace model (WSM) changes. + */ + @RequiresWriteLock default void beforeRootsChange(@NotNull ModuleRootEvent event) { } + /** + * Called within the same write action that triggers the change, after the change is actually applied. + */ + @RequiresWriteLock default void rootsChanged(@NotNull ModuleRootEvent event) { } }