Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/vscode-node/chatSessionRepositoryTracker.ts
13399 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 * as vscode from 'vscode';
7
import { ILogService } from '../../../platform/log/common/logService';
8
import { Disposable, DisposableResourceMap, DisposableStore } from '../../../util/vs/base/common/lifecycle';
9
import { IChatSessionWorkspaceFolderService } from '../common/chatSessionWorkspaceFolderService';
10
import { IChatSessionWorktreeService } from '../common/chatSessionWorktreeService';
11
import { ICopilotCLIChatSessionItemProvider } from './copilotCLIChatSessions';
12
import { IGitService } from '../../../platform/git/common/gitService';
13
import { IChatSessionMetadataStore } from '../common/chatSessionMetadataStore';
14
15
export class ChatSessionRepositoryTracker extends Disposable {
16
private readonly repositories = new DisposableResourceMap();
17
18
constructor(
19
// This is only required in non-controller code paths.
20
private readonly sessionItemProvider: ICopilotCLIChatSessionItemProvider | undefined,
21
@IChatSessionWorktreeService private readonly worktreeService: IChatSessionWorktreeService,
22
@IChatSessionWorkspaceFolderService private readonly workspaceFolderService: IChatSessionWorkspaceFolderService,
23
@IGitService private readonly gitService: IGitService,
24
@ILogService private readonly logService: ILogService,
25
@IChatSessionMetadataStore private readonly metadataStore: IChatSessionMetadataStore
26
) {
27
super();
28
29
// Only track repository changes in the sessions app
30
if (vscode.workspace.isAgentSessionsWorkspace) {
31
this.logService.trace('[ChatSessionRepositoryTracker][constructor] Initializing workspace folder event handler');
32
this._register(vscode.workspace.onDidChangeWorkspaceFolders(e => this.onDidChangeWorkspaceFolders(e)));
33
this.onDidChangeWorkspaceFolders({ added: vscode.workspace.workspaceFolders ?? [], removed: [] });
34
}
35
}
36
37
private async onDidChangeWorkspaceFolders(e: vscode.WorkspaceFoldersChangeEvent): Promise<void> {
38
this.logService.trace(`[ChatSessionRepositoryTracker][onDidChangeWorkspaceFolders] Workspace folders changed. Added: ${e.added.map(f => f.uri.fsPath).join(', ')}, Removed: ${e.removed.map(f => f.uri.fsPath).join(', ')}`);
39
40
// Add watchers
41
for (const added of e.added) {
42
await this.createRepositoryWatcher(added.uri);
43
}
44
45
// Dispose watchers
46
for (const removed of e.removed) {
47
this.disposeRepositoryWatcher(removed.uri);
48
}
49
}
50
51
private async createRepositoryWatcher(uri: vscode.Uri): Promise<void> {
52
if (this.repositories.has(uri)) {
53
this.logService.trace(`[ChatSessionRepositoryTracker][createRepositoryWatcher] Already tracking repository changes for ${uri.toString()}.`);
54
return;
55
}
56
57
const repository = await this.gitService.openRepository(uri);
58
if (!repository) {
59
this.logService.trace(`[ChatSessionRepositoryTracker][createRepositoryWatcher] No repository found at ${uri.toString()}.`);
60
return;
61
}
62
63
const disposables = new DisposableStore();
64
disposables.add(repository.state.onDidChange(() => this.onDidChangeRepositoryState(uri)));
65
this.repositories.set(uri, disposables);
66
67
// Trigger an initial update to set the session
68
// properties based on the current repository state
69
void this.onDidChangeRepositoryState(uri);
70
}
71
72
private async onDidChangeRepositoryState(uri: vscode.Uri): Promise<void> {
73
await clearChangesCacheForAffectedSessions(uri, [], this.logService, this.metadataStore, this.workspaceFolderService, this.worktreeService, this.sessionItemProvider);
74
}
75
76
private disposeRepositoryWatcher(uri: vscode.Uri): void {
77
if (!this.repositories.has(uri)) {
78
return;
79
}
80
81
this.logService.trace(`[ChatSessionRepositoryTracker][disposeRepositoryWatcher] Disposing repository watcher for ${uri.toString()}.`);
82
this.repositories.deleteAndDispose(uri);
83
}
84
85
override dispose(): void {
86
this.repositories.dispose();
87
super.dispose();
88
}
89
}
90
91
/**
92
* Invalidates the cache for sessions affected by a repository change, and triggers a refresh of those sessions.
93
* You can optionally provide a list of sessions that should not be refreshed.
94
* E.g. if you know that those sessions are not affected or are already up to date, you can exclude them from the refresh to avoid unnecessary work.
95
*/
96
export async function clearChangesCacheForAffectedSessions(folder: vscode.Uri, sessionsToIgnore: string[], logService: ILogService, metadataStore: IChatSessionMetadataStore, workspaceFolderService: IChatSessionWorkspaceFolderService, worktreeService: IChatSessionWorktreeService, sessionItemProvider?: ICopilotCLIChatSessionItemProvider): Promise<void> {
97
logService.trace(`[ChatSessionRepositoryTracker][onDidChangeRepositoryState] Repository state changed for ${folder.toString()}. Updating session properties.`);
98
99
const sessionIds = metadataStore.getSessionIdsForFolder(folder).filter(id => !sessionsToIgnore.includes(id));
100
const workspaceSessionIds = workspaceFolderService.clearWorkspaceChanges(folder).filter(id => !sessionsToIgnore.includes(id));
101
sessionIds.forEach(id => workspaceFolderService.clearWorkspaceChanges(id));
102
sessionIds.push(...workspaceSessionIds);
103
await Promise.all(Array.from(new Set(sessionIds)).map(async sessionId => {
104
// Worktree
105
const worktreeProperties = await worktreeService.getWorktreeProperties(sessionId);
106
if (worktreeProperties) {
107
await worktreeService.setWorktreeProperties(sessionId, {
108
...worktreeProperties,
109
changes: undefined
110
});
111
}
112
}));
113
// Will be passed in non-controller code paths.
114
if (sessionItemProvider) {
115
await sessionItemProvider.refreshSession({ reason: 'update', sessionIds });
116
}
117
logService.trace(`[ChatSessionRepositoryTracker][onDidChangeRepositoryState] Updated session properties for worktree ${folder.toString()}.`);
118
}
119
120