[external system][IDEA-306074] Store an external system project classpath in the workspace model

GitOrigin-RevId: 0bb72bf9b9a0e3a525fa3906a42d553db33d28a0
This commit is contained in:
Alexander.Glukhov
2024-03-19 17:00:40 +01:00
committed by intellij-monorepo-bot
parent df9f564369
commit d023e045e7
5 changed files with 182 additions and 11 deletions

View File

@@ -5,6 +5,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
@@ -24,5 +25,6 @@
<orderEntry type="module" module-name="intellij.platform.core.ui" />
<orderEntry type="module" module-name="intellij.platform.backend.observation" />
<orderEntry type="module" module-name="intellij.platform.workspace.storage" />
<orderEntry type="module" module-name="intellij.platform.backend.workspace" />
</component>
</module>

View File

@@ -227,3 +227,6 @@ notification.group.orphan.modules=Modules no longer referenced by external syste
notification.group.duplicate.content.roots=Duplicate content roots detected
notification.group.unindexed.repositories=Unindexed Maven repositories found
notification.group.gradle.wrapper.update=Gradle wrapper update failed
external.system.local.settings.workspace.model.project.update=Project settings update in progress
external.system.local.settings.workspace.model.project.unlink.title=The project has been unlinked. Saving changes

View File

@@ -1,23 +1,38 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.externalSystem.settings;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.externalSystem.ExternalSystemManager;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo;
import com.intellij.openapi.externalSystem.model.project.ExternalProjectBuildClasspathPojo;
import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
import com.intellij.openapi.externalSystem.settings.workspaceModel.ExternalProjectBuildClasspathEntity;
import com.intellij.openapi.externalSystem.settings.workspaceModel.ExternalProjectsBuildClasspathEntity;
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.platform.backend.workspace.WorkspaceModel;
import com.intellij.platform.workspace.storage.MutableEntityStorage;
import com.intellij.util.SmartList;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.containers.ContainerUtil;
import kotlin.sequences.Sequence;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Consumer;
import static com.intellij.openapi.externalSystem.settings.workspaceModel.EntitiesKt.modifyEntity;
import static com.intellij.openapi.externalSystem.settings.workspaceModel.MappersKt.getExternalProjectBuildClasspathPojo;
import static com.intellij.openapi.externalSystem.settings.workspaceModel.MappersKt.getExternalProjectsBuildClasspathEntity;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.*;
/**
@@ -71,14 +86,7 @@ public abstract class AbstractExternalSystemLocalSettings<S extends AbstractExte
}
}
for (Iterator<Map.Entry<String, ExternalProjectBuildClasspathPojo>> it = state.projectBuildClasspath.entrySet().iterator();
it.hasNext(); ) {
Map.Entry<String, ExternalProjectBuildClasspathPojo> entry = it.next();
if (linkedProjectPathsToForget.contains(entry.getKey())
|| linkedProjectPathsToForget.contains(getRootProjectPath(entry.getKey(), myExternalSystemId, myProject))) {
it.remove();
}
}
forgetExternalProjectsClasspath(myExternalSystemId, myProject, linkedProjectPathsToForget);
for (Iterator<Map.Entry<String, SyncType>> it = state.projectSyncType.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, SyncType> entry = it.next();
@@ -115,7 +123,7 @@ public abstract class AbstractExternalSystemLocalSettings<S extends AbstractExte
@NotNull
public Map<String, ExternalProjectBuildClasspathPojo> getProjectBuildClasspath() {
return state.projectBuildClasspath;
return getExternalProjectsBuildClasspath(myProject);
}
@NotNull
@@ -129,6 +137,8 @@ public abstract class AbstractExternalSystemLocalSettings<S extends AbstractExte
}
public void loadState(@NotNull State state) {
saveProjectBuildClasspathWorkspaceEntity(myProject, state.projectBuildClasspath);
state.projectBuildClasspath = Collections.emptyMap();
//noinspection unchecked
this.state = (S)state;
pruneOutdatedEntries();
@@ -160,7 +170,7 @@ public abstract class AbstractExternalSystemLocalSettings<S extends AbstractExte
}
public void setProjectBuildClasspath(Map<String, ExternalProjectBuildClasspathPojo> value) {
state.projectBuildClasspath = value;
saveProjectBuildClasspathWorkspaceEntity(myProject, value);
}
@Deprecated(forRemoval = true)
@@ -168,7 +178,6 @@ public abstract class AbstractExternalSystemLocalSettings<S extends AbstractExte
otherState.recentTasks.clear();
otherState.availableProjects = state.availableProjects;
otherState.modificationStamps = state.modificationStamps;
otherState.projectBuildClasspath = state.projectBuildClasspath;
otherState.projectSyncType = state.projectSyncType;
}
@@ -176,6 +185,7 @@ public abstract class AbstractExternalSystemLocalSettings<S extends AbstractExte
public final List<ExternalTaskExecutionInfo> recentTasks = new SmartList<>();
public Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> availableProjects = CollectionFactory.createSmallMemoryFootprintMap();
public Map<String/* linked project path */, Long/* last config modification stamp */> modificationStamps = CollectionFactory.createSmallMemoryFootprintMap();
@Deprecated(forRemoval = true, since = "24.2")
public Map<String/* linked project path */, ExternalProjectBuildClasspathPojo> projectBuildClasspath = CollectionFactory.createSmallMemoryFootprintMap();
public Map<String/* linked project path */, SyncType> projectSyncType = CollectionFactory.createSmallMemoryFootprintMap();
}
@@ -183,4 +193,90 @@ public abstract class AbstractExternalSystemLocalSettings<S extends AbstractExte
public enum SyncType {
PREVIEW, IMPORT, RE_IMPORT
}
private static void forgetExternalProjectsClasspath(@NotNull ProjectSystemId myExternalSystemId,
@NotNull Project project,
@NotNull Set<String> linkedProjectPathsToForget) {
modifyWorkspaceModel(
project,
ExternalSystemBundle.message("external.system.local.settings.workspace.model.project.unlink.title"),
"ForgetExternalProjects update",
storage -> {
Sequence<ExternalProjectsBuildClasspathEntity> currentEntities = storage.entities(ExternalProjectsBuildClasspathEntity.class);
Iterator<ExternalProjectsBuildClasspathEntity> entityIterator = currentEntities.iterator();
if (!entityIterator.hasNext()) {
return;
}
modifyEntity(storage, entityIterator.next(), modifiableEntity -> {
Map<String, ExternalProjectBuildClasspathEntity> classpath = modifiableEntity.getProjectsBuildClasspath();
Iterator<Map.Entry<String, ExternalProjectBuildClasspathEntity>> classpathIterator = classpath.entrySet().iterator();
while (classpathIterator.hasNext()) {
Map.Entry<String, ExternalProjectBuildClasspathEntity> entry = classpathIterator.next();
if (linkedProjectPathsToForget.contains(entry.getKey()) ||
linkedProjectPathsToForget.contains(getRootProjectPath(entry.getKey(), myExternalSystemId, project))) {
classpathIterator.remove();
}
}
return null;
});
});
}
private static void saveProjectBuildClasspathWorkspaceEntity(
@NotNull Project project,
@NotNull Map<String, ExternalProjectBuildClasspathPojo> projectBuildClasspath
) {
if (projectBuildClasspath.isEmpty()) {
return;
}
modifyWorkspaceModel(
project,
ExternalSystemBundle.message("external.system.local.settings.workspace.model.project.update"),
"AbstractExternalSystemLocalSettings update",
storage -> {
Sequence<ExternalProjectsBuildClasspathEntity> entities = storage.entities(ExternalProjectsBuildClasspathEntity.class);
Iterator<ExternalProjectsBuildClasspathEntity> entityIterator = entities.iterator();
ExternalProjectsBuildClasspathEntity newClasspathEntity = getExternalProjectsBuildClasspathEntity(projectBuildClasspath);
if (!entityIterator.hasNext()) {
storage.addEntity(newClasspathEntity);
}
else {
modifyEntity(storage, entityIterator.next(), modifiableEntity -> {
modifiableEntity.setProjectsBuildClasspath(newClasspathEntity.getProjectsBuildClasspath());
return null;
});
}
});
}
private static @NotNull Map<String, ExternalProjectBuildClasspathPojo> getExternalProjectsBuildClasspath(
@NotNull Project project
) {
Sequence<ExternalProjectsBuildClasspathEntity> entities = WorkspaceModel.getInstance(project)
.getCurrentSnapshot()
.entities(ExternalProjectsBuildClasspathEntity.class);
Iterator<ExternalProjectsBuildClasspathEntity> entityIterator = entities.iterator();
if (entityIterator.hasNext()) {
return getExternalProjectBuildClasspathPojo(entityIterator.next());
}
return Collections.emptyMap();
}
private static void modifyWorkspaceModel(@NotNull Project project,
@Nls String message,
@NonNls String cause,
@NotNull Consumer<MutableEntityStorage> mutator) {
new Task.Modal(project, message, true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
WriteAction.runAndWait(() -> {
WorkspaceModel.getInstance(project)
.updateProjectModel(cause, storage -> {
mutator.accept(storage);
return null;
});
});
}
}.queue();
}
}

