Skip to content

Commit a429afe

Browse files
committed
Merge branch 'main' into alexr00/like-crane
2 parents 70aea37 + ddae979 commit a429afe

File tree

10 files changed

+588
-74
lines changed

10 files changed

+588
-74
lines changed

.github/commands.json

Lines changed: 477 additions & 0 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"treeItemMarkdownLabel",
4242
"treeViewMarkdownMessage"
4343
],
44-
"version": "0.124.0",
44+
"version": "0.126.0",
4545
"publisher": "GitHub",
4646
"engines": {
4747
"vscode": "^1.107.0"
@@ -3610,12 +3610,12 @@
36103610
{
36113611
"command": "pr.checkoutFromDescription",
36123612
"group": "navigation@0",
3613-
"when": "chatSessionType == copilot-cloud-agent && workspaceFolderCount > 0"
3613+
"when": "chatSessionType == copilot-cloud-agent && workspaceFolderCount > 0 && github.vscode-pull-request-github.activated"
36143614
},
36153615
{
36163616
"command": "pr.applyChangesFromDescription",
36173617
"group": "navigation@1",
3618-
"when": "chatSessionType == copilot-cloud-agent && workspaceFolderCount > 0"
3618+
"when": "chatSessionType == copilot-cloud-agent && workspaceFolderCount > 0 && github.vscode-pull-request-github.activated"
36193619
}
36203620
]
36213621
},

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

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,6 @@ declare module 'vscode' {
4949
*/
5050
readonly onDidCommitChatSessionItem: Event<{ original: ChatSessionItem /** untitled */; modified: ChatSessionItem /** newly created */ }>;
5151

52-
/**
53-
* DEPRECATED: Will be removed!
54-
* Creates a new chat session.
55-
*
56-
* @param options Options for the new session including an optional initial prompt and history
57-
* @param token A cancellation token
58-
* @returns Metadata for the chat session
59-
*/
60-
provideNewChatSessionItem?(options: {
61-
/**
62-
* The chat request that initiated the session creation
63-
*/
64-
readonly request: ChatRequest;
65-
66-
/**
67-
* Additional metadata to use for session creation
68-
*/
69-
metadata?: any;
70-
}, token: CancellationToken): ProviderResult<ChatSessionItem>;
71-
7252
// #endregion
7353
}
7454

