Skip to content

Commit e287ca8

Browse files
authored
Use @copilot placeholder when creating new comments in Copilot PRs (#7348)
Fixes #7284
1 parent 85b3b74 commit e287ca8

File tree

9 files changed

+87
-9
lines changed

9 files changed

+87
-9
lines changed

src/github/githubRepository.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Disposable, disposeAll } from '../common/lifecycle';
1212
import Logger from '../common/logger';
1313
import { GitHubRemote, parseRemote } from '../common/remote';
1414
import { ITelemetry } from '../common/telemetry';
15+
import { PullRequestCommentController } from '../view/pullRequestCommentController';
1516
import { PRCommentControllerRegistry } from '../view/pullRequestCommentControllerRegistry';
1617
import { mergeQuerySchemaWithShared, OctokitCommon, Schema } from './common';
1718
import { CredentialStore, GitHub } from './credentials';
@@ -204,7 +205,7 @@ export class GitHubRepository extends Disposable {
204205

205206
await this.ensure();
206207
this.commentsController = vscode.comments.createCommentController(
207-
`github-browse-${this.remote.normalizedHost}-${this.remote.owner}-${this.remote.repositoryName}`,
208+
`${PullRequestCommentController.PREFIX}-${this.remote.gitProtocol.normalizeUri()?.authority}-${this.remote.owner}-${this.remote.repositoryName}`,
208209
`Pull Request (${this.remote.owner}/${this.remote.repositoryName})`,
209210
);
210211
this.commentsHandler = new PRCommentControllerRegistry(this.commentsController, this.telemetry);

src/github/pullRequestModel.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,3 +2081,18 @@ export class PullRequestModel extends IssueModel<PullRequest> implements IPullRe
20812081
};
20822082
}
20832083
}
2084+
2085+
export async function isCopilotOnMyBehalf(pullRequestModel: PullRequestModel, currentUser: IAccount, coAuthors?: IAccount[]): Promise<boolean> {
2086+
if (!COPILOT_ACCOUNTS[pullRequestModel.author.login]) {
2087+
return false;
2088+
}
2089+
2090+
if (!coAuthors) {
2091+
coAuthors = await pullRequestModel.getCoAuthors();
2092+
}
2093+
if (!coAuthors || coAuthors.length === 0) {
2094+
return false;
2095+
}
2096+
2097+
return coAuthors.some(c => c.login === currentUser.login);
2098+
}

