Merge branch 'fix/editor-reopen-phantom-tab' into fix/editor-close-all-quadratic

#12399 and #12404 both append a test to the tail of the same describe block
in editor.test.ts ('createEditorSlice recently closed editor tabs'), so git
saw one conflicting hunk spanning both new `it` bodies plus their shared
closing brace. editor.ts auto-merged cleanly — the two source changes touch
`openFile`'s id resolution and `closeAllFiles`'s position capture, far apart.

Resolution is purely additive: keep both tests as sibling `it` blocks. No
assertion, helper, or source line was altered. Both PRs' helpers
(`ownedEditorFileId`, `withCountedEntityIdReads`) merged without conflict.

Verified each fix is still live and independently guarded by reverting one
source hunk at a time:
  - revert `const id = existing ? existing.id : ...` -> only 'reactivates the
    live tab when a stale reopen id targets an already-open file' fails
  - revert `positionIndex.positionFor(f.id)` back to the per-tab
    `getRecentlyClosedTabPosition` scan -> only 'captures close-all snapshot
    positions without rescanning tab state per closed tab' fails
    (25920 reads vs the 13440 ceiling)
Restored: editor.test.ts 193/193 green.
This commit is contained in:
OrcaWin
2026-08-04 00:09:04 -04:00
2 changed files with 47 additions and 2 deletions
@@ -1772,6 +1772,49 @@ describe('createEditorSlice recently closed editor tabs', () => {
// made close-all cubic — 4x the tabs cost ~64x the scans and froze the renderer for seconds.
expect(countEntityIdReads(80)).toBeLessThanOrEqual(countEntityIdReads(20) * 8)
})
it('reactivates the live tab when a stale reopen id targets an already-open file', () => {
const store = createEditorTabsStore()
store.setState({
worktreesByRepo: {
'repo-1': [
{ id: 'wt-1', repoId: 'repo-1', path: '/repo' },
{ id: 'wt-2', repoId: 'repo-1', path: '/repo-2' }
]
}
} as unknown as Partial<AppState>)
const sharedPath = '/home/me/.zshrc'
const openShared = (worktreeId: string): string =>
store.getState().openFile({
filePath: sharedPath,
relativePath: '.zshrc',
worktreeId,
language: 'shell',
mode: 'edit'
})
// Bare path id: nothing else owns this path yet.
const staleWt1Id = openShared('wt-1')
expect(staleWt1Id).toBe(sharedPath)
store.getState().closeFile(staleWt1Id)
// wt-2 claims the bare id, so wt-1's reopen gets a namespaced id instead.
const wt2Id = openShared('wt-2')
expect(wt2Id).toBe(sharedPath)
const liveWt1Id = openShared('wt-1')
expect(liveWt1Id).toBe(ownedEditorFileId(sharedPath, 'wt-1', null))
store.getState().closeFile(wt2Id)
expect(store.getState().reopenClosedEditorTab('wt-1')).toBe(true)
expect(store.getState().openFiles.map((file) => file.id)).toEqual([liveWt1Id])
expect(store.getState().activeFileId).toBe(liveWt1Id)
expect(store.getState().activeFileIdByWorktree['wt-1']).toBe(liveWt1Id)
expect(
(store.getState().unifiedTabsByWorktree['wt-1'] ?? []).map((tab) => tab.entityId)
).toEqual([liveWt1Id])
expect(store.getState().tabBarOrderByWorktree['wt-1']).toEqual([liveWt1Id])
})
})
describe('createEditorSlice markdown view state', () => {
+4 -2
View File
@@ -1678,8 +1678,10 @@ export const createEditorSlice: StateCreator<AppState, [], [], EditorSlice> = (s
matchesEditorMode(f, reusableOpenFileModes) &&
isSameEditorOwner(f, worktreeId, runtimeEnvironmentId)
)
const id =
options?.reopenId && !s.openFiles.some((candidate) => candidate.id === options.reopenId)
// Why: a snapshot's reopenId can be a stale shape — the same path is bare in whichever worktree opened it first and namespaced elsewhere — so honoring it while this owner's tab is already open would strand activeFileId and the unified tab on an id no OpenFile has.
const id = existing
? existing.id
: options?.reopenId && !s.openFiles.some((candidate) => candidate.id === options.reopenId)
? options.reopenId
: resolveEditorFileIdForOwner(
s,