fix: bound editor self-write stamps (#4145)

This commit is contained in:
Neil
2026-05-31 05:58:41 -07:00
committed by GitHub
parent 1b9a82fdde
commit f749813bc4
2 changed files with 54 additions and 2 deletions
@@ -1,6 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import {
__clearSelfWriteRegistryForTests,
__getSelfWriteRegistrySizeForTests,
clearSelfWrite,
hasRecentSelfWrite,
recordSelfWrite
@@ -36,4 +37,25 @@ describe('editor self-write registry', () => {
expect(hasRecentSelfWrite('/repo/a.md')).toBe(false)
})
it('prunes expired stamps when recording later writes', () => {
recordSelfWrite('/repo/old.md')
vi.advanceTimersByTime(751)
recordSelfWrite('/repo/new.md')
expect(__getSelfWriteRegistrySizeForTests()).toBe(1)
expect(hasRecentSelfWrite('/repo/old.md')).toBe(false)
expect(hasRecentSelfWrite('/repo/new.md')).toBe(true)
})
it('caps retained stamps', () => {
for (let i = 0; i < 260; i++) {
recordSelfWrite(`/repo/${i}.md`)
}
expect(__getSelfWriteRegistrySizeForTests()).toBe(256)
expect(hasRecentSelfWrite('/repo/0.md')).toBe(false)
expect(hasRecentSelfWrite('/repo/259.md')).toBe(true)
})
})
@@ -12,6 +12,7 @@ import { normalizeAbsolutePathForComparison } from '@/components/right-sidebar/f
// a short TTL so a genuinely external edit that lands after the window still
// gets picked up.
const SELF_WRITE_TTL_MS = 750
const SELF_WRITE_MAX_STAMPS = 256
export type RecentSelfWrite = {
content: string | null
@@ -23,11 +24,36 @@ type SelfWriteStamp = RecentSelfWrite & {
const stamps = new Map<string, SelfWriteStamp>()
function pruneExpiredSelfWrites(now = Date.now()): void {
for (const [key, stamp] of stamps) {
if (now > stamp.expiresAt) {
stamps.delete(key)
}
}
}
function enforceSelfWriteStampLimit(): void {
while (stamps.size > SELF_WRITE_MAX_STAMPS) {
const oldest = stamps.keys().next().value
if (oldest === undefined) {
break
}
stamps.delete(oldest)
}
}
export function recordSelfWrite(absolutePath: string, content?: string): void {
stamps.set(normalizeAbsolutePathForComparison(absolutePath), {
const now = Date.now()
pruneExpiredSelfWrites(now)
const key = normalizeAbsolutePathForComparison(absolutePath)
// Why: a missing watcher echo should not leave stale path/content stamps in
// memory for the whole renderer session.
stamps.delete(key)
stamps.set(key, {
content: content ?? null,
expiresAt: Date.now() + SELF_WRITE_TTL_MS
expiresAt: now + SELF_WRITE_TTL_MS
})
enforceSelfWriteStampLimit()
}
export function clearSelfWrite(absolutePath: string): void {
@@ -54,3 +80,7 @@ export function hasRecentSelfWrite(absolutePath: string): boolean {
export function __clearSelfWriteRegistryForTests(): void {
stamps.clear()
}
export function __getSelfWriteRegistrySizeForTests(): number {
return stamps.size
}