Skip to content

Commit

Permalink
Create suggestions from working tree changes (#6153)
Browse files Browse the repository at this point in the history
Fixes #5256
  • Loading branch information
alexr00 committed Aug 21, 2024
1 parent d3c1a70 commit cb343dc
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 3 deletions.
17 changes: 17 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,12 @@
"title": "%command.review.requestChangesOnDotCom.title%",
"category": "%command.pull.request.category%"
},
{
"command": "review.createSuggestionsFromChanges",
"title": "%command.review.createSuggestionsFromChanges.title%",
"icon": "$(gift)",
"category": "%command.pull.request.category%"
},
{
"command": "pr.createPrMenuCreate",
"title": "%command.pr.createPrMenuCreate.title%",
Expand Down Expand Up @@ -1718,6 +1724,10 @@
"command": "review.requestChangesOnDotComDescription",
"when": "false"
},
{
"command": "review.createSuggestionsFromChanges",
"when": "false"
},
{
"command": "pr.refreshList",
"when": "gitHubOpenRepositoryCount != 0 && github:authenticated && github:hasGitHubRemotes"
Expand Down Expand Up @@ -2442,6 +2452,13 @@
"group": "navigation"
}
],
"scm/resourceGroup/context": [
{
"command": "review.createSuggestionsFromChanges",
"when": "scmProviderRootUri in github:reposInReviewMode && scmProvider =~ /^git|^remoteHub:github/ && scmResourceGroup == workingTree",
"group": "inline@-2"
}
],
"comments/commentThread/context": [
{
"command": "pr.createComment",
Expand Down
2 changes: 2 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@
"command.review.requestChanges.title": "Request Changes",
"command.review.approveOnDotCom.title": "Approve on github.com",
"command.review.requestChangesOnDotCom.title": "Request changes on github.com",
"command.review.createSuggestionsFromChanges.title": "Create Pull Request Suggestions",
"command.review.createSuggestionsFromChange.title": "Convert to Pull Request Suggestion",
"command.pr.refreshList.title": "Refresh Pull Requests List",
"command.pr.setFileListLayoutAsTree.title": "View as Tree",
"command.pr.setFileListLayoutAsFlat.title": "View as List",
Expand Down
12 changes: 12 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,18 @@ ${contents}
}),
);

context.subscriptions.push(vscode.commands.registerCommand('review.createSuggestionsFromChanges', async (value: { resourceStates: { resourceUri }[] }) => {
if (value.resourceStates.length === 0) {
return;
}
const folderManager = reposManager.getManagerForFile(value.resourceStates[0].resourceUri);
if (!folderManager || !folderManager.activePullRequest) {
return;
}
const reviewManager = ReviewManager.getReviewManagerForFolderManager(reviewsManager.reviewManagers, folderManager);
return reviewManager?.createSuggestionsFromChanges();
}));

