diff --git a/src/@types/vscode.proposed.chatSessionsProvider.d.ts b/src/@types/vscode.proposed.chatSessionsProvider.d.ts index 06d0649b18..6a394c158b 100644 --- a/src/@types/vscode.proposed.chatSessionsProvider.d.ts +++ b/src/@types/vscode.proposed.chatSessionsProvider.d.ts @@ -594,8 +594,15 @@ declare module 'vscode' { /** * The initial option selections for the session, provided with the first request. * Contains the options the user selected (or defaults) before the session was created. + * + * @deprecated Use `inputState` instead */ readonly initialSessionOptions?: ReadonlyArray<{ optionId: string; value: string | ChatSessionProviderOptionItem }>; + + /** + * The current input state of the chat session. + */ + readonly inputState: ChatSessionInputState; } export interface ChatSessionCapabilities { @@ -692,6 +699,8 @@ declare module 'vscode' { * * These commands will be displayed at the bottom of the group. * + * For extensions using the legacy `commands` API, these commands are passed the sessionResource as the first argument. + * * For extensions that use the new `provideChatSessionInputState` API, these commands are passed a context object * `{ inputState: ChatSessionInputState; sessionResource: Uri | undefined }` that they can use to determine which session and options they are being invoked for. */ diff --git a/src/github/pullRequestModel.ts b/src/github/pullRequestModel.ts index f37f2feda8..2a88e65034 100644 --- a/src/github/pullRequestModel.ts +++ b/src/github/pullRequestModel.ts @@ -751,7 +751,7 @@ export class PullRequestModel extends IssueModel implements IPullRe pullRequestId: this.graphNodeId, pullRequestReviewId: pendingReviewId, startLine: startLine === endLine ? undefined : startLine, - line: (endLine === undefined) ? 0 : endLine, + line: endLine, side, subjectType: (startLine === undefined || endLine === undefined) ? SubjectType.FILE : SubjectType.LINE } diff --git a/src/test/view/reviewCommentController.test.ts b/src/test/view/reviewCommentController.test.ts index 0dead8909e..1c1699e121 100644 --- a/src/test/view/reviewCommentController.test.ts +++ b/src/test/view/reviewCommentController.test.ts @@ -339,5 +339,106 @@ describe('ReviewCommentController', function () { assert.strictEqual(Object.keys(workspaceFileChangeCommentThreads)[0], fileName); assert.strictEqual(workspaceFileChangeCommentThreads[fileName].length, 1); }); + + it('creates a file-scoped comment on an empty thread', async function () { + const fileName = 'data/products.json'; + const uri = vscode.Uri.parse(`${repository.rootUri.toString()}/${fileName}`); + await activePullRequest.initializeReviewThreadCache(); + const localFileChanges = [createLocalFileChange(uri, fileName, repository.rootUri)]; + const reviewModel = new ReviewModel(); + reviewModel.localFileChanges = localFileChanges; + const reviewCommentController = new TestReviewCommentController( + reviewManager, + manager, + repository, + reviewModel, + gitApiImpl, + telemetry + ); + const thread = createGHPRCommentThread('review-1.2', uri); + thread.range = undefined as any; + + sinon.stub(activePullRequest, 'validateDraftMode').returns(Promise.resolve(false)); + sinon.stub(activePullRequest, 'getReviewThreads').returns(Promise.resolve([])); + sinon.stub(activePullRequest, 'getPendingReviewId').returns(Promise.resolve(undefined)); + + sinon.stub(manager, 'getCurrentUser').returns(Promise.resolve({ + login: 'rmacfarlane', + url: 'https://github.com/rmacfarlane', + id: '123', + accountType: AccountType.User + })); + + sinon.stub(vscode.workspace, 'getWorkspaceFolder').returns({ + uri: repository.rootUri, + name: '', + index: 0, + }); + + sinon.stub(vscode.workspace, 'asRelativePath').callsFake((pathOrUri: string | vscode.Uri): string => { + const path = pathOrUri.toString(); + return path.substring('/root/'.length); + }); + + sinon.stub(repository, 'diffWith').returns(Promise.resolve('')); + + await reviewCommentController.initialize(); + + githubRepo.queryProvider.expectGraphQLMutation( + { + mutation: schema.AddReviewThread, + variables: { + input: { + path: fileName, + body: 'file comment', + pullRequestId: activePullRequest.graphNodeId, + pullRequestReviewId: undefined, + startLine: undefined, + line: undefined, + side: 'RIGHT', + subjectType: 'FILE' + } + } + }, + { + data: { + addPullRequestReviewThread: { + thread: { + id: 2, + isResolved: false, + viewCanResolve: true, + path: fileName, + line: null, + startLine: null, + originalStartLine: null, + originalLine: null, + diffSide: 'RIGHT', + isOutdated: false, + subjectType: 'FILE', + comments: { + nodes: [ + { + databaseId: 2, + id: 2, + body: 'file comment', + commit: {}, + diffHunk: '', + reactionGroups: [], + author: {} + } + ] + } + } + } + } + } + ) + + const newReviewThreadPromise = asPromise(activePullRequest.onDidChangeReviewThreads); + await reviewCommentController.createOrReplyComment(thread, 'file comment', false); + await newReviewThreadPromise; + assert.strictEqual(thread.comments.length, 1); + assert.strictEqual(thread.comments[0].parent, thread); + }); }); });