diff --git a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/ChangesViewSelection.kt b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/ChangesViewSelection.kt index 2a329027ec2d..757dde15f426 100644 --- a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/ChangesViewSelection.kt +++ b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/changes/ChangesViewSelection.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.vcs.changes.LocalChangeList import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode import com.intellij.openapi.vcs.changes.ui.ChangesTree import com.intellij.openapi.vcs.changes.ui.VcsTreeModelData +import com.intellij.platform.vcs.impl.shared.rpc.ChangeId import com.intellij.platform.vcs.impl.shared.rpc.FilePathDto import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.containers.JBIterable @@ -14,6 +15,9 @@ import kotlinx.serialization.Serializable import org.jetbrains.annotations.ApiStatus import javax.swing.tree.TreePath +/** + * Note that only changes from change lists can be currently restored + */ @ApiStatus.Internal @Serializable data class ChangesViewSelection( @@ -28,7 +32,7 @@ data class ChangesViewSelection( @Serializable data class PathSelection( val filePath: FilePathDto, - val isChangeNode: Boolean, + val changeId: ChangeId?, val exactlySelected: Boolean, ) @@ -50,7 +54,7 @@ data class ChangesViewSelection( val exactlySelected = tree.isPathSelected(TreePath(node.path)) selectedPaths.add(PathSelection( filePath = convertToDto(filePath), - isChangeNode = userObject is Change, + changeId = (userObject as? Change)?.let { ChangeId.getId(it) }, exactlySelected = exactlySelected, )) } diff --git a/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/rpc/ChangeListChangeId.kt b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/rpc/ChangeListChangeId.kt new file mode 100644 index 000000000000..07625faca15e --- /dev/null +++ b/platform/vcs-impl/shared/src/com/intellij/platform/vcs/impl/shared/rpc/ChangeListChangeId.kt @@ -0,0 +1,40 @@ +// 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.platform.vcs.impl.shared.rpc + +import com.intellij.openapi.vcs.changes.Change +import com.intellij.openapi.vcs.changes.ChangeListChange +import com.intellij.platform.vcs.changes.ChangesUtil +import kotlinx.serialization.Serializable +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.NonNls +import org.jetbrains.annotations.SystemIndependent + +@Serializable +@ApiStatus.Internal +sealed class ChangeId { + /** + * Path returned by [ChangesUtil.getFilePath] + */ + abstract val filePath: @SystemIndependent String + + companion object { + fun getId(change: Change): ChangeId { + val filePath = ChangesUtil.getFilePath(change).path + if (change is ChangeListChange) { + return ChangeListChangeId(change.changeListId, filePath) + } + + return NonChangeListChangeId(filePath) + } + } + +} + +@Serializable +private data class NonChangeListChangeId(override val filePath: @SystemIndependent String) : ChangeId() + +@Serializable +private data class ChangeListChangeId( + val changeListId: @NonNls String, + override val filePath: @SystemIndependent String, +) : ChangeId() \ No newline at end of file diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java index d6efdfe3a223..9da64a33cd21 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java @@ -20,6 +20,7 @@ import com.intellij.util.ThreeState; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FactoryMap; import com.intellij.util.containers.MultiMap; +import com.intellij.vcs.changes.ChangeListChangeIdCache; import com.intellij.vcsUtil.VcsUtil; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.jetbrains.annotations.*; @@ -856,11 +857,7 @@ public final class ChangeListWorker { private LocalChangeListImpl toChangeList(@Nullable ListData data) { if (data == null) return null; - if (myReadOnlyChangesCache == null || myReadOnlyChangesCacheInvalidated.get()) { - myReadOnlyChangesCacheInvalidated.set(false); - myReadOnlyChangesCache = getChangesMapping(); - } - Set cachedChanges = myReadOnlyChangesCache.get(data); + Set cachedChanges = getCache().get(data); Set changes = cachedChanges != null ? Collections.unmodifiableSet(cachedChanges) : Collections.emptySet(); return buildChangeListFrom(data) @@ -868,6 +865,19 @@ public final class ChangeListWorker { .build(); } + private @NotNull Map> getCache() { + if (myReadOnlyChangesCache == null || myReadOnlyChangesCacheInvalidated.get()) { + myReadOnlyChangesCacheInvalidated.set(false); + Map> mapping = getChangesMapping(); + myReadOnlyChangesCache = mapping; + if (myMainWorker) { + ChangeListChangeIdCache.getInstance(myProject).updateCache(mapping.values()); + } + } + + return myReadOnlyChangesCache; + } + /** * Unlike {@link #toChangeList(ListData)}, will not populate {@link LocalChangeList#getChanges()}. */ diff --git a/platform/vcs-impl/src/com/intellij/vcs/changes/ChangeListChangeIdCache.kt b/platform/vcs-impl/src/com/intellij/vcs/changes/ChangeListChangeIdCache.kt new file mode 100644 index 000000000000..9d0d7cff5a3d --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/vcs/changes/ChangeListChangeIdCache.kt @@ -0,0 +1,27 @@ +// 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.vcs.changes + +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import com.intellij.openapi.vcs.changes.Change +import com.intellij.platform.vcs.impl.shared.rpc.ChangeId +import org.jetbrains.annotations.ApiStatus + +@Service(Service.Level.PROJECT) +@ApiStatus.Internal +class ChangeListChangeIdCache { + @Volatile + private var cachedChanges: Map = emptyMap() + + fun getChange(id: ChangeId): Change? = cachedChanges[id] + + fun updateCache(allChanges: Iterable>) { + cachedChanges = allChanges.asSequence().flatten().associateBy { ChangeId.getId(it) } + } + + companion object { + @JvmStatic + fun getInstance(project: Project): ChangeListChangeIdCache = project.service() + } +} \ No newline at end of file