@@ -241,6 +221,14 @@ declare module 'vscode' {
241221
*/
242222
readonly onDidChangeChatSessionOptions?: Event<ChatSessionOptionChangeEvent>;
243223

224+
/**
225+
* Event that the provider can fire to signal that the available provider options have changed.
226+
*
227+
* When fired, the editor will re-query {@link ChatSessionContentProvider.provideChatSessionProviderOptions}
228+
* and update the UI to reflect the new option groups.
229+
*/
230+
readonly onDidChangeChatSessionProviderOptions?: Event<void>;
231+
244232
/**
245233
* Provides the chat session content for a given uri.
246234
*

src/common/executeCommands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export namespace contexts {
2020
export const ACTIVE_COMMENT_HAS_SUGGESTION = 'github:activeCommentHasSuggestion'; // Boolean indicating if the active comment has a suggestion
2121
export const CREATING = 'pr:creating';
2222
export const NOTIFICATION_COUNT = 'github:notificationCount'; // Number of notifications in the notifications view
23+
export const ACTIVATED = 'github.vscode-pull-request-github.activated'; // Boolean indicating if the extension has been activated
2324
}
2425

2526
export namespace commands {

src/extension.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { LiveShare } from 'vsls/vscode.js';
1010
import { PostCommitCommandsProvider, Repository } from './api/api';
1111
import { GitApiImpl } from './api/api1';
1212
import { registerCommands } from './commands';
13-
import { commands } from './common/executeCommands';
13+
import { commands, contexts } from './common/executeCommands';
1414
import { isSubmodule } from './common/gitUtils';
1515
import Logger from './common/logger';
1616
import * as PersistentState from './common/persistentState';
@@ -340,7 +340,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<GitApi
340340
telemetry = new ExperimentationTelemetry(new TelemetryReporter(ingestionKey));
341341
context.subscriptions.push(telemetry);
342342

343-
return await deferredActivate(context, showPRController);
343+
const deferred = await deferredActivate(context, showPRController);
344+
await commands.setContext(contexts.ACTIVATED, true);
345+
return deferred;
344346
}
345347

346348
async function setGitSettingContexts(context: vscode.ExtensionContext) {

src/github/activityBarViewProvider.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { MergeArguments, PullRequest, ReviewType } from './views';
1515
import { IComment } from '../common/comment';
1616
import { emojify, ensureEmojis } from '../common/emoji';
1717
import { disposeAll } from '../common/lifecycle';
18+
import Logger from '../common/logger';
1819
import { CHECKOUT_DEFAULT_BRANCH, CHECKOUT_PULL_REQUEST_BASE_BRANCH, DELETE_BRANCH_AFTER_MERGE, POST_DONE, PR_SETTINGS_NAMESPACE } from '../common/settingKeys';
1920
import { ReviewEvent } from '../common/timelineEvent';
2021
import { formatError } from '../common/utils';
@@ -25,7 +26,7 @@ import { ReviewManager } from '../view/reviewManager';
2526
export class PullRequestViewProvider extends WebviewViewBase implements vscode.WebviewViewProvider {
2627
public override readonly viewType = 'github:activePullRequest';
2728
private _existingReviewers: ReviewState[] = [];
28-
private _isUpdating: boolean = false;
29+
private _updatingPromise: Promise<unknown> | undefined;
2930

3031
constructor(
3132
extensionUri: vscode.Uri,
@@ -151,7 +152,7 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
151152
}
152153
this._prDisposables = [];
153154
this._prDisposables.push(pullRequestModel.onDidChange(e => {
154-
if ((e.state || e.comments || e.reviewers) && !this._isUpdating) {
155+
if ((e.state || e.comments || e.reviewers) && !this._updatingPromise) {
155156
this.updatePullRequest(pullRequestModel);
156157
}
157158
}));
@@ -160,10 +161,16 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
160161

161162
private _updatePendingVisibility: vscode.Disposable | undefined = undefined;
162163
public async updatePullRequest(pullRequestModel: PullRequestModel): Promise<void> {
163-
if (this._isUpdating) {
164-
throw new Error('Already updating pull request view');
164+
const isSamePullRequest = pullRequestModel.equals(this._item);
165+
if (this._updatingPromise && isSamePullRequest) {
166+
Logger.error('Already updating pull request view', PullRequestViewProvider.name);
167+
return;
168+
} else if (this._updatingPromise && !isSamePullRequest) {
169+
this._item = pullRequestModel;
170+
await this._updatingPromise;
171+
} else {
172+
this._item = pullRequestModel;
165173
}
166-
this._isUpdating = true;
167174

168175
try {
169176
if (this._view && !this._view.visible) {
@@ -178,7 +185,7 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
178185
this.registerPrSpecificListeners(pullRequestModel);
179186
}
180187
this._item = pullRequestModel;
181-
const [pullRequest, repositoryAccess, timelineEvents, requestedReviewers, branchInfo, defaultBranch, currentUser, viewerCanEdit, hasReviewDraft, coAuthors] = await Promise.all([
188+
const updatingPromise = Promise.all([
182189
this._folderRepositoryManager.resolvePullRequest(
183190
pullRequestModel.remote.owner,
184191
pullRequestModel.remote.repositoryName,
@@ -195,13 +202,19 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
195202
pullRequestModel.getCoAuthors(),
196203
ensureEmojis(this._folderRepositoryManager.context)
197204
]);
205+
this._updatingPromise = updatingPromise;
206+
const [pullRequest, repositoryAccess, timelineEvents, requestedReviewers, branchInfo, defaultBranch, currentUser, viewerCanEdit, hasReviewDraft, coAuthors] = await updatingPromise;
198207

199208
if (!pullRequest) {
200209
throw new Error(
201210
`Fail to resolve Pull Request #${pullRequestModel.number} in ${pullRequestModel.remote.owner}/${pullRequestModel.remote.repositoryName}`,
202211
);
203212
}
204213

214+
if (!this._item.equals(pullRequestModel)) {
215+
return;
216+
}
217+
205218
this._item = pullRequest;
206219
if (!this._view) {
207220
// If the there is no PR webview, then there is nothing else to update.
@@ -292,8 +305,6 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
292305

293306
} catch (e) {
294307
vscode.window.showErrorMessage(`Error updating active pull request view: ${formatError(e)}`);
295-
} finally {
296-
this._isUpdating = false;
297308
}
298309
}
299310

src/github/copilotPrWatcher.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ export class CopilotStateModel extends Disposable {
6969
continue;
7070
}
7171
this._states.set(key, { item, status });
72+
if (status === CopilotPRStatus.Started) {
73+
continue;
74+
}
7275
changedModels.push(item);
7376
changedKeys.push(key);
7477
}

src/github/pullRequestOverview.ts

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
5858
private _assignableUsers: { [key: string]: IAccount[] } = {};
5959

6060
private _prListeners: vscode.Disposable[] = [];
61-
private _isUpdating: boolean = false;
61+
private _updatingPromise: Promise<unknown> | undefined;
6262

6363
public static override async createOrShow(
6464
telemetry: ITelemetry,
@@ -173,7 +173,7 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
173173

174174
if (this._item) {
175175
this._prListeners.push(this._item.onDidChange(e => {
176-
if ((e.state || e.comments) && !this._isUpdating) {
176+
if ((e.state || e.comments) && !this._updatingPromise) {
177177
this.refreshPanel();
178178
}
179179
}));
@@ -236,32 +236,19 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
236236
}
237237

238238
protected override async updateItem(pullRequestModel: PullRequestModel): Promise<void> {
239-
this._item = pullRequestModel;
240-
241-
if (this._isUpdating) {
242-
throw new Error('Already updating pull request webview');
239+
const isSamePullRequest = pullRequestModel.equals(this._item);
240+
if (this._updatingPromise && isSamePullRequest) {
241+
Logger.error('Already updating pull request webview', PullRequestOverviewPanel.ID);
242+
return;
243+
} else if (this._updatingPromise && !isSamePullRequest) {
244+
this._item = pullRequestModel;
245+
await this._updatingPromise;
246+
} else {
247+
this._item = pullRequestModel;
243248
}
244-
this._isUpdating = true;
249+
245250
try {
246-
const [
247-
pullRequest,
248-
timelineEvents,
249-
defaultBranch,
250-
status,
251-
requestedReviewers,
252-
repositoryAccess,
253-
branchInfo,
254-
currentUser,
255-
viewerCanEdit,
256-
orgTeamsCount,
257-
mergeQueueMethod,
258-
isBranchUpToDateWithBase,
259-
mergeability,
260-
emailForCommit,
261-
coAuthors,
262-
hasReviewDraft,
263-
assignableUsers
264-
] = await Promise.all([
251+
const updatingPromise = Promise.all([
265252
this._folderRepositoryManager.resolvePullRequest(
266253
pullRequestModel.remote.owner,
267254
pullRequestModel.remote.repositoryName,
@@ -284,12 +271,39 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
284271
pullRequestModel.validateDraftMode(),
285272
this._folderRepositoryManager.getAssignableUsers()
286273
]);
274+
this._updatingPromise = updatingPromise;
275+
276+
const [
277+
pullRequest,
278+
timelineEvents,
279+
defaultBranch,
280+
status,
281+
requestedReviewers,
282+
repositoryAccess,
283+
branchInfo,
284+
currentUser,
285+
viewerCanEdit,
286+
orgTeamsCount,
287+
mergeQueueMethod,
288+
isBranchUpToDateWithBase,
289+
mergeability,
290+
emailForCommit,
291+
coAuthors,
292+
hasReviewDraft,
293+
assignableUsers
294+
] = await updatingPromise;
295+
this._updatingPromise = undefined;
287296
if (!pullRequest) {
288297
throw new Error(
289298
`Fail to resolve Pull Request #${pullRequestModel.number} in ${pullRequestModel.remote.owner}/${pullRequestModel.remote.repositoryName}`,
290299
);
291300
}
292301

302+
if (!this._item.equals(pullRequestModel)) {
303+
// Updated PR is no longer the current one
304+
return;
305+
}
306+
293307
this._item = pullRequest;
294308
this.registerPrListeners();
295309
this._repositoryDefaultBranch = defaultBranch!;
@@ -362,8 +376,6 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
362376
}
363377
} catch (e) {
364378
vscode.window.showErrorMessage(`Error updating pull request description: ${formatError(e)}`);
365-
} finally {
366-
this._isUpdating = false;
367379
}
368380
}
369381

src/github/pullRequestReviewCommon.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,30 @@ export namespace PullRequestReviewCommon {
462462
}
463463

464464
// Execute all deletions in parallel
465-
await performBranchDeletion(folderRepositoryManager, item, defaultBranch, branchInfo!, selectedActions);
465+
const deletedBranchTypes = await performBranchDeletion(folderRepositoryManager, item, defaultBranch, branchInfo!, selectedActions);
466+
467+
// Show notification to the user about what was deleted
468+
if (deletedBranchTypes.length > 0) {
469+
const wasLocalDeleted = deletedBranchTypes.includes('local');
470+
const wasRemoteDeleted = deletedBranchTypes.includes('remoteHead') || deletedBranchTypes.includes('remote');
471+
const branchName = branchInfo?.branch || item.head?.ref;
472+
473+
// Only show notification if we have a branch name
474+
if (branchName) {
475+
if (wasLocalDeleted && wasRemoteDeleted) {
476+
vscode.window.showInformationMessage(
477+
vscode.l10n.t('Deleted local and remote branches for {0}.', branchName)
478+
);
479+
} else if (wasLocalDeleted) {
480+
vscode.window.showInformationMessage(
481+
vscode.l10n.t('Deleted local branch {0}.', branchName)
482+
);
483+
} else {
484+
vscode.window.showInformationMessage(
485+
vscode.l10n.t('Deleted remote branch {0}.', branchName)
486+
);
487+
}
488+
}
489+
}
466490
}
467491
}

src/test/github/copilotPrWatcher.test.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@ describe('Copilot PR watcher', () => {
3131
model.set([{ item: pr, status: CopilotPRStatus.Started }]);
3232

3333
assert.strictEqual(model.get('octo', 'repo', 1), CopilotPRStatus.Started);
34-
assert.strictEqual(changeEvents, 1);
34+
assert.strictEqual(changeEvents, 0);
3535
assert.strictEqual(notifications.length, 0);
3636
assert.strictEqual(model.notifications.size, 0);
3737

3838
model.set([{ item: pr, status: CopilotPRStatus.Started }]);
39-
assert.strictEqual(changeEvents, 1);
39+
assert.strictEqual(changeEvents, 0);
4040

4141
model.setInitialized();
4242
const updated = createPullRequest('octo', 'repo', 1);
4343
model.set([{ item: updated, status: CopilotPRStatus.Completed }]);
4444

4545
assert.strictEqual(model.get('octo', 'repo', 1), CopilotPRStatus.Completed);
46-
assert.strictEqual(changeEvents, 2);
46+
assert.strictEqual(changeEvents, 1);
4747
assert.strictEqual(notifications.length, 1);
4848
assert.deepStrictEqual(notifications[0], [updated]);
4949
assert.ok(model.notifications.has('octo/repo#1'));
@@ -58,7 +58,7 @@ describe('Copilot PR watcher', () => {
5858

5959
model.setInitialized();
6060
const pr = createPullRequest('octo', 'repo', 42);
61-
model.set([{ item: pr, status: CopilotPRStatus.Started }]);
61+
model.set([{ item: pr, status: CopilotPRStatus.Completed }]);
6262

6363
assert.strictEqual(model.notifications.size, 1);
6464
assert.strictEqual(changeEvents, 1);
@@ -79,7 +79,7 @@ describe('Copilot PR watcher', () => {
7979

8080
model.setInitialized();
8181
const pr = createPullRequest('octo', 'repo', 5);
82-
model.set([{ item: pr, status: CopilotPRStatus.Started }]);
82+
model.set([{ item: pr, status: CopilotPRStatus.Completed }]);
8383
assert.strictEqual(model.notifications.size, 1);
8484
assert.strictEqual(notifications.length, 1);
8585

@@ -110,22 +110,18 @@ describe('Copilot PR watcher', () => {
110110
{ item: prThree, status: CopilotPRStatus.Completed }
111111
]);
112112

113-
assert.strictEqual(model.notifications.size, 3);
113+
assert.strictEqual(model.notifications.size, 2);
114114
assert.strictEqual(notifications.length, 1);
115-
assert.deepStrictEqual(notifications[0], [prOne, prTwo, prThree]);
116-
assert.strictEqual(model.getNotificationsCount('octo', 'repo'), 2);
115+
assert.deepStrictEqual(notifications[0], [prTwo, prThree]);
116+
assert.strictEqual(model.getNotificationsCount('octo', 'repo'), 1);
117117
assert.deepStrictEqual(model.keys().sort(), ['octo/repo#1', 'octo/repo#2', 'other/repo#3']);
118118

119119
model.clearAllNotifications('octo', 'repo');
120120
assert.strictEqual(model.notifications.size, 1);
121121
assert.strictEqual(model.getNotificationsCount('octo', 'repo'), 0);
122-
assert.strictEqual(notifications.length, 2);
123-
assert.deepStrictEqual(notifications[1], [prOne, prTwo]);
124122

125123
model.clearAllNotifications();
126124
assert.strictEqual(model.notifications.size, 0);
127-
assert.strictEqual(notifications.length, 3);
128-
assert.deepStrictEqual(notifications[2], [prThree]);
129125

130126
const counts = model.getCounts('octo', 'repo');
131127
assert.deepStrictEqual(counts, { total: 3, inProgress: 1, error: 1 });

0 commit comments

Comments
 (0)