IJPL-207563 drop project.root.manager.over.wsm flag

GitOrigin-RevId: 4ceb26a2e142d5570a4328be5ed31a63d4de7e25
This commit is contained in:
Ilya Korennoy
2025-10-07 11:55:13 +02:00
committed by intellij-monorepo-bot
parent 56ca9c0a94
commit f80f8e5bf3
17 changed files with 64 additions and 458 deletions

View File

@@ -1554,7 +1554,6 @@
<regExpLanguageHost forClass="com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl"
implementationClass="com.intellij.psi.impl.JavaRegExpHost"/>
<diff.DiffExtension implementation="com.intellij.refactoring.extractMethod.preview.ExtractMethodDiffViewerCustomizer"/>
<projectExtension id="compiler" implementation="com.intellij.openapi.roots.impl.CompilerProjectExtensionImpl$MyProjectExtension"/>
<roots.watchedRootsProvider implementation="com.intellij.openapi.roots.impl.CompilerProjectExtensionImpl$MyWatchedRootsProvider"/>
<orderRootType implementation="com.intellij.openapi.roots.AnnotationOrderRootType"/>
<orderRootType implementation="com.intellij.openapi.roots.JavadocOrderRootType"/>

View File

@@ -10,16 +10,13 @@ import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.CompilerModuleExtension;
import com.intellij.openapi.roots.CompilerProjectExtension;
import com.intellij.openapi.roots.ProjectExtension;
import com.intellij.openapi.roots.WatchedRootsProvider;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.LightFilePointer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import com.intellij.platform.backend.workspace.VirtualFileUrls;
import com.intellij.platform.backend.workspace.WorkspaceModel;
import com.intellij.platform.workspace.storage.url.VirtualFileUrl;
@@ -27,70 +24,35 @@ import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager;
import com.intellij.util.concurrency.ThreadingAssertions;
import com.intellij.util.concurrency.annotations.RequiresWriteLock;
import kotlin.Unit;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
final class CompilerProjectExtensionImpl extends CompilerProjectExtension implements Disposable {
private static final String OUTPUT_TAG = "output";
private static final String URL = "url";
private static final Logger LOG = Logger.getInstance(CompilerProjectExtensionImpl.class);
// This field is not used when useWsm set to `true`
private VirtualFilePointer myCompilerOutput;
private LocalFileSystem.WatchRequest myCompilerOutputWatchRequest;
private final Project project;
private final boolean useWsm = Registry.is("project.root.manager.over.wsm", true);
CompilerProjectExtensionImpl(@NotNull Project project) {
this.project = project;
}
/**
* Returns true if the compiler output was changed after read
*/
private boolean readExternal(@NotNull Element element) {
Element pathElement = element.getChild(OUTPUT_TAG);
if (pathElement != null) {
String outputPath = pathElement.getAttributeValue(URL);
VirtualFilePointer oldValue = myCompilerOutput;
myCompilerOutput = outputPath != null ? VirtualFilePointerManager.getInstance().create(outputPath, this, null) : null;
return !Objects.equals(
oldValue != null ? oldValue.getUrl() : null,
myCompilerOutput != null ? myCompilerOutput.getUrl() : null
);
}
return false;
}
private void writeExternal(@NotNull Element element) {
if (myCompilerOutput != null) {
Element pathElement = new Element(OUTPUT_TAG);
pathElement.setAttribute(URL, myCompilerOutput.getUrl());
element.addContent(pathElement);
}
}
@Override
public void dispose() {
myCompilerOutput = null;
}
private @Nullable VirtualFileUrl getCompilerOutputWSM() {
LOG.assertTrue(useWsm, "This code does should not be used when project.root.manager.over.wsm is false");
JavaProjectSettingsEntity entity = JavaEntitiesWsmUtils.getSingleEntity(WorkspaceModel.getInstance(project).getCurrentSnapshot(), JavaProjectSettingsEntity.class);
return entity != null ? entity.getCompilerOutput() : null;
}
@RequiresWriteLock
private void setCompilerOutputWSM(@Nullable String fileUrl) {
LOG.assertTrue(useWsm, "This code does should not be used when project.root.manager.over.wsm is false");
ThreadingAssertions.assertWriteAccess();
WorkspaceModel workspaceModel = WorkspaceModel.getInstance(project);
@@ -106,42 +68,27 @@ final class CompilerProjectExtensionImpl extends CompilerProjectExtension implem
@Override
public VirtualFile getCompilerOutput() {
if (useWsm) {
VirtualFileUrl fileUrl = getCompilerOutputWSM();
return fileUrl != null ? VirtualFileUrls.getVirtualFile(fileUrl) : null;
}
else {
return myCompilerOutput != null ? myCompilerOutput.getFile() : null;
}
VirtualFileUrl fileUrl = getCompilerOutputWSM();
return fileUrl != null ? VirtualFileUrls.getVirtualFile(fileUrl) : null;
}
@Override
public String getCompilerOutputUrl() {
if (useWsm) {
VirtualFileUrl fileUrl = getCompilerOutputWSM();
return fileUrl != null ? fileUrl.getUrl() : null;
}
else {
return myCompilerOutput != null ? myCompilerOutput.getUrl() : null;
}
VirtualFileUrl fileUrl = getCompilerOutputWSM();
return fileUrl != null ? fileUrl.getUrl() : null;
}
@Override
public @Nullable VirtualFilePointer getCompilerOutputPointer() {
if (useWsm) {
VirtualFileUrl fileUrl = getCompilerOutputWSM();
if (fileUrl == null) {
return null;
}
else if (fileUrl instanceof VirtualFilePointer virtualFilePointer) {
return virtualFilePointer;
}
else {
return new LightFilePointer(fileUrl.getUrl());
}
VirtualFileUrl fileUrl = getCompilerOutputWSM();
if (fileUrl == null) {
return null;
}
else if (fileUrl instanceof VirtualFilePointer virtualFilePointer) {
return virtualFilePointer;
}
else {
return myCompilerOutput;
return new LightFilePointer(fileUrl.getUrl());
}
}
@@ -152,12 +99,7 @@ final class CompilerProjectExtensionImpl extends CompilerProjectExtension implem
"Compiler outputs may only be updated under write action. " +
"This method is deprecated. Please consider using `setCompilerOutputUrl` instead.");
if (useWsm) {
setCompilerOutputWSM(pointer != null ? pointer.getUrl() : null);
}
else {
myCompilerOutput = pointer;
}
setCompilerOutputWSM(pointer != null ? pointer.getUrl() : null);
}
@Override
@@ -173,13 +115,7 @@ final class CompilerProjectExtensionImpl extends CompilerProjectExtension implem
else {
// TODO ANK (Maybe): maybe we should remove old compilerOutputUrl from watched roots? (keep in mind that there might be
// some other code which has added exactly the same root to the watch roots)
if (useWsm) {
setCompilerOutputWSM(compilerOutputUrl);
}
else {
VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create(compilerOutputUrl, this, null);
setCompilerOutputPointer(pointer);
}
setCompilerOutputWSM(compilerOutputUrl);
String path = VfsUtilCore.urlToPath(compilerOutputUrl);
myCompilerOutputWatchRequest = LocalFileSystem.getInstance().replaceWatchedRoot(myCompilerOutputWatchRequest, path, true);
}
@@ -214,28 +150,6 @@ final class CompilerProjectExtensionImpl extends CompilerProjectExtension implem
return rootsToWatch;
}
private static CompilerProjectExtensionImpl getImpl(Project project) {
return (CompilerProjectExtensionImpl)CompilerProjectExtension.getInstance(project);
}
static final class MyProjectExtension extends ProjectExtension {
private final Project myProject;
MyProjectExtension(Project project) {
myProject = project;
}
@Override
public boolean readExternalElement(@NotNull Element element) {
return getImpl(myProject).readExternal(element);
}
@Override
public void writeExternal(@NotNull Element element) {
getImpl(myProject).writeExternal(element);
}
}
static final class MyWatchedRootsProvider implements WatchedRootsProvider {
@Override
public @NotNull Set<String> getRootsToWatch(@NotNull Project project) {

View File

@@ -11,7 +11,6 @@ import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.roots.ProjectExtension;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.platform.backend.workspace.WorkspaceModel;
import com.intellij.pom.java.JavaRelease;
import com.intellij.pom.java.LanguageLevel;
@@ -19,13 +18,10 @@ import com.intellij.util.ObjectUtils;
import com.intellij.util.concurrency.ThreadingAssertions;
import com.intellij.util.concurrency.annotations.RequiresWriteLock;
import kotlin.Unit;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.Objects;
/**
* @author anna
*/
@@ -36,17 +32,11 @@ public final class LanguageLevelProjectExtensionImpl extends LanguageLevelProjec
) {
}
private static final String LANGUAGE_LEVEL = "languageLevel";
private static final String DEFAULT_ATTRIBUTE = "default";
private static final Logger LOG = Logger.getInstance(LanguageLevelProjectExtensionImpl.class);
private final boolean useWsm = Registry.is("project.root.manager.over.wsm", true);
private final Project myProject;
private LanguageLevel myCurrentLevel;
// This field is not used when useWsm set to `true`
private @NotNull LanguageLevelExtensionState myLanguageLevelState = new LanguageLevelExtensionState(null, null);
public LanguageLevelProjectExtensionImpl(final Project project) {
myProject = project;
}
@@ -55,21 +45,6 @@ public final class LanguageLevelProjectExtensionImpl extends LanguageLevelProjec
return (LanguageLevelProjectExtensionImpl)getInstance(project);
}
/**
* Returns true if the state was changed after read
*/
private boolean readExternal(final Element element) {
Logger.getInstance(LanguageLevelProjectExtensionImpl.class).assertTrue(!useWsm, "Should read state from WSM");
String level = element.getAttributeValue(LANGUAGE_LEVEL);
LanguageLevel languageLevelOldValue = myLanguageLevelState.myLanguageLevel;
LanguageLevel languageLevelNewValue = readLanguageLevel(level);
String defaultNewValueStr = element.getAttributeValue(DEFAULT_ATTRIBUTE);
Boolean defaultOldValue = myLanguageLevelState.myDefault;
Boolean defaultNewValue = defaultNewValueStr != null ? Boolean.parseBoolean(defaultNewValueStr) : null;
myLanguageLevelState = new LanguageLevelExtensionState(languageLevelNewValue, defaultNewValue);
return !Objects.equals(defaultOldValue, defaultNewValue) || languageLevelOldValue != languageLevelNewValue;
}
private static @Nullable LanguageLevel readLanguageLevel(@Nullable String level) {
if (level != null) {
for (LanguageLevel languageLevel : LanguageLevel.getEntries()) {
@@ -84,18 +59,6 @@ public final class LanguageLevelProjectExtensionImpl extends LanguageLevelProjec
}
}
private void writeExternal(final Element element) {
Logger.getInstance(LanguageLevelProjectExtensionImpl.class).assertTrue(!useWsm, "Should write state to WSM");
if (myLanguageLevelState.myLanguageLevel != null) {
element.setAttribute(LANGUAGE_LEVEL, myLanguageLevelState.myLanguageLevel.name());
}
Boolean aBoolean = myLanguageLevelState.myDefault;
if (aBoolean != null && aBoolean != myProject.isDefault()) { // do not write default 'true' for default project
element.setAttribute(DEFAULT_ATTRIBUTE, Boolean.toString(aBoolean));
}
}
@Override
public @NotNull LanguageLevel getLanguageLevel() {
return getLanguageLevelOrDefault();
@@ -124,38 +87,28 @@ public final class LanguageLevelProjectExtensionImpl extends LanguageLevelProjec
@RequiresWriteLock(generateAssertion = false)
private void setLanguageLevelInternal(@Nullable LanguageLevel languageLevel, @Nullable Boolean isDefault) {
if (useWsm) {
ThreadingAssertions.assertWriteAccess();
ThreadingAssertions.assertWriteAccess();
WorkspaceModel workspaceModel = WorkspaceModel.getInstance(myProject);
workspaceModel.updateProjectModel("setLanguageLevelInternal", mutableStorage -> {
JavaEntitiesWsmUtils.addOrModifyJavaProjectSettingsEntity(myProject, mutableStorage, entity -> {
var ll = languageLevel != null ? languageLevel.name() : null;
entity.setLanguageLevelId(ll);
entity.setLanguageLevelDefault(isDefault);
});
return Unit.INSTANCE;
WorkspaceModel workspaceModel = WorkspaceModel.getInstance(myProject);
workspaceModel.updateProjectModel("setLanguageLevelInternal", mutableStorage -> {
JavaEntitiesWsmUtils.addOrModifyJavaProjectSettingsEntity(myProject, mutableStorage, entity -> {
var ll = languageLevel != null ? languageLevel.name() : null;
entity.setLanguageLevelId(ll);
entity.setLanguageLevelDefault(isDefault);
});
}
else {
myLanguageLevelState = new LanguageLevelExtensionState(languageLevel, isDefault);
}
return Unit.INSTANCE;
});
}
private @NotNull LanguageLevelExtensionState getLanguageLevelInternal() {
if (useWsm) {
JavaProjectSettingsEntity entity = JavaEntitiesWsmUtils.getSingleEntity(WorkspaceModel.getInstance(myProject).getCurrentSnapshot(), JavaProjectSettingsEntity.class);
JavaProjectSettingsEntity entity = JavaEntitiesWsmUtils.getSingleEntity(WorkspaceModel.getInstance(myProject).getCurrentSnapshot(), JavaProjectSettingsEntity.class);
if (entity != null) {
LanguageLevel llParsed = readLanguageLevel(entity.getLanguageLevelId());
return new LanguageLevelExtensionState(llParsed, entity.getLanguageLevelDefault());
}
else {
return new LanguageLevelExtensionState(null, null);
}
if (entity != null) {
LanguageLevel llParsed = readLanguageLevel(entity.getLanguageLevelId());
return new LanguageLevelExtensionState(llParsed, entity.getLanguageLevelDefault());
}
else {
return myLanguageLevelState;
return new LanguageLevelExtensionState(null, null);
}
}
@@ -220,16 +173,6 @@ public final class LanguageLevelProjectExtensionImpl extends LanguageLevelProjec
myInstance = ((LanguageLevelProjectExtensionImpl)getInstance(project));
}
@Override
public boolean readExternalElement(@NotNull Element element) {
return myInstance.readExternal(element);
}
@Override
public void writeExternal(@NotNull Element element) {
myInstance.writeExternal(element);
}
@Override
public void projectSdkChanged(@Nullable Sdk sdk) {
myInstance.projectSdkChanged(sdk);

View File

@@ -6,7 +6,6 @@ import com.intellij.openapi.application.readAction
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.writeText
import com.intellij.testFramework.ApplicationRule
import com.intellij.testFramework.TemporaryDirectory
@@ -74,12 +73,6 @@ class ProjectRootManagerImplTest {
"corretto-17" == ProjectRootManagerEx.getInstanceEx(project).projectSdkName
}
if (!Registry.`is`("project.root.manager.over.wsm")) {
// In legacy mode, ProjectJdkListener will be invoked in WA, but not the same WA where the change occurred.
// Wait for yet another WA to occur.
awaitWriteActions()
}
Assertions.assertThat(count).hasValue(1)
}
}

View File

@@ -247,13 +247,8 @@ public class RootsChangedTest extends JavaModuleTestCase {
ProjectRootManager.getInstance(myProject).setProjectSdk(jdkBBB);
if (Registry.is("project.root.manager.over.wsm")) {
// don't care if there are references from modules to project SDK or not: change in the "project sdk" always generates events
myModuleRootListener.assertEventsCount(1);
}
else {
myModuleRootListener.assertNoEvents(true);
}
// don't care if there are references from modules to project SDK or not: change in the "project sdk" always generates events
myModuleRootListener.assertEventsCount(1);
final ModifiableRootModel rootModelA = ModuleRootManager.getInstance(moduleA).getModifiableModel();
final ModifiableRootModel rootModelB = ModuleRootManager.getInstance(moduleB).getModifiableModel();

View File

@@ -187,15 +187,13 @@ open class ProjectRootManagerComponent(
}
AdditionalLibraryRootsProvider.EP_NAME.addChangeListener(coroutineScope, rootsExtensionPointListener)
OrderEnumerationHandler.EP_NAME.addChangeListener(coroutineScope, rootsExtensionPointListener)
if (useWsm) {
connection.subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener {
override fun changed(event: VersionedStorageChange) {
if (event.getChanges(ProjectSettingsEntity::class.java).isNotEmpty()) {
projectJdkChanged()
}
connection.subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener {
override fun changed(event: VersionedStorageChange) {
if (event.getChanges(ProjectSettingsEntity::class.java).isNotEmpty()) {
projectJdkChanged()
}
})
}
}
})
}
protected open fun projectClosed() {

View File

@@ -4,7 +4,6 @@ package com.intellij.util.indexing;
import com.intellij.openapi.extensions.ExtensionPointListener;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.platform.workspace.jps.entities.*;
import com.intellij.platform.workspace.storage.WorkspaceEntity;
import com.intellij.util.containers.ContainerUtil;
@@ -137,7 +136,7 @@ final class CustomEntitiesCausingReindexTracker {
} else if (entity instanceof SdkEntity) {
return hasDependencyOn((SdkEntity) entity, project);
}
else if (entity instanceof ProjectSettingsEntity && Registry.is("project.root.manager.over.wsm", true)) {
else if (entity instanceof ProjectSettingsEntity) {
return true; // don't care if there are references from modules or not: project sdk is always indexed
}
return isEntityToRescan(entity);

View File

@@ -52,19 +52,16 @@ public final class ModuleDependencyEntitiesIndexableEntityProvider implements In
}
private static @NotNull Collection<? extends IndexableIteratorBuilder> createIteratorBuildersForDependency(@NotNull ModuleDependencyItem dependency) {
if (Registry.is("use.workspace.file.index.for.partial.scanning")) return Collections.emptyList();
if (dependency instanceof SdkDependency) {
if (Registry.is("use.workspace.file.index.for.partial.scanning")) return Collections.emptyList();
return Collections.singletonList(IndexableIteratorBuilders.INSTANCE.forSdk(((SdkDependency)dependency).getSdk().getName(),
((SdkDependency)dependency).getSdk().getType()));
}
else if (dependency instanceof LibraryDependency) {
if (Registry.is("use.workspace.file.index.for.partial.scanning")) return Collections.emptyList();
LibraryId libraryId = ((LibraryDependency)dependency).getLibrary();
return IndexableIteratorBuilders.INSTANCE.forLibraryEntity(libraryId, true);
}
else if (dependency instanceof InheritedSdkDependency) {
if (Registry.is("use.workspace.file.index.for.partial.scanning") &&
Registry.is("project.root.manager.over.wsm")) return Collections.emptyList();
return IndexableIteratorBuilders.INSTANCE.forInheritedSdk();
}
return Collections.emptyList();

View File

@@ -29,17 +29,6 @@ class ProjectRootManagerBridge(project: Project, coroutineScope: CoroutineScope)
private val moduleDependencyIndex
get() = ModuleDependencyIndex.getInstance(project)
override val actionToRunWhenProjectJdkChanges: Runnable
get() {
return Runnable {
super.actionToRunWhenProjectJdkChanges.run()
if (!useWsm && moduleDependencyIndex.hasProjectSdkDependency()) {
val info = BuildableRootsChangeRescanningInfo.newInstance().addInheritedSdk().buildInfo()
fireRootsChanged(info)
}
}
}
override fun getOrderRootsCache(project: Project): OrderRootsCache {
return OrderRootsCacheBridge(project, project)
}

View File

@@ -1813,8 +1813,6 @@
<backgroundPostStartupActivity implementation="com.intellij.openapi.vfs.newvfs.monitoring.VFSInitializationConditionsToFusReporter"/>
<initProjectActivity implementation="com.intellij.openapi.roots.impl.ProjectRootManagerInitProjectActivity"/>
<idePerformanceListener implementation="com.intellij.diagnostic.FusFreezeReporter"/>
<backgroundPostStartupActivity implementation="com.intellij.internal.DumpPluginDescriptorsOnProjectOpenTrigger"/>

View File

@@ -125,8 +125,6 @@
<registryKey key="unknown.sdk.show.editor.actions" defaultValue="true" description="Show editor suggestions to fix missing SKDs"/>
<registryKey key="psi.vfs.listener.over.wsm" defaultValue="true" restartRequired="true"
description="Use WSM listeners to invalidate PSI caches (new) in addition to legacy ModuleRootListener"/>
<registryKey key="project.root.manager.over.wsm" defaultValue="true" restartRequired="true"
description="Use WSM to hold project settings (java language level, project sdk, compiler outputs)"/>
<editorNotificationProvider implementation="com.intellij.openapi.projectRoots.impl.UnknownSdkEditorNotificationsProvider"/>
</extensions>

View File

@@ -14,7 +14,6 @@ import com.intellij.openapi.roots.impl.ProjectFileIndexScopes.IN_LIBRARY
import com.intellij.openapi.roots.impl.ProjectFileIndexScopes.IN_SOURCE
import com.intellij.openapi.roots.impl.ProjectFileIndexScopes.NOT_IN_PROJECT
import com.intellij.openapi.roots.impl.ProjectFileIndexScopes.assertScope
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.testFramework.assertion.collectionAssertion.CollectionAssertions.assertEmpty
import com.intellij.platform.testFramework.assertion.collectionAssertion.CollectionAssertions.assertEqualsUnordered
@@ -31,8 +30,7 @@ import kotlin.test.assertEquals
@RunInEdt(writeIntent = true)
class SdkInProjectFileIndexTest {
private val useWsmForProjectSdk = Registry.`is`("project.root.manager.over.wsm")
private val unreferencedProjectSdkScope = if (useWsmForProjectSdk) IN_LIBRARY else NOT_IN_PROJECT
private val unreferencedProjectSdkScope = IN_LIBRARY
@JvmField
@RegisterExtension

View File

@@ -18,11 +18,4 @@ import org.jetbrains.annotations.Nullable;
public abstract class ProjectExtension {
public void projectSdkChanged(@Nullable Sdk sdk) {
}
/**
* Returns true if the state was changed after read
*/
public abstract boolean readExternalElement(@NotNull Element element);
public abstract void writeExternal(@NotNull Element element);
}

View File

@@ -1,12 +1,10 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.roots.impl
import com.intellij.openapi.application.*
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.HandledByWSM
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.State
import com.intellij.openapi.components.serviceAsync
import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.ProjectExtensionPointName
import com.intellij.openapi.module.Module
@@ -18,9 +16,6 @@ import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
import com.intellij.openapi.startup.InitProjectActivity
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener
@@ -36,9 +31,6 @@ import com.intellij.workspaceModel.ide.WsmProjectSettingsEntityUtils
import com.intellij.workspaceModel.ide.WsmSingletonEntityUtils
import com.intellij.workspaceModel.ide.impl.legacyBridge.module.roots.ModuleRootComponentBridge
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jdom.Element
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
@@ -47,9 +39,6 @@ import java.util.concurrent.ConcurrentHashMap
private val LOG = logger<ProjectRootManagerImpl>()
private val EP_NAME = ProjectExtensionPointName<ProjectExtension>("com.intellij.projectExtension")
private const val PROJECT_JDK_NAME_ATTR = "project-jdk-name"
private const val PROJECT_JDK_TYPE_ATTR = "project-jdk-type"
private const val ATTRIBUTE_VERSION = "version"
@State(name = "ProjectRootManager")
@ApiStatus.Internal
@@ -58,119 +47,9 @@ open class ProjectRootManagerImpl(
@JvmField protected val coroutineScope: CoroutineScope,
) : ProjectRootManagerEx(), PersistentStateComponent<Element> {
private class ProjectRootManagerStateComponentImpl(
private val notifyAll: () -> Unit,
private val notifyExtensionsOnly: suspend (Sdk?) -> Unit,
) {
var projectSdkName: String? = null
var projectSdkType: String? = null
private var isStateLoaded = false
@Volatile
var shouldFireRootsChanged: Ref<Boolean>? = null
fun loadState(element: Element, project: Project, coroutineScope: CoroutineScope) {
LOG.debug("Loading state into element")
var stateChanged = false
for (extension in EP_NAME.getExtensions(project)) {
stateChanged = stateChanged or extension.readExternalElement(element)
}
val oldSdkName = projectSdkName
val oldSdkType = projectSdkType
projectSdkName = element.getAttributeValue(PROJECT_JDK_NAME_ATTR)
projectSdkType = element.getAttributeValue(PROJECT_JDK_TYPE_ATTR)
if (oldSdkName != projectSdkName) stateChanged = true
if (oldSdkType != projectSdkType) stateChanged = true
val app = ApplicationManager.getApplication()
LOG.debug { "ProjectRootManagerImpl state was changed: $stateChanged" }
if (app != null) {
val isStateLoaded = isStateLoaded
if (stateChanged) {
if (!project.isInitialized) {
shouldFireRootsChanged = Ref.create(isStateLoaded)
}
else {
coroutineScope.launch {
// make sure we execute it only after any current modality dialog
withContext(Dispatchers.EDT + ModalityState.nonModal().asContextElement()) {
}
applyState(isStateLoaded)
}
}
}
}
isStateLoaded = true
}
fun noStateLoaded() {
isStateLoaded = true
}
fun getState(project: Project): Element {
val element = Element("state")
element.setAttribute(ATTRIBUTE_VERSION, "2")
for (extension in EP_NAME.getExtensions(project)) {
extension.writeExternal(element)
}
if (projectSdkName != null) {
element.setAttribute(PROJECT_JDK_NAME_ATTR, projectSdkName)
}
if (projectSdkType != null) {
element.setAttribute(PROJECT_JDK_TYPE_ATTR, projectSdkType)
}
if (element.attributes.size == 1) {
// remove an empty element to not write defaults
element.removeAttribute(ATTRIBUTE_VERSION)
}
return element
}
suspend fun applyState(isStateLoaded: Boolean) {
if (isStateLoaded) {
LOG.debug("Run write action for projectJdkChanged()")
backgroundWriteAction {
notifyAll()
}
return
}
// prevent root changed event during startup to improve startup performance
val projectSdkName = projectSdkName
val sdk = if (projectSdkName == null) {
null
}
else {
val projectJdkTable = serviceAsync<ProjectJdkTable>()
readActionBlocking {
if (projectSdkType == null) {
projectJdkTable.findJdk(projectSdkName)
}
else {
projectJdkTable.findJdk(projectSdkName, projectSdkType!!)
}
}
}
notifyExtensionsOnly(sdk)
}
suspend fun initProjectActivity(){
val oldShouldFireRootsChanged = shouldFireRootsChanged?.get() ?: return
shouldFireRootsChanged = null
applyState(oldShouldFireRootsChanged)
}
}
private val projectJdkEventDispatcher = EventDispatcher.create(ProjectJdkListener::class.java)
private val moduleRootManagerInstances = ConcurrentHashMap<Module, ModuleRootManager>()
// This field is not used when useWsm set to `true`
private val stateComponent = ProjectRootManagerStateComponentImpl(this::projectJdkChanged, this::notifyExtensions)
protected val useWsm: Boolean = Registry.`is`("project.root.manager.over.wsm", true)
private val rootCache: OrderRootsCache
init {
@@ -181,17 +60,11 @@ open class ProjectRootManagerImpl(
val currentName = projectSdkName
if (previousName == currentName) {
// if already had jdk name and that name was the name of the jdk just changed
if (useWsm) {
project.workspaceModel.updateProjectModel("jdkNameChanged") { mutableStorage ->
WsmProjectSettingsEntityUtils.addOrModifyProjectSettingsEntity(project, mutableStorage) { entity ->
entity.projectSdk = SdkId(jdk.getName(), jdk.getSdkType().getName())
}
project.workspaceModel.updateProjectModel("jdkNameChanged") { mutableStorage ->
WsmProjectSettingsEntityUtils.addOrModifyProjectSettingsEntity(project, mutableStorage) { entity ->
entity.projectSdk = SdkId(jdk.getName(), jdk.getSdkType().getName())
}
}
else {
stateComponent.projectSdkName = jdk.getName()
stateComponent.projectSdkType = jdk.getSdkType().getName()
}
}
}
})
@@ -416,24 +289,14 @@ open class ProjectRootManagerImpl(
@ApiStatus.Internal
override fun getProjectSdkName(): String? {
return if (useWsm) {
val settings = WsmSingletonEntityUtils.getSingleEntity(project.workspaceModel.currentSnapshot, ProjectSettingsEntity::class.java)
settings?.projectSdk?.name
}
else {
stateComponent.projectSdkName
}
val settings = WsmSingletonEntityUtils.getSingleEntity(project.workspaceModel.currentSnapshot, ProjectSettingsEntity::class.java)
return settings?.projectSdk?.name
}
@ApiStatus.Internal
override fun getProjectSdkTypeName(): String? {
return if (useWsm) {
val settings = WsmSingletonEntityUtils.getSingleEntity(project.workspaceModel.currentSnapshot, ProjectSettingsEntity::class.java)
settings?.projectSdk?.type
}
else {
stateComponent.projectSdkType
}
val settings = WsmSingletonEntityUtils.getSingleEntity(project.workspaceModel.currentSnapshot, ProjectSettingsEntity::class.java)
return settings?.projectSdk?.type
}
@ApiStatus.Internal
@@ -449,14 +312,9 @@ open class ProjectRootManagerImpl(
fun projectJdkChanged() {
incModificationCount()
if (useWsm) {
// There is no mergeRootsChangesDuring because currently it has a bug: "after" event will never fire if mergeRootsChangesDuring
// is invoked while another rootsChange event (caused by the WSM change) is in progress (see RootsChangedTest).
actionToRunWhenProjectJdkChanges.run()
}
else {
mergeRootsChangesDuring(actionToRunWhenProjectJdkChanges)
}
// There is no mergeRootsChangesDuring because currently it has a bug: "after" event will never fire if mergeRootsChangesDuring
// is invoked while another rootsChange event (caused by the WSM change) is in progress (see RootsChangedTest).
actionToRunWhenProjectJdkChanges.run()
fireJdkChanged()
}
@@ -467,16 +325,6 @@ open class ProjectRootManagerImpl(
}
}
private suspend fun notifyExtensions(sdk: Sdk?){
LOG.debug("Run write action for extension.projectSdkChanged(sdk)")
val extensions = EP_NAME.getExtensions(project)
backgroundWriteAction {
for (extension in extensions) {
extension.projectSdkChanged(sdk)
}
}
}
@get:ApiStatus.Internal
protected open val actionToRunWhenProjectJdkChanges: Runnable
get() = Runnable { projectJdkEventDispatcher.getMulticaster().projectJdkChanged() }
@@ -488,21 +336,12 @@ open class ProjectRootManagerImpl(
}
private fun setOrClearProjectSdkName(name: String?, sdkTypeName: String?) {
if (useWsm) {
ThreadingAssertions.assertWriteAccess()
val newSdk = if (name != null && sdkTypeName != null) SdkId(name, sdkTypeName) else null
project.workspaceModel.updateProjectModel("setOrClearProjectSdkName") { mutableStorage ->
WsmProjectSettingsEntityUtils.addOrModifyProjectSettingsEntity(project, mutableStorage) { entity ->
entity.projectSdk = newSdk
}
ThreadingAssertions.assertWriteAccess()
val newSdk = if (name != null && sdkTypeName != null) SdkId(name, sdkTypeName) else null
project.workspaceModel.updateProjectModel("setOrClearProjectSdkName") { mutableStorage ->
WsmProjectSettingsEntityUtils.addOrModifyProjectSettingsEntity(project, mutableStorage) { entity ->
entity.projectSdk = newSdk
}
// projectJdkChanged will be invoked via WSM change listener
}
else {
LOG.assertTrue((name == null) == (sdkTypeName == null), "Sdk name and type should both be null or not-null")
stateComponent.projectSdkName = name
stateComponent.projectSdkType = sdkTypeName
projectJdkChanged()
}
}
@@ -514,30 +353,6 @@ open class ProjectRootManagerImpl(
projectJdkEventDispatcher.removeListener(listener)
}
@ApiStatus.Internal
override fun loadState(element: Element) {
if (!useWsm) {
stateComponent.loadState(element, project, coroutineScope)
}
}
@ApiStatus.Internal
override fun noStateLoaded() {
if (!useWsm) {
stateComponent.noStateLoaded()
}
}
@ApiStatus.Internal
override fun getState(): Element? {
return if (useWsm) {
HandledByWSM
}
else {
stateComponent.getState(project)
}
}
@ApiStatus.Internal
override fun mergeRootsChangesDuring(runnable: Runnable) {
ApplicationManager.getApplication().assertWriteAccessAllowed()
@@ -606,6 +421,15 @@ open class ProjectRootManagerImpl(
return moduleRootManagerInstances.computeIfAbsent(module) { ModuleRootComponentBridge(module) }
}
@ApiStatus.Internal
override fun loadState(element: Element) {
}
@ApiStatus.Internal
override fun getState(): Element? {
return HandledByWSM
}
@ApiStatus.Internal
var isFiringEvent: Boolean = false
protected set
@@ -645,17 +469,4 @@ open class ProjectRootManagerImpl(
@ApiStatus.Internal
override fun markRootsForRefresh(): List<VirtualFile> = emptyList()
suspend fun initProjectActivity() {
if (!useWsm) {
stateComponent.initProjectActivity()
}
}
}
private class ProjectRootManagerInitProjectActivity : InitProjectActivity {
override suspend fun run(project: Project) {
val projectRootManager = project.serviceAsync<ProjectRootManager>() as? ProjectRootManagerImpl ?: return
projectRootManager.initProjectActivity()
}
}

View File

@@ -3,7 +3,6 @@ package com.intellij.workspaceModel.core.fileIndex.impl
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.util.registry.Registry
import com.intellij.platform.workspace.jps.entities.ProjectSettingsEntity
import com.intellij.platform.workspace.jps.entities.SdkEntity
import com.intellij.platform.workspace.jps.entities.SdkId
@@ -14,8 +13,6 @@ import com.intellij.workspaceModel.ide.impl.legacyBridge.sdk.customName
class SdkEntityFileIndexContributor : WorkspaceFileIndexContributor<SdkEntity>, PlatformInternalWorkspaceFileIndexContributor {
private val useWsmForProjectSdk: Boolean = Registry.`is`("project.root.manager.over.wsm", true)
override val entityClass: Class<SdkEntity>
get() = SdkEntity::class.java
@@ -23,7 +20,7 @@ class SdkEntityFileIndexContributor : WorkspaceFileIndexContributor<SdkEntity>,
val compiledRootsData: WorkspaceFileSetData
val sourceRootFileSetData: WorkspaceFileSetData
if (useWsmForProjectSdk && isProjectSdk(entity, storage)) {
if (isProjectSdk(entity, storage)) {
compiledRootsData = SdkRootFileSetData(entity.symbolicId)
sourceRootFileSetData = SdkSourceRootFileSetData(entity.symbolicId)
}

View File

@@ -2,8 +2,6 @@
package com.intellij.platform.workspace.jps.serialization.impl
import com.intellij.java.workspace.entities.JavaProjectSettingsEntity
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.util.registry.Registry
import com.intellij.platform.diagnostic.telemetry.helpers.MillisecondsMeasurer
import com.intellij.platform.workspace.jps.JpsFileEntitySource
import com.intellij.platform.workspace.jps.entities.ProjectSettingsEntity
@@ -51,9 +49,6 @@ class ProjectSettingsSerializer(
errorReporter: ErrorReporter,
virtualFileManager: VirtualFileUrlManager,
): LoadingResult<Map<Class<out WorkspaceEntity>, Collection<WorkspaceEntity.Builder<out WorkspaceEntity>>>> = loadEntitiesTimeMs.addMeasuredTime {
if (!Registry.`is`("project.root.manager.over.wsm", true)) {
return@addMeasuredTime LoadingResult(emptyMap())
}
val projectRootManager = runCatchingXmlIssues { reader.loadComponent(fileUrl.url, TAG_PROJECT_ROOT_MANAGER) }
.getOrElse { return@addMeasuredTime LoadingResult(emptyMap(), it) }
@@ -102,13 +97,6 @@ class ProjectSettingsSerializer(
storage: EntityStorage,
writer: JpsFileContentWriter,
): Unit = saveEntitiesTimeMs.addMeasuredTime {
if (!Registry.`is`("project.root.manager.over.wsm", true)) {
thisLogger().error( // severity error because we don't expect these entities to exist in the first place
"ProjectSettingsEntity and JavaProjectSettingsEntity will not be saved, because registry flag " +
"`project.root.manager.over.wsm` is false"
)
return@addMeasuredTime
}
val projectSettingsEntity = mainEntities.firstOrNull()
if (projectSettingsEntity == null) return@addMeasuredTime

View File

@@ -114,10 +114,6 @@ class JpsProjectSaveAllEntitiesTest {
projectData.serializers.saveAllEntities(projectData.storage, projectData.configLocation)
val componentsToIgnore = mutableListOf("CompilerConfiguration", "Encoding")
val filesToIgnore = mutableSetOf(".idea/encodings.xml", ".idea/compiler.xml", ".idea/.name")
if (!Registry.`is`("project.root.manager.over.wsm")) {
componentsToIgnore.add("ProjectRootManager")
filesToIgnore.add(".idea/misc.xml")
}
assertDirectoryMatches(projectData.projectDir, projectData.originalProjectDir,
filesToIgnore,
componentsToIgnore)