Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/claude/node/claudeCodeFolderMru.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IGitService } from '../../../../platform/git/common/gitService';
7
import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';
8
import { raceTimeout } from '../../../../util/vs/base/common/async';
9
import { CancellationToken } from '../../../../util/vs/base/common/cancellation';
10
import { ResourceMap, ResourceSet } from '../../../../util/vs/base/common/map';
11
import { URI } from '../../../../util/vs/base/common/uri';
12
import { FolderRepositoryMRUEntry, IChatFolderMruService } from '../../common/folderRepositoryManager';
13
import { IClaudeCodeSessionService } from './sessionParser/claudeCodeSessionService';
14
15
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
16
17
const WORKTREE_PATH_PATTERNS = ['.claude/worktrees/', '.worktrees/copilot-'] as const;
18
19
function isWorktreePath(path: string): boolean {
20
return WORKTREE_PATH_PATTERNS.some(pattern => path.includes(pattern));
21
}
22
23
export class ClaudeCodeFolderMruService implements IChatFolderMruService {
24
declare _serviceBrand: undefined;
25
private readonly removedFolders = new ResourceSet();
26
private cachedEntries: FolderRepositoryMRUEntry[] | undefined = undefined;
27
28
constructor(
29
@IClaudeCodeSessionService private readonly sessionService: IClaudeCodeSessionService,
30
@IGitService private readonly gitService: IGitService,
31
@IWorkspaceService private readonly workspaceService: IWorkspaceService,
32
) { }
33
34
async getRecentlyUsedFolders(token: CancellationToken): Promise<FolderRepositoryMRUEntry[]> {
35
const cachedEntries = this.cachedEntries;
36
const entries = this.getRecentlyUsedFoldersImpl(token).then(entries => {
37
this.cachedEntries = entries;
38
return entries;
39
});
40
41
return (cachedEntries ? cachedEntries : await entries).filter(e => !this.removedFolders.has(e.folder));
42
}
43
44
private async getRecentlyUsedFoldersImpl(token: CancellationToken): Promise<FolderRepositoryMRUEntry[]> {
45
const mruEntries = new ResourceMap<Mutable<FolderRepositoryMRUEntry>>();
46
47
// We're getting MRU, don't delay session retrieve by more than 5s
48
const sessions = await raceTimeout(this.sessionService.getAllSessions(token), 5_000);
49
50
for (const session of (sessions ?? [])) {
51
if (!session.cwd) {
52
continue;
53
}
54
if (isWorktreePath(session.cwd)) {
55
continue;
56
}
57
const folderUri = URI.file(session.cwd);
58
const lastAccessed = session.lastRequestEnded ?? session.lastRequestStarted ?? session.created ?? 0;
59
mruEntries.set(folderUri, {
60
folder: folderUri,
61
repository: undefined,
62
lastAccessed,
63
});
64
}
65
66
// Add recent git repositories
67
for (const repo of this.gitService.getRecentRepositories()) {
68
if (isWorktreePath(repo.rootUri.path)) {
69
continue;
70
}
71
const existingEntry = mruEntries.get(repo.rootUri);
72
if (existingEntry) {
73
existingEntry.lastAccessed = Math.max(existingEntry.lastAccessed, repo.lastAccessTime);
74
existingEntry.repository = repo.rootUri;
75
continue;
76
}
77
mruEntries.set(repo.rootUri, {
78
folder: repo.rootUri,
79
repository: repo.rootUri,
80
lastAccessed: repo.lastAccessTime,
81
});
82
}
83
84
// If in multi-root folder add the folders as well, but on top.
85
for (const folder of this.workspaceService.getWorkspaceFolders()) {
86
const existingEntry = mruEntries.get(folder);
87
if (existingEntry) {
88
continue;
89
}
90
mruEntries.set(folder, {
91
folder,
92
repository: undefined,
93
lastAccessed: Date.now(),
94
});
95
}
96
97
return Array.from(mruEntries.values())
98
.sort((a, b) => b.lastAccessed - a.lastAccessed);
99
}
100
101
async deleteRecentlyUsedFolder(folder: URI): Promise<void> {
102
this.removedFolders.add(folder);
103
}
104
}
105
106