Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class MockFolderRepositoryManager implements IFolderRepositoryManager {
async initializeFolderRepository(): Promise<{ folder: undefined; repository: undefined; worktree: undefined; worktreeProperties: undefined; trusted: undefined }> { return { folder: undefined, repository: undefined, worktree: undefined, worktreeProperties: undefined, trusted: undefined }; }
async getRepositoryInfo(): Promise<{ repository: undefined; headBranchName: undefined }> { return { repository: undefined, headBranchName: undefined }; }
async getFolderMRU(): Promise<FolderRepositoryMRUEntry[]> { return this._mruEntries; }
async deleteMRUEntry(): Promise<void> { }
}

// #endregion
Expand Down Expand Up @@ -476,7 +475,7 @@ describe('ClaudeCodeSessionService', () => {
noWorkspaceTestingServiceCollection.define(IFolderRepositoryManager, noWorkspaceFolderManager);

noWorkspaceFolderManager.setMRUEntries([
{ folder: mruFolder, repository: undefined, lastAccessed: Date.now(), isUntitledSessionSelection: false },
{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() },
]);

const accessor = noWorkspaceTestingServiceCollection.createTestingAccessor();
Expand Down Expand Up @@ -515,8 +514,8 @@ describe('ClaudeCodeSessionService', () => {
const mruFolder2 = URI.file('/another/project');

noWorkspaceFolderManager.setMRUEntries([
{ folder: mruFolder, repository: undefined, lastAccessed: Date.now(), isUntitledSessionSelection: false },
{ folder: mruFolder2, repository: undefined, lastAccessed: Date.now() - 1000, isUntitledSessionSelection: false },
{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() },
{ folder: mruFolder2, repository: undefined, lastAccessed: Date.now() - 1000 },
]);

const multiMruServiceCollection = store.add(createExtensionUnitTestingServices(store));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class MockFolderRepositoryManager implements IFolderRepositoryManager {
async initializeFolderRepository(): Promise<{ folder: undefined; repository: undefined; worktree: undefined; worktreeProperties: undefined; trusted: undefined }> { return { folder: undefined, repository: undefined, worktree: undefined, worktreeProperties: undefined, trusted: undefined }; }
async getRepositoryInfo(): Promise<any> { return undefined; }
async getFolderMRU(): Promise<FolderRepositoryMRUEntry[]> { return this._mruEntries; }
async deleteMRUEntry(): Promise<void> { }
}

// #endregion
Expand Down Expand Up @@ -90,7 +89,7 @@ describe('getProjectFolders', () => {
const mruFolder = URI.file('/Users/test/recent-project');
const workspace = new TestWorkspaceService([]);
const folderManager = new MockFolderRepositoryManager();
folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now(), isUntitledSessionSelection: false }]);
folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() }]);

const result = await getProjectFolders(workspace, folderManager);

Expand All @@ -113,7 +112,7 @@ describe('getProjectFolders', () => {
const mruFolder = URI.file('/Users/test/mru-folder');
const workspace = new TestWorkspaceService([workspaceFolder]);
const folderManager = new MockFolderRepositoryManager();
folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now(), isUntitledSessionSelection: false }]);
folderManager.setMRUEntries([{ folder: mruFolder, repository: undefined, lastAccessed: Date.now() }]);

