Skip to content

Commit 82a5ebe

Browse files
Merge branch 'main' into feat/github-copilot-provider
2 parents 4e6e543 + 2681ccb commit 82a5ebe

2 files changed

Lines changed: 103 additions & 1 deletion

File tree

‎src/services/code-index/__tests__/code-index-status-manager.spec.ts‎

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ vi.mock("../state-manager")
2020

2121
vi.mock("vscode", () => ({
2222
window: { onDidChangeActiveTextEditor: vi.fn(), activeTextEditor: undefined },
23-
workspace: { workspaceFolders: undefined, getWorkspaceFolder: vi.fn(), getConfiguration: vi.fn() },
23+
workspace: {
24+
workspaceFolders: undefined,
25+
getWorkspaceFolder: vi.fn(),
26+
getConfiguration: vi.fn(),
27+
onDidChangeWorkspaceFolders: vi.fn(),
28+
},
2429
}))
2530

2631
function makeSource(workspacePath: string) {
@@ -59,6 +64,8 @@ function makeSource(workspacePath: string) {
5964
describe("CodeIndexStatusManager", () => {
6065
let editorChanged: () => void
6166
let disposeEditor: ReturnType<typeof vi.fn<() => void>>
67+
let workspaceFoldersChanged: () => void
68+
let disposeWorkspaceFolders: ReturnType<typeof vi.fn<() => void>>
6269

6370
beforeEach(() => {
6471
vi.clearAllMocks()
@@ -74,6 +81,11 @@ describe("CodeIndexStatusManager", () => {
7481
editorChanged = () => listener(undefined)
7582
return { dispose: disposeEditor }
7683
})
84+
disposeWorkspaceFolders = vi.fn()
85+
vi.mocked(vscode.workspace.onDidChangeWorkspaceFolders).mockImplementation((listener) => {
86+
workspaceFoldersChanged = () => listener({ added: [], removed: [] })
87+
return { dispose: disposeWorkspaceFolders }
88+
})
7789
})
7890

7991
it("resolves the workspace before requesting its manager", () => {
@@ -308,4 +320,87 @@ describe("CodeIndexStatusManager", () => {
308320
expect(source.subscription.dispose).not.toHaveBeenCalled()
309321
manager.dispose()
310322
})
323+
324+
it("switches to the newly selected workspace on a workspace-folder change alone", () => {
325+
const first = makeSource("/first")
326+
const second = makeSource("/second")
327+
const resolve = vi.fn((path: string) => (path === "/first" ? first : second))
328+
const publish = vi.fn()
329+
const manager = new CodeIndexStatusManager(resolve)
330+
manager["publishStatus"] = publish
331+
try {
332+
manager.init()
333+
expect(publish).toHaveBeenCalledExactlyOnceWith(first.status)
334+
Object.defineProperty(vscode.workspace, "workspaceFolders", {
335+
configurable: true,
336+
value: [{ uri: makeUri("/second"), name: "second", index: 0 }],
337+
})
338+
workspaceFoldersChanged()
339+
expect(resolve).toHaveBeenLastCalledWith("/second")
340+
expect(first.subscription.dispose).toHaveBeenCalledTimes(1)
341+
expect(second.onProgressUpdate).toHaveBeenCalledTimes(1)
342+
expect(publish).toHaveBeenCalledTimes(2)
343+
expect(publish).toHaveBeenLastCalledWith(second.status)
344+
first.emit()
345+
expect(publish).toHaveBeenCalledTimes(2)
346+
second.status.message = "Updated"
347+
second.emit()
348+
expect(publish).toHaveBeenCalledTimes(3)
349+
expect(publish).toHaveBeenLastCalledWith(second.status)
350+
} finally {
351+
manager.dispose()
352+
}
353+
})
354+
355+
it("keeps the subscription when a workspace-folder change leaves the selection unchanged", () => {
356+
const source = makeSource("/first")
357+
const publish = vi.fn()
358+
const manager = new CodeIndexStatusManager(() => source)
359+
manager["publishStatus"] = publish
360+
manager.init()
361+
workspaceFoldersChanged()
362+
expect(source.onProgressUpdate).toHaveBeenCalledTimes(1)
363+
expect(source.subscription.dispose).not.toHaveBeenCalled()
364+
expect(publish).toHaveBeenCalledExactlyOnceWith(source.status)
365+
manager.dispose()
366+
})
367+
368+
it("releases the subscription when a workspace-folder change leaves no workspace", () => {
369+
const source = makeSource("/first")
370+
const publish = vi.fn()
371+
const manager = new CodeIndexStatusManager(() => source)
372+
manager["publishStatus"] = publish
373+
manager.init()
374+
Object.defineProperty(vscode.workspace, "workspaceFolders", { configurable: true, value: [] })
375+
workspaceFoldersChanged()
376+
expect(source.subscription.dispose).toHaveBeenCalledTimes(1)
377+
source.emit()
378+
expect(publish).toHaveBeenCalledExactlyOnceWith(source.status)
379+
manager.dispose()
380+
})
381+
382+
it("releases the workspace-folder listener on disposal even when its cleanup fails", () => {
383+
const source = makeSource("/first")
384+
const manager = new CodeIndexStatusManager(() => source)
385+
const folderError = new Error("workspace folders cleanup failed")
386+
const log = vi.spyOn(console, "error").mockImplementation(() => {})
387+
try {
388+
manager.init()
389+
disposeWorkspaceFolders.mockImplementationOnce(() => {
390+
throw folderError
391+
})
392+
expect(() => manager.dispose()).not.toThrow()
393+
expect(log).toHaveBeenCalledExactlyOnceWith(
394+
"[CodeIndexStatusManager] Failed to dispose workspace folders subscription:",
395+
folderError,
396+
)
397+
expect(disposeEditor).toHaveBeenCalledTimes(1)
398+
expect(source.subscription.dispose).toHaveBeenCalledTimes(1)
399+
manager.dispose()
400+
expect(disposeWorkspaceFolders).toHaveBeenCalledTimes(1)
401+
expect(log).toHaveBeenCalledTimes(1)
402+
} finally {
403+
log.mockRestore()
404+
}
405+
})
311406
})

‎src/services/code-index/code-index-status-manager.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ export class CodeIndexStatusManager implements vscode.Disposable {
1010
private currentManager?: CodeIndexManager
1111
private progressSubscription?: vscode.Disposable
1212
private editorSubscription?: vscode.Disposable
13+
private workspaceFoldersSubscription?: vscode.Disposable
1314

1415
public constructor(
1516
private readonly getManagerForWorkspace: (workspacePath: string) => CodeIndexManager | undefined,
1617
) {}
1718

1819
public init(): void {
1920
this.editorSubscription = vscode.window.onDidChangeActiveTextEditor(() => this.updateSubscription())
21+
this.workspaceFoldersSubscription = vscode.workspace.onDidChangeWorkspaceFolders(() =>
22+
this.updateSubscription(),
23+
)
2024
this.updateSubscription()
2125
}
2226

@@ -69,11 +73,14 @@ export class CodeIndexStatusManager implements vscode.Disposable {
6973

7074
public dispose(): void {
7175
const editorSubscription = this.editorSubscription
76+
const workspaceFoldersSubscription = this.workspaceFoldersSubscription
7277
const progressSubscription = this.progressSubscription
7378
this.currentManager = undefined
7479
this.editorSubscription = undefined
80+
this.workspaceFoldersSubscription = undefined
7581
this.progressSubscription = undefined
7682
this.disposeSubscription(editorSubscription, "active editor")
83+
this.disposeSubscription(workspaceFoldersSubscription, "workspace folders")
7784
this.disposeSubscription(progressSubscription, "progress")
7885
}
7986
}

0 commit comments

Comments
 (0)