Skip to content

Commit 44f26a6

Browse files
committed
Merge remote-tracking branch 'origin/main' into copilot/add-commit-count-decoration
2 parents 4925651 + b05f22a commit 44f26a6

34 files changed

Lines changed: 529 additions & 595 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"tokenInformation",
3838
"treeViewMarkdownMessage"
3939
],
40-
"version": "0.118.0",
40+
"version": "0.120.0",
4141
"publisher": "GitHub",
4242
"engines": {
4343
"vscode": "^1.105.0"

resources/icons/copy.svg

Lines changed: 1 addition & 2 deletions
Loading

src/@types/vscode.proposed.chatParticipantAdditions.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
declare module 'vscode' {
77

88
export interface ChatParticipant {
9-
onDidPerformAction: Event<ChatUserActionEvent>;
9+
readonly onDidPerformAction: Event<ChatUserActionEvent>;
1010
}
1111

1212
/**
@@ -442,7 +442,7 @@ declare module 'vscode' {
442442
* Event that fires when a request is paused or unpaused.
443443
* Chat requests are initially unpaused in the {@link requestHandler}.
444444
*/
445-
onDidChangePauseState: Event<ChatParticipantPauseStateEvent>;
445+
readonly onDidChangePauseState: Event<ChatParticipantPauseStateEvent>;
446446
}
447447

448448
export interface ChatParticipantPauseStateEvent {

src/commands.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import { GitHubRepository } from './github/githubRepository';
2626
import { Issue } from './github/interface';
2727
import { IssueModel } from './github/issueModel';
2828
import { IssueOverviewPanel } from './github/issueOverview';
29-
import { NotificationProvider } from './github/notifications';
3029
import { GHPRComment, GHPRCommentThread, TemporaryComment } from './github/prComment';
3130
import { PullRequestModel } from './github/pullRequestModel';
3231
import { PullRequestOverviewPanel } from './github/pullRequestOverview';
@@ -35,7 +34,7 @@ import { RepositoriesManager } from './github/repositoriesManager';
3534
import { getIssuesUrl, getPullsUrl, isInCodespaces, ISSUE_OR_URL_EXPRESSION, parseIssueExpressionOutput, vscodeDevPrLink } from './github/utils';
3635
import { OverviewContext } from './github/views';
3736
import { isNotificationTreeItem, NotificationTreeItem } from './notifications/notificationItem';
38-
import { PullRequestsTreeDataProvider } from './view/prsTreeDataProvider';
37+
import { NotificationsManager } from './notifications/notificationsManager';
3938
import { ReviewCommentController } from './view/reviewCommentController';
4039
import { ReviewManager } from './view/reviewManager';
4140
import { ReviewsManager } from './view/reviewsManager';
@@ -149,7 +148,6 @@ export async function openDescription(
149148
folderManager: FolderRepositoryManager,
150149
revealNode: boolean,
151150
preserveFocus: boolean = true,
152-
notificationProvider?: NotificationProvider
153151
) {
154152
const issue = ensurePR(folderManager, issueModel);
155153
if (revealNode) {
@@ -165,12 +163,6 @@ export async function openDescription(
165163
*/
166164
telemetry.sendTelemetryEvent('issue.openDescription');
167165
}
168-
169-
if (notificationProvider?.hasNotification(issue)) {
170-
notificationProvider.markPrNotificationsAsRead(issue);
171-
}
172-
173-
174166
}
175167

176168
export async function openPullRequestOnGitHub(e: PRNode | RepositoryChangesNode | IssueModel | NotificationTreeItem, telemetry: ITelemetry) {
@@ -210,8 +202,8 @@ export function registerCommands(
210202
reposManager: RepositoriesManager,
211203
reviewsManager: ReviewsManager,
212204
telemetry: ITelemetry,
213-
tree: PullRequestsTreeDataProvider,
214205
copilotRemoteAgentManager: CopilotRemoteAgentManager,
206+
notificationManager: NotificationsManager
215207
) {
216208
const logId = 'RegisterCommands';
217209
context.subscriptions.push(
@@ -907,10 +899,7 @@ export function registerCommands(
907899
context.subscriptions.push(
908900
vscode.commands.registerCommand('pr.dismissNotification', node => {
909901
if (node instanceof PRNode) {
910-
tree.notificationProvider.markPrNotificationsAsRead(node.pullRequestModel).then(
911-
() => tree.refresh(node)
912-
);
913-
902+
notificationManager.markPrNotificationsAsRead(node.pullRequestModel);
914903
}
915904
}),
916905
);
@@ -968,7 +957,7 @@ export function registerCommands(
968957

969958
const revealDescription = !(argument instanceof PRNode);
970959

971-
await openDescription(telemetry, issueModel, descriptionNode, folderManager, revealDescription, !(argument instanceof RepositoryChangesNode), tree.notificationProvider);
960+
await openDescription(telemetry, issueModel, descriptionNode, folderManager, revealDescription, !(argument instanceof RepositoryChangesNode));
972961
}
973962

974963
async function checkoutChatSessionPullRequest(argument: ChatSessionWithPR) {

src/common/settingKeys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const OVERRIDE_DEFAULT_BRANCH = 'overrideDefaultBranch';
1919
export const PULL_BRANCH = 'pullBranch';
2020
export const PULL_REQUEST_DESCRIPTION = 'pullRequestDescription';
2121
export const NOTIFICATION_SETTING = 'notifications';
22+
export type NotificationVariants = 'off' | 'pullRequests';
2223
export const POST_CREATE = 'postCreate';
2324
export const POST_DONE = 'postDone';
2425
export const QUERIES = 'queries';

src/common/uri.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ export function fromPRUri(uri: vscode.Uri): PRUriParams | undefined {
5353
}
5454

5555
export interface PRNodeUriParams {
56-
prIdentifier: string
56+
prIdentifier: string;
57+
showCopilot?: boolean;
5758
}
5859

5960
export function fromPRNodeUri(uri: vscode.Uri): PRNodeUriParams | undefined {
@@ -501,12 +502,15 @@ export function parsePRNodeIdentifier(identifier: string): { remote: string, prN
501502
}
502503

503504
export function createPRNodeUri(
504-
pullRequest: PullRequestModel | { remote: string, prNumber: number } | string
505+
pullRequest: PullRequestModel | { remote: string, prNumber: number } | string, showCopilot?: boolean
505506
): vscode.Uri {
506507
const identifier = createPRNodeIdentifier(pullRequest);
507508
const params: PRNodeUriParams = {
508509
prIdentifier: identifier,
509510
};
511+
if (showCopilot !== undefined) {
512+
params.showCopilot = showCopilot;
513+
}
510514

511515
const uri = vscode.Uri.parse(`PRNode:${identifier}`);
512516

src/extension.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import { ChatParticipant, ChatParticipantState } from './lm/participants';
3737
import { registerTools } from './lm/tools/tools';
3838
import { migrate } from './migrations';
3939
import { NotificationsFeatureRegister } from './notifications/notificationsFeatureRegistar';
40+
import { NotificationsManager } from './notifications/notificationsManager';
41+
import { NotificationsProvider } from './notifications/notificationsProvider';
4042
import { ThemeWatcher } from './themeWatcher';
4143
import { UriHandler } from './uriHandler';
4244
import { CommentDecorationProvider } from './view/commentDecorationProvider';
@@ -46,7 +48,6 @@ import { FileTypeDecorationProvider } from './view/fileTypeDecorationProvider';
4648
import { GitHubCommitFileSystemProvider } from './view/githubFileContentProvider';
4749
import { getInMemPRFileSystemProvider } from './view/inMemPRContentProvider';
4850
import { PullRequestChangesTreeDataProvider } from './view/prChangesTreeDataProvider';
49-
import { PRNotificationDecorationProvider } from './view/prNotificationDecorationProvider';
5051
import { PullRequestsTreeDataProvider } from './view/prsTreeDataProvider';
5152
import { ReviewManager, ShowPullRequest } from './view/reviewManager';
5253
import { ReviewsManager } from './view/reviewsManager';
@@ -170,7 +171,13 @@ async function init(
170171
context.subscriptions.push(treeDecorationProviders);
171172
treeDecorationProviders.registerProviders([new FileTypeDecorationProvider(), new CommentDecorationProvider(reposManager)]);
172173

173-
const reviewsManager = new ReviewsManager(context, reposManager, reviewManagers, tree, changesTree, telemetry, credentialStore, git, copilotRemoteAgentManager);
174+
const notificationsProvider = new NotificationsProvider(credentialStore, reposManager);
175+
context.subscriptions.push(notificationsProvider);
176+
177+
const notificationsManager = new NotificationsManager(notificationsProvider, credentialStore, reposManager, context);
178+
context.subscriptions.push(notificationsManager);
179+
180+
const reviewsManager = new ReviewsManager(context, reposManager, reviewManagers, tree, changesTree, telemetry, credentialStore, git, copilotRemoteAgentManager, notificationsManager);
174181
context.subscriptions.push(reviewsManager);
175182

176183
git.onDidChangeState(() => {
@@ -218,7 +225,6 @@ async function init(
218225
return;
219226
}
220227
addRepo();
221-
tree.notificationProvider.refreshOrLaunchPolling();
222228
const disposable = repo.state.onDidChange(() => {
223229
Logger.appendLine(`Repo state for ${repo.rootUri} changed.`, ACTIVATION);
224230
addRepo();
@@ -229,14 +235,11 @@ async function init(
229235
git.onDidCloseRepository(repo => {
230236
reposManager.removeRepo(repo);
231237
reviewsManager.removeReviewManager(repo);
232-
tree.notificationProvider.refreshOrLaunchPolling();
233238
});
234239

235-
tree.initialize(reviewsManager.reviewManagers.map(manager => manager.reviewModel), credentialStore);
236-
237-
context.subscriptions.push(new PRNotificationDecorationProvider(tree.notificationProvider));
240+
tree.initialize(reviewsManager.reviewManagers.map(manager => manager.reviewModel), notificationsManager);
238241

239-
registerCommands(context, reposManager, reviewsManager, telemetry, tree, copilotRemoteAgentManager);
242+
registerCommands(context, reposManager, reviewsManager, telemetry, copilotRemoteAgentManager, notificationsManager);
240243

241244
const layout = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<string>(FILE_LIST_LAYOUT);
242245
await vscode.commands.executeCommand('setContext', 'fileListLayout:flat', layout === 'flat');
@@ -245,7 +248,7 @@ async function init(
245248
context.subscriptions.push(issuesFeatures);
246249
await issuesFeatures.initialize();
247250

248-
const notificationsFeatures = new NotificationsFeatureRegister(credentialStore, reposManager, telemetry, context);
251+
const notificationsFeatures = new NotificationsFeatureRegister(credentialStore, reposManager, telemetry, notificationsManager);
249252
context.subscriptions.push(notificationsFeatures);
250253

251254
context.subscriptions.push(new GitLensIntegration());

src/github/copilotPrWatcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ export class CopilotPRWatcher extends Disposable {
270270
private async _updateSingleState(pr: PullRequestModel): Promise<void> {
271271
const changes: { pullRequestModel: PullRequestModel, status: CopilotPRStatus }[] = [];
272272

273-
const copilotEvents = await pr.getCopilotTimelineEvents(pr);
273+
const copilotEvents = await pr.getCopilotTimelineEvents(pr, false, !this._model.isInitialized);
274274
let latestEvent = copilotEventToStatus(copilotEvents[copilotEvents.length - 1]);
275275
if (latestEvent === CopilotPRStatus.None) {
276276
if (!COPILOT_ACCOUNTS[pr.author.login]) {
@@ -315,7 +315,7 @@ export class CopilotPRWatcher extends Disposable {
315315

316316
for (const pr of items) {
317317
unseenKeys.delete(this._model.makeKey(pr.remote.owner, pr.remote.repositoryName, pr.number));
318-
const copilotEvents = await pr.getCopilotTimelineEvents(pr);
318+
const copilotEvents = await pr.getCopilotTimelineEvents(pr, false, !this._model.isInitialized);
319319
let latestEvent = copilotEventToStatus(copilotEvents[copilotEvents.length - 1]);
320320
if (latestEvent === CopilotPRStatus.None) {
321321
if (!COPILOT_ACCOUNTS[pr.author.login]) {

src/github/folderRepositoryManager.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,10 +2130,10 @@ export class FolderRepositoryManager extends Disposable {
21302130
useCache: boolean = false,
21312131
): Promise<PullRequestModel | undefined> {
21322132
const githubRepo = await this.resolveItem(owner, repositoryName);
2133-
Logger.appendLine(`Found GitHub repo for pr #${pullRequestNumber}: ${githubRepo ? 'yes' : 'no'}`, this.id);
2133+
Logger.trace(`Found GitHub repo for pr #${pullRequestNumber}: ${githubRepo ? 'yes' : 'no'}`, this.id);
21342134
if (githubRepo) {
21352135
const pr = await githubRepo.getPullRequest(pullRequestNumber, useCache);
2136-
Logger.appendLine(`Found GitHub pr repo for pr #${pullRequestNumber}: ${pr ? 'yes' : 'no'}`, this.id);
2136+
Logger.trace(`Found GitHub pr repo for pr #${pullRequestNumber}: ${pr ? 'yes' : 'no'}`, this.id);
21372137
return pr;
21382138
}
21392139
return undefined;
@@ -2144,10 +2144,14 @@ export class FolderRepositoryManager extends Disposable {
21442144
repositoryName: string,
21452145
pullRequestNumber: number,
21462146
withComments: boolean = false,
2147+
useCache: boolean = false
21472148
): Promise<IssueModel | undefined> {
21482149
const githubRepo = await this.resolveItem(owner, repositoryName);
2150+
Logger.trace(`Found GitHub repo for issue #${pullRequestNumber}: ${githubRepo ? 'yes' : 'no'}`, this.id);
21492151
if (githubRepo) {
2150-
return githubRepo.getIssue(pullRequestNumber, withComments);
2152+
const issue = await githubRepo.getIssue(pullRequestNumber, withComments, useCache);
2153+
Logger.trace(`Found GitHub issue repo for issue #${pullRequestNumber}: ${issue ? 'yes' : 'no'}`, this.id);
2154+
return issue;
21512155
}
21522156
return undefined;
21532157
}

src/github/githubRepository.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ export class GitHubRepository extends Disposable {
177177
value.model.dispose();
178178
}
179179
});
180+
private _issueModelsByNumber: LRUCache<number, { model: IssueModel, disposables: vscode.Disposable[] }> = new LRUCache({
181+
maxAge: 1000 * 60 * 60 * 4 /* 4 hours */, stale: true, updateAgeOnGet: true,
182+
dispose: (_key, value) => {
183+
disposeAll(value.disposables);
184+
value.model.dispose();
185+
}
186+
});
180187
// eslint-disable-next-line rulesdir/no-any-except-union-method-signature
181188
private _queriesSchema: any;
182189
private _areQueriesLimited: boolean = false;
@@ -206,10 +213,18 @@ export class GitHubRepository extends Disposable {
206213
return this._pullRequestModelsByNumber.get(prNumber)?.model;
207214
}
208215

216+
getExistingIssueModel(issueNumber: number): IssueModel | undefined {
217+
return this._issueModelsByNumber.get(issueNumber)?.model;
218+
}
219+
209220
get pullRequestModels(): PullRequestModel[] {
210221
return Array.from(this._pullRequestModelsByNumber.values().map(value => value.model));
211222
}
212223

224+
get issueModels(): IssueModel[] {
225+
return Array.from(this._issueModelsByNumber.values().map(value => value.model));
226+
}
227+
213228
public async ensureCommentsController(): Promise<void> {
214229
try {
215230
if (this.commentsController) {
@@ -1009,6 +1024,19 @@ export class GitHubRepository extends Disposable {
10091024
return model;
10101025
}
10111026

1027+
private createOrUpdateIssueModel(issue: Issue): IssueModel {
1028+
let model = this._issueModelsByNumber.get(issue.number)?.model;
1029+
if (model) {
1030+
model.update(issue);
1031+
} else {
1032+
model = new IssueModel(this.telemetry, this, this.remote, issue);
1033+
// No issue-specific event emitters yet; store empty disposables list for symmetry/cleanup
1034+
const disposables: vscode.Disposable[] = [];
1035+
this._issueModelsByNumber.set(issue.number, { model, disposables });
1036+
}
1037+
return model;
1038+
}
1039+
10121040
private _onPullRequestModelChanged(model: PullRequestModel, change: IssueChangeEvent): void {
10131041
this._onDidChangePullRequests.fire([{ model, event: change }]);
10141042
}
@@ -1094,14 +1122,23 @@ export class GitHubRepository extends Disposable {
10941122
}
10951123

10961124
Logger.debug(`Fetch pull request ${id} - done`, this.id);
1097-
return this.createOrUpdatePullRequestModel(await parseGraphQLPullRequest(data.repository.pullRequest, this));
1125+
const pr = this.createOrUpdatePullRequestModel(await parseGraphQLPullRequest(data.repository.pullRequest, this));
1126+
await pr.getLastUpdateTime(new Date(pr.item.updatedAt));
1127+
return pr;
10981128
} catch (e) {
10991129
Logger.error(`Unable to fetch PR: ${e}`, this.id);
11001130
return;
11011131
}
11021132
}
11031133

1104-
async getIssue(id: number, withComments: boolean = false): Promise<IssueModel | undefined> {
1134+
async getIssue(id: number, withComments: boolean = false, useCache: boolean = false): Promise<IssueModel | undefined> {
1135+
if (useCache) {
1136+
const cached = this._issueModelsByNumber.get(id)?.model;
1137+
if (cached) {
1138+
Logger.debug(`Using cached issue model for ${id}`, this.id);
1139+
return cached;
1140+
}
1141+
}
11051142
try {
11061143
Logger.debug(`Fetch issue ${id} - enter`, this.id);
11071144
const { query, remote, schema } = await this.ensure();
@@ -1121,7 +1158,9 @@ export class GitHubRepository extends Disposable {
11211158
}
11221159
Logger.debug(`Fetch issue ${id} - done`, this.id);
11231160

1124-
return new IssueModel(this.telemetry, this, remote, await parseGraphQLIssue(data.repository.issue, this));
1161+
const issue = this.createOrUpdateIssueModel(await parseGraphQLIssue(data.repository.issue, this));
1162+
await issue.getLastUpdateTime(new Date(issue.item.updatedAt));
1163+
return issue;
11251164
} catch (e) {
11261165
Logger.error(`Unable to fetch issue: ${e}`, this.id);
11271166
return;

0 commit comments

Comments
 (0)