const result = await getProjectFolders(workspace, folderManager);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ export interface IChatSessionMetadataStore {
getWorktreeProperties(folder: Uri): Promise<ChatSessionWorktreeProperties | undefined>;
getSessionWorkspaceFolder(sessionId: string): Promise<vscode.Uri | undefined>;
getSessionWorkspaceFolderEntry(sessionId: string): Promise<WorkspaceFolderEntry | undefined>;
getUsedWorkspaceFolders(): Promise<WorkspaceFolderEntry[]>;
getAdditionalWorkspaces(sessionId: string): Promise<IWorkspaceInfo[]>;
setAdditionalWorkspaces(sessionId: string, workspaces: IWorkspaceInfo[]): Promise<void>;
getSessionFirstUserMessage(sessionId: string): Promise<string | undefined>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ export const IChatSessionWorkspaceFolderService = createServiceIdentifier<IChatS
*/
export interface IChatSessionWorkspaceFolderService {
readonly _serviceBrand: undefined;
getRecentFolders(): Promise<{ folder: vscode.Uri; lastAccessTime: number }[]>;
/**
* Delete a recent folder from all tracked sessions.
*/
deleteRecentFolder(folder: vscode.Uri): Promise<void>;
deleteTrackedWorkspaceFolder(sessionId: string): Promise<void>;
/**
* Track workspace folder selection for a session (for folders without git repos in multi-root workspaces)
Expand Down
11 changes: 1 addition & 10 deletions src/extension/chatSessions/common/folderRepositoryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,6 @@ export interface FolderRepositoryMRUEntry {
* Timestamp of last access (milliseconds since epoch).
*/
readonly lastAccessed: number;

/**
* Whether this entry was used in an untitled session.
*/
readonly isUntitledSessionSelection: boolean;
}

export const IFolderRepositoryManager = createServiceIdentifier<IFolderRepositoryManager>('IFolderRepositoryManager');
Expand Down Expand Up @@ -152,6 +147,7 @@ export interface IFolderRepositoryManager {
): Promise<{ repository: vscode.Uri | undefined; headBranchName: string | undefined }>;

/**
* @deprecated
* Get list of most recently used folders and repositories.
*
* This is used for empty workspaces to show a list of previously used
Expand All @@ -161,9 +157,4 @@ export interface IFolderRepositoryManager {
* limited to 10 items, with non-existent paths filtered out
*/
getFolderMRU(): Promise<FolderRepositoryMRUEntry[]>;

/**
* Delete an entry from the MRU list.
*/
deleteMRUEntry(folder: vscode.Uri): Promise<void>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ export class MockChatSessionMetadataStore implements IChatSessionMetadataStore {
return undefined;
}

async getUsedWorkspaceFolders(): Promise<WorkspaceFolderEntry[]> {
return Array.from(this._workspaceFolders.values());
}

async getAdditionalWorkspaces(sessionId: string): Promise<IWorkspaceInfo[]> {
return this._additionalWorkspaces.get(sessionId) ?? [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export interface ICopilotCLISessionItem {
readonly status?: ChatSessionStatus;
readonly workingDirectory?: Uri;
}

export type ExtendedChatRequest = ChatRequest & { prompt: string };
export type ISessionOptions = {
model?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ class NullAgentSessionsWorkspace implements IAgentSessionsWorkspace {
}

class NullChatSessionWorkspaceFolderService extends mock<IChatSessionWorkspaceFolderService>() {
override getRecentFolders = vi.fn(async () => []);
override deleteRecentFolder = vi.fn(async () => { });
override deleteTrackedWorkspaceFolder = vi.fn(async () => { });
override trackSessionWorkspaceFolder = vi.fn(async () => { });
override getSessionWorkspaceFolder = vi.fn(async () => undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { findLast } from '../../../util/vs/base/common/arraysFind';
import { SequencerByKey, ThrottledDelayer } from '../../../util/vs/base/common/async';
import { Lazy } from '../../../util/vs/base/common/lazy';
import { Disposable } from '../../../util/vs/base/common/lifecycle';
import { ResourceMap } from '../../../util/vs/base/common/map';
import { dirname, isEqual } from '../../../util/vs/base/common/resources';
import { ChatSessionMetadataFile, IChatSessionMetadataStore, RequestDetails, WorkspaceFolderEntry } from '../common/chatSessionMetadataStore';
import { ChatSessionWorktreeData, ChatSessionWorktreeProperties } from '../common/chatSessionWorktreeService';
Expand Down Expand Up @@ -245,18 +244,6 @@ export class ChatSessionMetadataStore extends Disposable implements IChatSession
return metadata.workspaceFolder;
}

async getUsedWorkspaceFolders(): Promise<WorkspaceFolderEntry[]> {
await this._intialize.value;
const entries = new ResourceMap<number>();
for (const metadata of Object.values(this._cache)) {
if (metadata.workspaceFolder?.folderPath) {
const folderUri = Uri.file(metadata.workspaceFolder.folderPath);
entries.set(folderUri, Math.max(entries.get(folderUri) ?? 0, metadata.workspaceFolder.timestamp));
}
}
return Array.from(entries.entries()).map(([folderUri, timestamp]) => ({ folderPath: folderUri.fsPath, timestamp }));
}

async getAdditionalWorkspaces(sessionId: string): Promise<IWorkspaceInfo[]> {
const metadata = await this.getSessionMetadata(sessionId);
if (!metadata?.additionalWorkspaces?.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ import { IGitService } from '../../../platform/git/common/gitService';
import { parseGitChangesRaw } from '../../../platform/git/vscode-node/utils';
import { DiffChange } from '../../../platform/git/vscode/git';
import { ILogService } from '../../../platform/log/common/logService';
import { coalesce } from '../../../util/vs/base/common/arrays';
import { SequencerByKey } from '../../../util/vs/base/common/async';
import { Disposable } from '../../../util/vs/base/common/lifecycle';
import { ResourceMap, ResourceSet } from '../../../util/vs/base/common/map';
import { ResourceMap } from '../../../util/vs/base/common/map';
import * as path from '../../../util/vs/base/common/path';
import { isEqual } from '../../../util/vs/base/common/resources';
import { generateUuid } from '../../../util/vs/base/common/uuid';
import { IChatSessionMetadataStore, WorkspaceFolderEntry } from '../common/chatSessionMetadataStore';
import { IChatSessionWorkspaceFolderService } from '../common/chatSessionWorkspaceFolderService';
Expand All @@ -32,8 +30,6 @@ export class ChatSessionWorkspaceFolderService extends Disposable implements ICh

private readonly workspaceFolderChanges = new ResourceMap<ChatSessionWorktreeFile[]>();
private readonly workspaceState = new Map<string, WorkspaceFolderEntry>();
private recentFolders: { folder: vscode.Uri; lastAccessTime: number }[] = [];
private readonly deletedFolders = new ResourceSet();
private readonly workspaceChangesSequencer = new SequencerByKey<string>();

constructor(
Expand All @@ -45,28 +41,6 @@ export class ChatSessionWorkspaceFolderService extends Disposable implements ICh
super();
}

public async deleteRecentFolder(folder: vscode.Uri): Promise<void> {
this.recentFolders = this.recentFolders.filter(entry => !isEqual(entry.folder, folder));
this.deletedFolders.add(folder);
}

public async getRecentFolders(): Promise<{ folder: vscode.Uri; lastAccessTime: number }[]> {
const items = await this.metadataStore.getUsedWorkspaceFolders();
this.recentFolders = coalesce(items.map(item => {
if (!item.folderPath) {
return;
}
const folder = vscode.Uri.file(item.folderPath);
if (this.deletedFolders.has(folder)) {
return;
}
return {
folder,
lastAccessTime: item.timestamp
};
})).sort((a, b) => b.lastAccessTime - a.lastAccessTime);
return this.recentFolders;
}
async deleteTrackedWorkspaceFolder(sessionId: string): Promise<void> {
this.workspaceState.delete(sessionId);
await this.metadataStore.deleteSessionMetadata(sessionId);
Expand Down
9 changes: 7 additions & 2 deletions src/extension/chatSessions/vscode-node/chatSessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { ChatSessionWorktreeService } from './chatSessionWorktreeServiceImpl';
import { ClaudeChatSessionContentProvider } from './claudeChatSessionContentProvider';
import { CopilotCLIChatSessionContentProvider, CopilotCLIChatSessionParticipant, registerCLIChatCommands } from './copilotCLIChatSessions';
import { CopilotCLIChatSessionContentProvider as CopilotCLIChatSessionContentProviderV1, CopilotCLIChatSessionItemProvider as CopilotCLIChatSessionItemProviderV1, CopilotCLIChatSessionParticipant as CopilotCLIChatSessionParticipantV1, registerCLIChatCommands as registerCLIChatCommandsV1 } from './copilotCLIChatSessionsContribution';
import { CopilotCLIFolderMruService, ICopilotCLIFolderMruService } from './copilotCLIFolderMru';
import { CopilotCLITerminalIntegration, ICopilotCLITerminalIntegration } from './copilotCLITerminalIntegration';
import { CopilotCloudSessionsProvider } from './copilotCloudSessionsProvider';
import { ClaudeFolderRepositoryManager, CopilotCLIFolderRepositoryManager } from './folderRepositoryManagerImpl';
Expand Down Expand Up @@ -166,6 +167,7 @@ export class ChatSessionsContrib extends Disposable implements IExtensionContrib
[ICustomSessionTitleService, new SyncDescriptor(CustomSessionTitleService)],
[ICopilotCLISkills, new SyncDescriptor(CopilotCLISkills)],
[IChatSessionMetadataStore, new SyncDescriptor(ChatSessionMetadataStore)],
[ICopilotCLIFolderMruService, new SyncDescriptor(CopilotCLIFolderMruService)],
...getServices()
));

Expand All @@ -189,6 +191,7 @@ export class ChatSessionsContrib extends Disposable implements IExtensionContrib
const nativeEnvService = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(INativeEnvService));
const fileSystemService = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(IFileSystemService));
const copilotModels = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(ICopilotCLIModels));
const copilotCLIFolderMruService = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(ICopilotCLIFolderMruService));

this._register(copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(ICopilotCLISessionTracker)));
this._register(copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(IChatPromptFileService)));
Expand All @@ -198,7 +201,7 @@ export class ChatSessionsContrib extends Disposable implements IExtensionContrib

