diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 905e657b37e..73760100645 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1825,6 +1825,58 @@ export class ClineProvider return this.mcpHub } + /** + * Handle workspace folder change by re-initializing the CodeIndexManager + * This ensures the code index corresponds to the currently active workspace folder + */ + public async handleWorkspaceFolderChange() { + if (!this.codeIndexManager) { + return + } + + try { + // Dispose of the old code index status subscription + if (this.codeIndexStatusSubscription) { + this.codeIndexStatusSubscription.dispose() + this.codeIndexStatusSubscription = undefined + } + + // Get a new instance of CodeIndexManager for the current workspace + const newManager = CodeIndexManager.getInstance(this.context) + if (!newManager) { + this.log("No workspace folder available for code index") + return + } + + // Re-initialize the manager + await newManager.initialize(this.contextProxy) + + // Re-subscribe to status updates + this.codeIndexStatusSubscription = newManager.onProgressUpdate((update: IndexProgressUpdate) => { + this.postMessageToWebview({ + type: "indexingStatusUpdate", + values: update, + }) + }) + if (this.webviewDisposables && this.codeIndexStatusSubscription) { + this.webviewDisposables.push(this.codeIndexStatusSubscription) + } + + // Update the reference + ;(this as any).codeIndexManager = newManager + + // Send the current status to the webview + this.postMessageToWebview({ + type: "indexingStatusUpdate", + values: newManager.getCurrentStatus(), + }) + + this.log(`Code index re-initialized for workspace: ${getWorkspacePath()}`) + } catch (error) { + this.log(`Failed to re-initialize code index: ${error}`) + } + } + /** * Check if the current state is compliant with MDM policy * @returns true if compliant, false if blocked diff --git a/src/integrations/workspace/WorkspaceTracker.ts b/src/integrations/workspace/WorkspaceTracker.ts index 7de8f994c03..10ce5535d54 100644 --- a/src/integrations/workspace/WorkspaceTracker.ts +++ b/src/integrations/workspace/WorkspaceTracker.ts @@ -97,11 +97,16 @@ class WorkspaceTracker { } this.resetTimer = setTimeout(async () => { if (this.prevWorkSpacePath !== this.cwd) { - await this.providerRef.deref()?.postMessageToWebview({ - type: "workspaceUpdated", - filePaths: [], - openedTabs: this.getOpenedTabsInfo(), - }) + const provider = this.providerRef.deref() + if (provider) { + await provider.postMessageToWebview({ + type: "workspaceUpdated", + filePaths: [], + openedTabs: this.getOpenedTabsInfo(), + }) + // Trigger code index re-initialization for the new workspace + await provider.handleWorkspaceFolderChange() + } this.filePaths.clear() this.prevWorkSpacePath = this.cwd this.initializeFilePaths() diff --git a/src/integrations/workspace/__tests__/WorkspaceTracker.spec.ts b/src/integrations/workspace/__tests__/WorkspaceTracker.spec.ts index b0a617d9709..e22b8bfc875 100644 --- a/src/integrations/workspace/__tests__/WorkspaceTracker.spec.ts +++ b/src/integrations/workspace/__tests__/WorkspaceTracker.spec.ts @@ -82,7 +82,8 @@ describe("WorkspaceTracker", () => { // Create provider mock mockProvider = { postMessageToWebview: vitest.fn().mockResolvedValue(undefined), - } as unknown as ClineProvider & { postMessageToWebview: Mock } + handleWorkspaceFolderChange: vitest.fn().mockResolvedValue(undefined), + } as unknown as ClineProvider & { postMessageToWebview: Mock; handleWorkspaceFolderChange: Mock } // Create tracker instance workspaceTracker = new WorkspaceTracker(mockProvider) @@ -225,9 +226,14 @@ describe("WorkspaceTracker", () => { // Simulate tab change event await registeredTabChangeCallback!() - // Run the debounce timer for workspaceDidReset + // Run the debounce timer for workspaceDidReset (300ms) vitest.advanceTimersByTime(300) + // Wait for the async operations in the timeout callback to complete + await Promise.resolve() // For the setTimeout callback + await Promise.resolve() // For the async operations inside + await Promise.resolve() // For handleWorkspaceFolderChange + // Should clear file paths and reset workspace expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "workspaceUpdated", @@ -235,13 +241,17 @@ describe("WorkspaceTracker", () => { openedTabs: [], }) + // Should have called handleWorkspaceFolderChange + expect(mockProvider.handleWorkspaceFolderChange).toHaveBeenCalled() + // Run all remaining timers to complete initialization - await Promise.resolve() // Wait for initializeFilePaths to complete vitest.runAllTimers() + // Wait for initializeFilePaths to complete + await Promise.resolve() + // Should initialize file paths for new workspace expect(listFiles).toHaveBeenCalledWith("/test/new-workspace", true, 1000) - vitest.runAllTimers() }) it("should not update file paths if workspace changes during initialization", async () => { diff --git a/src/services/code-index/__tests__/manager.spec.ts b/src/services/code-index/__tests__/manager.spec.ts index 8c64c2fdc62..dd21a2bb30c 100644 --- a/src/services/code-index/__tests__/manager.spec.ts +++ b/src/services/code-index/__tests__/manager.spec.ts @@ -13,6 +13,9 @@ vi.mock("vscode", () => ({ }, ], }, + window: { + activeTextEditor: undefined, + }, })) // Mock only the essential dependencies diff --git a/src/services/code-index/manager.ts b/src/services/code-index/manager.ts index 18e0752c34d..4d718d78563 100644 --- a/src/services/code-index/manager.ts +++ b/src/services/code-index/manager.ts @@ -29,17 +29,14 @@ export class CodeIndexManager { private _cacheManager: CacheManager | undefined public static getInstance(context: vscode.ExtensionContext): CodeIndexManager | undefined { - // Use first workspace folder consistently - const workspaceFolders = vscode.workspace.workspaceFolders - if (!workspaceFolders || workspaceFolders.length === 0) { + // Get the workspace path based on the active editor or current context + const workspacePath = getWorkspacePath() + if (!workspacePath) { return undefined } - // Always use the first workspace folder for consistency across all indexing operations. - // This ensures that the same workspace context is used throughout the indexing pipeline, - // preventing path resolution errors in multi-workspace scenarios. - const workspacePath = workspaceFolders[0].uri.fsPath - + // Use the workspace folder of the active editor to support multi-folder workspaces. + // This ensures that the codebase index corresponds to the folder the user is currently working in. if (!CodeIndexManager.instances.has(workspacePath)) { CodeIndexManager.instances.set(workspacePath, new CodeIndexManager(workspacePath, context)) }