src/github/pullRequestOverview.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
ReviewState,
3434
} from './interface';
3535
import { IssueOverviewPanel } from './issueOverview';
36-
import { PullRequestModel } from './pullRequestModel';
36+
import { isCopilotOnMyBehalf, PullRequestModel } from './pullRequestModel';
3737
import { PullRequestView } from './pullRequestOverviewCommon';
3838
import { pickEmail, reviewersQuickPick } from './quickPicks';
3939
import { parseReviewers } from './utils';
@@ -222,6 +222,7 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
222222
isBranchUpToDateWithBase,
223223
mergeability,
224224
emailForCommit,
225+
coAuthors,
225226
] = await Promise.all([
226227
this._folderRepositoryManager.resolvePullRequest(
227228
pullRequestModel.remote.owner,
@@ -241,6 +242,7 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
241242
this._folderRepositoryManager.isHeadUpToDateWithBase(pullRequestModel),
242243
pullRequestModel.getMergeability(),
243244
this._folderRepositoryManager.getPreferredEmail(pullRequestModel),
245+
pullRequestModel.getCoAuthors()
244246
]);
245247
if (!pullRequest) {
246248
throw new Error(
@@ -293,7 +295,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
293295
isIssue: false,
294296
emailForCommit,
295297
currentUserReviewState: reviewState,
296-
revertable: pullRequest.state === GithubItemStateEnum.Merged
298+
revertable: pullRequest.state === GithubItemStateEnum.Merged,
299+
isCopilotOnMyBehalf: await isCopilotOnMyBehalf(pullRequest, currentUser, coAuthors)
297300
};
298301
this._postMessage({
299302
command: 'pr.initialize',

src/github/views.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export interface Issue {
6363
}
6464

6565
export interface PullRequest extends Issue {
66+
isCopilotOnMyBehalf: boolean;
6667
isCurrentlyCheckedOut: boolean;
6768
isRemoteBaseDeleted?: boolean;
6869
base: string;

src/view/commentControllBase.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66
import * as vscode from 'vscode';
77
import { Disposable } from '../common/lifecycle';
88
import { ITelemetry } from '../common/telemetry';
9+
import { Schemes } from '../common/uri';
910
import { FolderRepositoryManager } from '../github/folderRepositoryManager';
1011
import { GitHubRepository } from '../github/githubRepository';
11-
import { PullRequestModel } from '../github/pullRequestModel';
12+
import { isCopilotOnMyBehalf, PullRequestModel } from '../github/pullRequestModel';
1213

1314
export abstract class CommentControllerBase extends Disposable {
1415
constructor(
1516
protected _folderRepoManager: FolderRepositoryManager,
1617
protected _telemetry: ITelemetry
18+
1719
) {
1820
super();
21+
22+
this._register(vscode.window.onDidChangeActiveTextEditor(e => this.onDidChangeActiveTextEditor(e)));
1923
}
2024

2125
protected _commentController: vscode.CommentController;
@@ -37,4 +41,26 @@ export abstract class CommentControllerBase extends Disposable {
3741
}
3842
return githubRepositories;
3943
}
40-
}
44+
45+
protected abstract onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined);
46+
47+
protected async tryAddCopilotMention(editor: vscode.TextEditor, pullRequest: PullRequestModel) {
48+
if (editor.document.uri.scheme !== Schemes.Comment) {
49+
return;
50+
}
51+
52+
if (editor.document.lineCount < 1 || editor.document.lineAt(0).text.length > 0) {
53+
return;
54+
}
55+
56+
const currentUser = await this._folderRepoManager.getCurrentUser();
57+
if (!await isCopilotOnMyBehalf(pullRequest, currentUser)) {
58+
return;
59+
}
60+
61+
return editor.edit(editBuilder => {
62+
editBuilder.insert(new vscode.Position(0, 0), '@copilot');
63+
});
64+
}
65+
}
66+

src/view/pullRequestCommentController.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import { CommentControllerBase } from './commentControllBase';
3232

3333
export class PullRequestCommentController extends CommentControllerBase implements CommentHandler, CommentReactionHandler {
3434
private static ID = 'PullRequestCommentController';
35+
static readonly PREFIX = 'github-browse';
36+
3537
private _pendingCommentThreadAdds: GHPRCommentThread[] = [];
3638
private _commentHandlerId: string;
3739
private _commentThreadCache: { [key: string]: GHPRCommentThread[] } = {};
@@ -298,6 +300,22 @@ export class PullRequestCommentController extends CommentControllerBase implemen
298300
});
299301
}
300302

303+
protected override onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
304+
const activeTab = vscode.window.tabGroups.activeTabGroup.activeTab;
305+
const activeUri = activeTab?.input instanceof vscode.TabInputText ? activeTab.input.uri : (activeTab?.input instanceof vscode.TabInputTextDiff ? activeTab.input.original : undefined);
306+
307+
if (editor === undefined || !editor.document.uri.authority.startsWith(PullRequestCommentController.PREFIX) || !activeUri || (activeUri.scheme !== Schemes.Pr)) {
308+
return;
309+
}
310+
311+
const params = fromPRUri(activeUri);
312+
if (!params || params.prNumber !== this.pullRequestModel.number) {
313+
return;
314+
}
315+
316+
return this.tryAddCopilotMention(editor, this.pullRequestModel);
317+
}
318+
301319
hasCommentThread(thread: GHPRCommentThread): boolean {
302320
if (thread.uri.scheme !== Schemes.Pr) {
303321
return false;

src/view/reviewCommentController.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export interface SuggestionInformation {
4949

5050
export class ReviewCommentController extends CommentControllerBase implements CommentHandler, vscode.CommentingRangeProvider2, CommentReactionHandler {
5151
private static readonly ID = 'ReviewCommentController';
52+
private static readonly PREFIX = 'github-review';
5253
private _commentHandlerId: string;
5354

5455
// Note: marked as protected so that tests can verify caches have been updated correctly without breaking type safety
@@ -73,7 +74,7 @@ export class ReviewCommentController extends CommentControllerBase implements Co
7374
super(folderRepoManager, telemetry);
7475
this._context = this._folderRepoManager.context;
7576
this._commentController = this._register(vscode.comments.createCommentController(
76-
`github-review-${folderRepoManager.activePullRequest?.remote.owner}-${folderRepoManager.activePullRequest?.remote.owner}-${folderRepoManager.activePullRequest!.number}`,
77+
`${ReviewCommentController.PREFIX}-${folderRepoManager.activePullRequest?.remote.owner}-${folderRepoManager.activePullRequest?.remote.owner}-${folderRepoManager.activePullRequest!.number}`,
7778
vscode.l10n.t('Pull Request ({0})', folderRepoManager.activePullRequest!.title),
7879
));
7980
this._commentController.commentingRangeProvider = this as vscode.CommentingRangeProvider;
@@ -345,7 +346,6 @@ export class ReviewCommentController extends CommentControllerBase implements Co
345346
this.updateResourcesWithCommentingRanges();
346347
}),
347348
);
348-
this._register(vscode.window.onDidChangeActiveTextEditor(e => this.onDidChangeActiveTextEditor(e)));
349349
}
350350

