fix(diff): reload a combined section after save so a stale original side recovers

A revalidation rejected while a row was dirty was never re-driven: requestSectionReload
refuses while dirty, and the git-status signature does not change for an edit inside an
already-modified line, so the original side stayed stale indefinitely.
This commit is contained in:
Neil
2026-09-10 20:20:29 -07:00
parent 551dc41641
commit e48e0b7cb7
4 changed files with 29 additions and 2 deletions
@@ -203,6 +203,7 @@ export default function CombinedDiffViewer({
isAllMode: entrySet.isAllMode,
isBranchMode: entrySet.isBranchMode,
isCommitMode: entrySet.isCommitMode,
requestSectionReloadRef: registry.requestSectionReloadRef,
sectionsRef: registry.sectionsRef,
setSectionHeights,
setSections
@@ -26,6 +26,7 @@ export function useCombinedDiffSectionActions({
isAllMode,
isBranchMode,
isCommitMode,
requestSectionReloadRef,
sectionsRef,
setSectionHeights,
setSections
@@ -38,6 +39,7 @@ export function useCombinedDiffSectionActions({
isAllMode: boolean
isBranchMode: boolean
isCommitMode: boolean
requestSectionReloadRef: React.RefObject<(index: number) => void>
sectionsRef: React.RefObject<DiffSection[]>
setSectionHeights: React.Dispatch<React.SetStateAction<Record<number, number>>>
setSections: React.Dispatch<React.SetStateAction<DiffSection[]>>
@@ -144,6 +146,7 @@ export function useCombinedDiffSectionActions({
const handleSectionSaveRef = useCombinedDiffSectionSave({
file,
requestSectionReloadRef,
sectionsRef,
setSectionHeights,
setSections
@@ -44,11 +44,18 @@ function section(key = 'file.ts'): DiffSection {
}
}
}
const reloadSpy = vi.fn()
function setup(initial = [section()]) {
return renderHook(() => {
const { sections, setSections, sectionsRef } = useCombinedDiffSectionsState(initial)
const [heights, setSectionHeights] = useState<Record<number, number>>({ 0: 100, 1: 200 })
const save = useCombinedDiffSectionSave({ file, sectionsRef, setSections, setSectionHeights })
const save = useCombinedDiffSectionSave({
file,
requestSectionReloadRef: { current: reloadSpy },
sectionsRef,
setSections,
setSectionHeights
})
return { sections, setSections, heights, save }
})
}
@@ -216,3 +223,13 @@ describe('combined diff section saves', () => {
expect(writeFile).not.toHaveBeenCalled()
})
})
it('re-drives a reload after saving, so a revalidation rejected while dirty is not stranded', async () => {
const { result } = setup()
await act(async () => {
await result.current.save.current(0)
})
// The git-status signature does not change for an edit inside an already-modified line, so the
// save is the only event that can recover a stale original side.
expect(reloadSpy).toHaveBeenCalledWith(0)
})
@@ -43,11 +43,13 @@ function acknowledgeSectionSave(section: DiffSection, content: string): DiffSect
export function useCombinedDiffSectionSave({
file,
requestSectionReloadRef,
sectionsRef,
setSectionHeights,
setSections
}: {
file: OpenFile
requestSectionReloadRef: React.RefObject<(index: number) => void>
sectionsRef: React.RefObject<DiffSection[]>
setSectionHeights: React.Dispatch<React.SetStateAction<Record<number, number>>>
setSections: React.Dispatch<React.SetStateAction<DiffSection[]>>
@@ -95,6 +97,10 @@ export function useCombinedDiffSectionSave({
: entry
)
)
// Why: a revalidation rejected while this row was dirty left the original side stale,
// and the git-status signature does not change for an edit inside an already-modified
// line, so nothing else re-drives it. The row is clean now, so the reload can proceed.
requestSectionReloadRef.current(savedIndex)
})
.catch((error: unknown) => {
console.error('Save failed:', error)
@@ -107,7 +113,7 @@ export function useCombinedDiffSectionSave({
saves.set(key, tracked)
return tracked
},
[file, sectionsRef, setSectionHeights, setSections]
[file, requestSectionReloadRef, sectionsRef, setSectionHeights, setSections]
)
const saveRef = useRef(saveSection)
useLayoutEffect(() => {