context.subscriptions.push(
vscode.commands.registerCommand('pr.refreshChanges', _ => {
reviewsManager.reviewManagers.forEach(reviewManager => {
Expand Down
51 changes: 51 additions & 0 deletions src/view/reviewCommentController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { GitApiImpl } from '../api/api1';
import { CommentHandler, registerCommentHandler, unregisterCommentHandler } from '../commentHandlerResolver';
import { DiffSide, IReviewThread, SubjectType } from '../common/comment';
import { getCommentingRanges } from '../common/commentingRanges';
import { DiffChangeType, DiffHunk } from '../common/diffHunk';
import { mapNewPositionToOld, mapOldPositionToNew } from '../common/diffPositionMapping';
import { GitChangeType } from '../common/file';
import Logger from '../common/logger';
Expand Down Expand Up @@ -796,6 +797,56 @@ export class ReviewCommentController extends CommentControllerBase
}
}

private trimContextFromHunk(hunk: DiffHunk) {
let oldLineNumber = hunk.oldLineNumber;
let oldLength = hunk.oldLength;

// start at 1 to skip the control line
let i = 1;
for (; i < hunk.diffLines.length; i++) {
const line = hunk.diffLines[i];
if (line.type === DiffChangeType.Context) {
oldLineNumber++;
oldLength--;
} else {
break;
}
}
let j = hunk.diffLines.length - 1;
for (; j >= 0; j--) {
if (hunk.diffLines[j].type === DiffChangeType.Context) {
oldLength--;
} else {
break;
}
}
hunk.diffLines = hunk.diffLines.slice(i, j + 1);
hunk.oldLength = oldLength;
hunk.oldLineNumber = oldLineNumber;
}

async createSuggestionsFromChanges(file: vscode.Uri, hunk: DiffHunk) {
const activePr = this._folderRepoManager.activePullRequest;
if (!activePr) {
return;
}

this.trimContextFromHunk(hunk);

const path = this.gitRelativeRootPath(file.path);
const body = `\`\`\`suggestion
${hunk.diffLines.filter(line => (line.type === DiffChangeType.Add) || (line.type == DiffChangeType.Context)).map(line => line.text).join('\n')}
\`\`\``;
await activePr.createReviewThread(
body,
path,
hunk.oldLineNumber,
hunk.oldLineNumber + hunk.oldLength - 1,
DiffSide.RIGHT,
false,
);
}

private async createCommentOnResolve(thread: GHPRCommentThread, input: string): Promise<void> {
if (!this._folderRepoManager.activePullRequest) {
throw new Error('Cannot create comment on resolve without an active pull request.');
Expand Down
37 changes: 34 additions & 3 deletions src/view/reviewManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import * as nodePath from 'path';
import * as vscode from 'vscode';
import type { Branch, Repository } from '../api/api';
import { GitApiImpl, GitErrorCodes } from '../api/api1';
import { GitApiImpl, GitErrorCodes, Status } from '../api/api1';
import { openDescription } from '../commands';
import { DiffChangeType } from '../common/diffHunk';
import { DiffChangeType, parsePatch } from '../common/diffHunk';
import { commands } from '../common/executeCommands';
import { GitChangeType, InMemFileChange, SlimFileChange } from '../common/file';
import Logger from '../common/logger';
Expand Down Expand Up @@ -53,7 +53,7 @@ export class ReviewManager {
private _localToDispose: vscode.Disposable[] = [];
private _disposables: vscode.Disposable[];

private _reviewModel: ReviewModel = new ReviewModel();
private readonly _reviewModel: ReviewModel = new ReviewModel();
private _lastCommitSha?: string;
private _updateMessageShown: boolean = false;
private _validateStatusInProgress?: Promise<void>;
Expand Down Expand Up @@ -691,6 +691,37 @@ export class ReviewManager {
return Promise.all(reopenPromises);
}

async createSuggestionsFromChanges() {
let hasError: boolean = false;
const convertedFiles: vscode.Uri[] = [];
await vscode.window.withProgress({ location: vscode.ProgressLocation.Window, title: 'Converting changes to suggestions' }, async () => {
await Promise.all(this._folderRepoManager.repository.state.workingTreeChanges.map(async changeFile => {
if (changeFile.status !== Status.MODIFIED) {
return;
}
const diff = parsePatch(await this._folderRepoManager.repository.diffWithHEAD(changeFile.uri.fsPath));
try {
await Promise.all(diff.map(async hunk => {
await this._reviewCommentController?.createSuggestionsFromChanges(changeFile.uri, hunk);
convertedFiles.push(changeFile.uri);
}));
} catch (e) {
hasError = true;
}
}));
});
if (!hasError) {
const checkoutAllFilesResponse = vscode.l10n.t('Checkout all files');
vscode.window.showInformationMessage(vscode.l10n.t('All changes have been converted to suggestions.'), { modal: true, detail: vscode.l10n.t('Do you want to checkout all files and reset your working state to match the pull request state?') }, checkoutAllFilesResponse).then((response) => {
if (response === checkoutAllFilesResponse) {
return Promise.all(convertedFiles.map(changeFile => this._folderRepoManager.repository.checkout(changeFile.fsPath)));
}
});
} else {
vscode.window.showWarningMessage(vscode.l10n.t('Not all changes could be converted to suggestions.'), { detail: vscode.l10n.t('Some of the changes may be outside of commenting ranges.'), modal: true });
}
}

public async updateComments(): Promise<void> {
const branch = this._repository.state.HEAD;
if (!branch) {
Expand Down

0 comments on commit cb343dc

Please sign in to comment.