fix(file-tree): invalidate both cached target and ancestor, skip collapsed refetches
When a deep directory was renamed or deleted externally and both the directory itself and a parent directory were cached, only the self entry was cleared — leaving the parent's children list holding a ghost reference to the old child. Walk up to the nearest cached ancestor in addition to the direct hit so both stale entries are dropped together. Also gate the follow-up getFileTree refetch on the directory still being expanded. Collapsed branches only need their cache cleared; they will re-hydrate naturally on the next expansion, which avoids unnecessary IPC traffic during FS event bursts.
This commit is contained in:
@@ -1107,19 +1107,26 @@ export function FileTreeTab() {
|
||||
for (const changed of paths) {
|
||||
const normalized = normalizeComparePath(changed)
|
||||
if (!normalized) continue
|
||||
// Walk from the changed path up to the root, invalidating the
|
||||
// nearest cached ancestor (or the path itself if a directory of
|
||||
// that name is in the cache). Checking the path itself covers
|
||||
// FS events that report the directory path directly.
|
||||
// When the changed path is itself a cached directory (FS events
|
||||
// that report the directory directly, e.g. a rename or a dir-level
|
||||
// notification), its own entry is stale — invalidate it.
|
||||
if (cache.has(normalized)) {
|
||||
invalidated.add(normalized)
|
||||
}
|
||||
// Independently of the above, walk up to the nearest cached
|
||||
// ancestor: the ancestor's children listing may also be stale
|
||||
// (a child was added, removed, or renamed). Without this, cases
|
||||
// where both a parent and child are cached leave the parent
|
||||
// holding a ghost reference to the old child.
|
||||
let cursor = normalized
|
||||
while (cursor.length > 0) {
|
||||
if (cache.has(cursor)) {
|
||||
invalidated.add(cursor)
|
||||
break
|
||||
}
|
||||
const slash = cursor.lastIndexOf("/")
|
||||
const parent = slash === -1 ? "" : cursor.slice(0, slash)
|
||||
if (parent.length === 0) break
|
||||
if (cache.has(parent)) {
|
||||
invalidated.add(parent)
|
||||
break
|
||||
}
|
||||
cursor = parent
|
||||
}
|
||||
}
|
||||
@@ -1129,7 +1136,13 @@ export function FileTreeTab() {
|
||||
cache.delete(path)
|
||||
}
|
||||
if (!loader) return
|
||||
// Skip refetching directories that are no longer expanded — their
|
||||
// cleared cache will be re-hydrated on the next expansion via the
|
||||
// expandedPaths effect. This avoids spurious getFileTree traffic
|
||||
// for collapsed branches under bursty FS activity.
|
||||
const expanded = expandedPathsRef.current
|
||||
for (const path of invalidated) {
|
||||
if (!expanded.has(path)) continue
|
||||
void loader(path)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user