351351
private _findMatchingThread(thread: IReviewThread): { threadMap: { [key: string]: GHPRCommentThread[] }, index: number } {
@@ -371,9 +371,19 @@ export class ReviewCommentController extends CommentControllerBase implements Co
371371
}
372372

373373
private _commentContentChangedListener: vscode.Disposable | undefined;
374-
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
374+
protected onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
375375
this._commentContentChangedListener?.dispose();
376376
this._commentContentChangedListener = undefined;
377+
378+
const activeTab = vscode.window.tabGroups.activeTabGroup.activeTab;
379+
const activeUri = activeTab?.input instanceof vscode.TabInputText ? activeTab.input.uri : (activeTab?.input instanceof vscode.TabInputTextDiff ? activeTab.input.modified : undefined);
380+
381+
if (editor && activeUri && editor.document.uri.authority.startsWith(ReviewCommentController.PREFIX) && (activeUri.scheme === Schemes.File)) {
382+
if (this._folderRepoManager.activePullRequest && activeUri.toString().startsWith(this._repository.rootUri.toString())) {
383+
this.tryAddCopilotMention(editor, this._folderRepoManager.activePullRequest);
384+
}
385+
}
386+
377387
if (editor?.document.uri.scheme !== Schemes.Comment) {
378388
return;
379389
}

webviews/components/comment.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ const CommentReactions = ({ reactions }: CommentReactionsProps) => {
340340

341341
export function AddComment({
342342
pendingCommentText,
343+
isCopilotOnMyBehalf,
343344
state,
344345
hasWritePermission,
345346
isIssue,
@@ -412,6 +413,8 @@ export function AddComment({
412413
}
413414
: commentMethods(isIssue);
414415

416+
const commentStartingText = pendingCommentText ?? (isCopilotOnMyBehalf ? '@copilot' : '');
417+
415418
return (
416419
<form id="comment-form" ref={form as React.MutableRefObject<HTMLFormElement>} className="comment-form main-comment-form" onSubmit={() => submit(textareaRef.current?.value ?? '')}>
417420
<textarea
@@ -420,7 +423,7 @@ export function AddComment({
420423
ref={textareaRef as React.MutableRefObject<HTMLTextAreaElement>}
421424
onInput={({ target }) => updatePR({ pendingCommentText: (target as any).value })}
422425
onKeyDown={onKeyDown}
423-
value={pendingCommentText}
426+
value={commentStartingText}
424427
placeholder="Leave a comment"
425428
/>
426429
<div className="form-actions">

webviews/editorWebview/test/builder/pullRequest.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,6 @@ export const PullRequestBuilder = createBuilderClass<PullRequest>()({
5959
busy: { default: undefined },
6060
lastReviewType: { default: undefined },
6161
canAssignCopilot: { default: false },
62+
isCopilotOnMyBehalf: { default: false },
6263
reactions: { default: [] },
6364
});

0 commit comments

Comments
 (0)