View File

@@ -0,0 +1,21 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.externalSystem.settings.workspaceModel
import com.intellij.platform.workspace.storage.EntitySource
import com.intellij.platform.workspace.storage.EntityType
import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion
import com.intellij.platform.workspace.storage.MutableEntityStorage
import com.intellij.platform.workspace.storage.WorkspaceEntity
import com.intellij.platform.workspace.storage.impl.containers.toMutableWorkspaceList
object ExternalProjectsBuildClasspathEntitySource: EntitySource
interface ExternalProjectsBuildClasspathEntity : WorkspaceEntity {
val projectsBuildClasspath: Map<String, ExternalProjectBuildClasspathEntity>
}
data class ExternalModuleBuildClasspathEntity(val path: String, val entries: List<String>)
data class ExternalProjectBuildClasspathEntity(val name: String,
val projectBuildClasspath: List<String>,
val moduleBuildClasspath: Map<String, ExternalModuleBuildClasspathEntity>)

View File

@@ -0,0 +1,49 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.externalSystem.settings.workspaceModel
import com.intellij.openapi.externalSystem.model.project.ExternalModuleBuildClasspathPojo
import com.intellij.openapi.externalSystem.model.project.ExternalProjectBuildClasspathPojo
fun getExternalProjectsBuildClasspathEntity(
projectBuildClasspath: Map<String, ExternalProjectBuildClasspathPojo>
): ExternalProjectsBuildClasspathEntity {
val classpathEntityMap = projectBuildClasspath.mapValues { getExternalProjectBuildClasspathEntity(it.value) }
return ExternalProjectsBuildClasspathEntity.invoke(
projectsBuildClasspath = classpathEntityMap,
entitySource = ExternalProjectsBuildClasspathEntitySource
)
}
fun getExternalProjectBuildClasspathPojo(entity: ExternalProjectsBuildClasspathEntity): Map<String, ExternalProjectBuildClasspathPojo> {
return entity.projectsBuildClasspath
.mapValues { getExternalProjectBuildClasspathPojo(it.value) }
}
private fun getExternalProjectBuildClasspathEntity(pojo: ExternalProjectBuildClasspathPojo): ExternalProjectBuildClasspathEntity {
val moduleBuildClasspath = pojo.modulesBuildClasspath
.mapValues {
ExternalModuleBuildClasspathEntity(
path = it.value.path,
entries = it.value.entries
)
}
return ExternalProjectBuildClasspathEntity(
name = pojo.name,
projectBuildClasspath = pojo.projectBuildClasspath,
moduleBuildClasspath = moduleBuildClasspath
)
}
private fun getExternalProjectBuildClasspathPojo(entity: ExternalProjectBuildClasspathEntity): ExternalProjectBuildClasspathPojo {
return ExternalProjectBuildClasspathPojo(
entity.name,
entity.projectBuildClasspath,
entity.moduleBuildClasspath
.mapValues {
ExternalModuleBuildClasspathPojo(
it.value.path,
it.value.entries
)
}
)
}