Reuse Explorer watcher child indexes across unchanged payloads (#35319)

* perf(explorer): reuse watcher child indexes by listing identity

* test(explorer): trim child index reuse tests to reuse and invalidation

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-28 14:29:41 -07:00
committed by GitHub
co-authored by OrcaWin
parent 9015f13735
commit c66d873ff5
2 changed files with 85 additions and 11 deletions
@@ -0,0 +1,73 @@
import { describe, expect, it, vi } from 'vitest'
import type { DirCache, TreeNode } from './file-explorer-types'
import { processFileExplorerFsPayload } from './file-explorer-watch-reconcile'
function reconcile(
cache: Record<string, DirCache>,
absolutePath: string
): { dir: ReturnType<typeof vi.fn>; tree: ReturnType<typeof vi.fn> } {
const dir = vi.fn()
const tree = vi.fn()
processFileExplorerFsPayload({
payload: {
worktreePath: '/repo',
events: [{ kind: 'update', absolutePath, isDirectory: false }]
},
currentWorktreePath: '/repo',
worktreeId: 'wt-1',
cache,
expanded: new Set(),
setDirCache: vi.fn(),
setSelectedPath: vi.fn(),
refreshDir: dir,
refreshTree: tree
})
return { dir, tree }
}
function node(path: string): TreeNode {
return {
path,
name: path,
relativePath: path.slice('/repo/'.length),
isDirectory: false,
isSymlink: false,
depth: 0,
operationOwner: { kind: 'local' }
}
}
describe('watcher sibling index reuse', () => {
it('indexes unchanged siblings once across update payloads', () => {
let reads = 0
const children = Array.from({ length: 1_000 }, (_, index) => {
const path = `/repo/file-${index}.ts`
const child = node(path)
Object.defineProperty(child, 'path', {
get: () => {
reads++
return path
}
})
return child
})
const cache = { '/repo': { children } }
for (let payload = 0; payload < 10; payload++) {
const refresh = reconcile(cache, '/repo/file-0.ts')
expect(refresh.dir).not.toHaveBeenCalled()
expect(refresh.tree).not.toHaveBeenCalled()
}
expect(reads).toBe(1_000)
})
it('reindexes replaced listings while reusing an unchanged array through cache copies', () => {
const children = [node('/repo/old.ts')]
const before = { '/repo': { children } }
expect(reconcile(before, '/repo/old.ts').dir).not.toHaveBeenCalled()
expect(reconcile({ ...before }, '/repo/old.ts').dir).not.toHaveBeenCalled()
const after = { '/repo': { children: [node('/repo/new.ts')] } }
expect(reconcile(after, '/repo/new.ts').dir).not.toHaveBeenCalled()
expect(reconcile(after, '/repo/old.ts').dir).toHaveBeenCalledWith('/repo')
})
})
@@ -31,21 +31,23 @@ export type ProcessFileExplorerFsPayloadArgs = {
refreshTree: () => void
}
const cachedChildPathIndexes = new WeakMap<readonly TreeNode[], ReadonlyMap<string, TreeNode>>()
function cachedDirectoryChild(
cache: Record<string, DirCache>,
cachedDirPath: string,
childPath: string,
childPathIndexes: Map<string, Map<string, TreeNode>>
childPath: string
): TreeNode | undefined {
let childPaths = childPathIndexes.get(cachedDirPath)
const children = cache[cachedDirPath]?.children
if (!children) {
return undefined
}
let childPaths = cachedChildPathIndexes.get(children)
if (!childPaths) {
childPaths = new Map(
cache[cachedDirPath]?.children.map((child) => [
normalizeRuntimePathForComparison(child.path),
child
]) ?? []
children.map((child) => [normalizeRuntimePathForComparison(child.path), child])
)
childPathIndexes.set(cachedDirPath, childPaths)
cachedChildPathIndexes.set(children, childPaths)
}
return childPaths.get(normalizeRuntimePathForComparison(childPath))
}
@@ -83,7 +85,6 @@ export function processFileExplorerFsPayload(args: ProcessFileExplorerFsPayloadA
}
const dirsToRefresh = new Set<string>()
const childPathIndexes = new Map<string, Map<string, TreeNode>>()
let hasLinkedCache: boolean | undefined
let cachedDirKeys: string[] | undefined
let cachedDirPathIndex: ReadonlyMap<string, string> | undefined
@@ -114,7 +115,7 @@ export function processFileExplorerFsPayload(args: ProcessFileExplorerFsPayloadA
const cachedParent = resolveCachedDirPath(cache, parent, currentWorktreePath, cachePathIndex)
const updatedChild =
evt.kind === 'update' && cachedParent
? cachedDirectoryChild(cache, cachedParent, normalizedPath, childPathIndexes)
? cachedDirectoryChild(cache, cachedParent, normalizedPath)
: undefined
const knownFileUpdate =
evt.kind === 'update' &&
@@ -227,7 +228,7 @@ export function processFileExplorerFsPayload(args: ProcessFileExplorerFsPayloadA
if (
cachedParent &&
!dirsToRefresh.has(cachedParent) &&
!cachedDirectoryChild(cache, cachedParent, normalizedPath, childPathIndexes)
!cachedDirectoryChild(cache, cachedParent, normalizedPath)
) {
dirsToRefresh.add(cachedParent)
}