const copilotcliParticipant = vscode.chat.createChatParticipant(this.copilotcliSessionType, copilotcliChatSessionParticipant.createHandler());
this._register(vscode.chat.registerChatSessionContentProvider(this.copilotcliSessionType, copilotcliChatSessionContentProvider, copilotcliParticipant));
this._register(registerCLIChatCommands(copilotCLISessionService, copilotCLIWorktreeManagerService, gitService, copilotCLIWorkspaceFolderSessions, copilotcliChatSessionContentProvider, folderRepositoryManager, nativeEnvService, fileSystemService, sessionTracker, terminalIntegration, logService));
this._register(registerCLIChatCommands(copilotCLISessionService, copilotCLIWorktreeManagerService, gitService, copilotCLIWorkspaceFolderSessions, copilotcliChatSessionContentProvider, folderRepositoryManager, copilotCLIFolderMruService, nativeEnvService, fileSystemService, sessionTracker, terminalIntegration, logService));
// #endregion

const sessionMetadata = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(IChatSessionMetadataStore));
Expand Down Expand Up @@ -228,6 +231,7 @@ export class ChatSessionsContrib extends Disposable implements IExtensionContrib
[ICustomSessionTitleService, new SyncDescriptor(CustomSessionTitleService)],
[ICopilotCLISkills, new SyncDescriptor(CopilotCLISkills)],
[IChatSessionMetadataStore, new SyncDescriptor(ChatSessionMetadataStore)],
[ICopilotCLIFolderMruService, new SyncDescriptor(CopilotCLIFolderMruService)],
...getServices()
));

