[vcs] IJPL-173924 Restore Change as ChangeListChange if possible

GitOrigin-RevId: 960ff38ff7d274ba06fff97de4cf25fea3b6c002
This commit is contained in:
Ilia.Shulgin
2025-10-07 18:29:33 +00:00
committed by intellij-monorepo-bot
parent 39b6deb709
commit f0b97cbcd3
4 changed files with 88 additions and 7 deletions
@@ -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,
))
}
@@ -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()
@@ -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<Change> cachedChanges = myReadOnlyChangesCache.get(data);
Set<Change> cachedChanges = getCache().get(data);
Set<Change> changes = cachedChanges != null ? Collections.unmodifiableSet(cachedChanges) : Collections.emptySet();
return buildChangeListFrom(data)
@@ -868,6 +865,19 @@ public final class ChangeListWorker {
.build();
}
private @NotNull Map<ListData, Set<Change>> getCache() {
if (myReadOnlyChangesCache == null || myReadOnlyChangesCacheInvalidated.get()) {
myReadOnlyChangesCacheInvalidated.set(false);
Map<ListData, Set<Change>> mapping = getChangesMapping();
myReadOnlyChangesCache = mapping;
if (myMainWorker) {
ChangeListChangeIdCache.getInstance(myProject).updateCache(mapping.values());
}
}
return myReadOnlyChangesCache;
}
/**
* Unlike {@link #toChangeList(ListData)}, will not populate {@link LocalChangeList#getChanges()}.
*/
@@ -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<ChangeId, Change> = emptyMap()
fun getChange(id: ChangeId): Change? = cachedChanges[id]
fun updateCache(allChanges: Iterable<Set<Change>>) {
cachedChanges = allChanges.asSequence().flatten().associateBy { ChangeId.getId(it) }
}
companion object {
@JvmStatic
fun getInstance(project: Project): ChangeListChangeIdCache = project.service()
}
}