Expand All @@ -254,6 +258,7 @@ export class ChatSessionsContrib extends Disposable implements IExtensionContrib
const nativeEnvService = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(INativeEnvService));
const fileSystemService = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(IFileSystemService));
const copilotModels = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(ICopilotCLIModels));
const copilotFolderMruService = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(ICopilotCLIFolderMruService));

this._register(copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(ICopilotCLISessionTracker)));
this._register(copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(IChatPromptFileService)));
Expand All @@ -263,7 +268,7 @@ export class ChatSessionsContrib extends Disposable implements IExtensionContrib

const copilotcliParticipant = vscode.chat.createChatParticipant(this.copilotcliSessionType, copilotcliChatSessionParticipant.createHandler());
this._register(vscode.chat.registerChatSessionContentProvider(this.copilotcliSessionType, copilotcliChatSessionContentProvider, copilotcliParticipant));
this._register(registerCLIChatCommandsV1(copilotcliSessionItemProvider, copilotCLISessionService, copilotCLIWorktreeManagerService, gitService, gitExtensionService, toolsService, copilotCLIWorkspaceFolderSessions, copilotcliChatSessionContentProvider, folderRepositoryManager, nativeEnvService, fileSystemService, logService));
this._register(registerCLIChatCommandsV1(copilotcliSessionItemProvider, copilotCLISessionService, copilotCLIWorktreeManagerService, gitService, gitExtensionService, toolsService, copilotCLIWorkspaceFolderSessions, copilotcliChatSessionContentProvider, folderRepositoryManager, copilotFolderMruService, nativeEnvService, fileSystemService, logService));
// #endregion

const sessionMetadata = copilotcliAgentInstaService.invokeFunction(accessor => accessor.get(IChatSessionMetadataStore));
Expand Down
Loading
Loading