Skip to content

Commit 38a7b01

Browse files
alexr00eslamashour1
authored andcommitted
Support GitHub's suggested changes (microsoft#4248)
Fixes microsoft#603
1 parent 60652b0 commit 38a7b01

5 files changed

Lines changed: 74 additions & 5 deletions

File tree

‎package.json‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,12 @@
870870
"category": "%command.pull.request.category%",
871871
"icon": "$(copy)"
872872
},
873+
{
874+
"command": "pr.applySuggestion",
875+
"title": "%command.pr.applySuggestion.title%",
876+
"category": "%command.pull.request.category%",
877+
"icon": "$(gift)"
878+
},
873879
{
874880
"command": "issue.createIssueFromSelection",
875881
"title": "%command.issue.createIssueFromSelection.title%",
@@ -1765,17 +1771,22 @@
17651771
"comments/comment/title": [
17661772
{
17671773
"command": "pr.copyCommentLink",
1768-
"group": "inline@0",
1774+
"group": "inline@1",
17691775
"when": "commentController =~ /^github-(browse|review)/ && comment =~ /canEdit/"
17701776
},
1777+
{
1778+
"command": "pr.applySuggestion",
1779+
"group": "inline@0",
1780+
"when": "commentController =~ /^github-review/ && comment =~ /hasSuggestion/"
1781+
},
17711782
{
17721783
"command": "pr.editComment",
1773-
"group": "inline@1",
1784+
"group": "inline@2",
17741785
"when": "commentController =~ /^github-(browse|review)/ && comment =~ /canEdit/"
17751786
},
17761787
{
17771788
"command": "pr.deleteComment",
1778-
"group": "inline@2",
1789+
"group": "inline@3",
17791790
"when": "commentController =~ /^github-(browse|review)/ && comment =~ /canDelete/"
17801791
}
17811792
],

‎package.nls.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
"command.pr.goToNextDiffInPr.title": "Go to Next Diff in Pull Request",
165165
"command.pr.goToPreviousDiffInPr.title": "Go to Previous Diff in Pull Request",
166166
"command.pr.copyCommentLink.title": "Copy Comment Link",
167+
"command.pr.applySuggestion.title": "Apply Suggestion",
167168
"command.issues.category": "GitHub Issues",
168169
"command.issue.createIssueFromSelection.title": "Create Issue From Selection",
169170
"command.issue.createIssueFromClipboard.title": "Create Issue From Clipboard",

‎src/commands.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { PullRequestOverviewPanel } from './github/pullRequestOverview';
2525
import { RepositoriesManager } from './github/repositoriesManager';
2626
import { getIssuesUrl, getPullsUrl, isInCodespaces, vscodeDevPrLink } from './github/utils';
2727
import { PullRequestsTreeDataProvider } from './view/prsTreeDataProvider';
28+
import { ReviewCommentController } from './view/reviewCommentController';
2829
import { ReviewManager } from './view/reviewManager';
2930
import { CategoryTreeNode } from './view/treeNodes/categoryNode';
3031
import { CommitNode } from './view/treeNodes/commitNode';
@@ -1136,6 +1137,20 @@ export function registerCommands(
11361137
}
11371138
}));
11381139

1140+
context.subscriptions.push(
1141+
vscode.commands.registerCommand('pr.applySuggestion', async (comment: GHPRComment) => {
1142+
/* __GDPR__
1143+
"pr.applySuggestion" : {}
1144+
*/
1145+
telemetry.sendTelemetryEvent('pr.applySuggestion');
1146+
1147+
const handler = resolveCommentHandler(comment.parent);
1148+
1149+
if (handler instanceof ReviewCommentController) {
1150+
handler.applySuggestion(comment);
1151+
}
1152+
}));
1153+
11391154
function goToNextPrevDiff(diffs: vscode.LineChange[], next: boolean) {
11401155
const tab = vscode.window.tabGroups.activeTabGroup.activeTab;
11411156
const input = tab?.input;

‎src/github/prComment.ts‎

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,27 +215,53 @@ export class GHPRComment extends CommentBase {
215215
contextValues.push('canDelete');
216216
}
217217

218+
if (this.suggestion) {
219+
contextValues.push('hasSuggestion');
220+
}
221+
218222
this.contextValue = contextValues.join(',');
219223
this.timestamp = new Date(comment.createdAt);
220224
}
221225

226+
get suggestion(): string | undefined {
227+
const suggestionBody = this.rawComment.body.match(/```suggestion(\n|\r\n)(.*)(\n|\r\n)```/);
228+
if (suggestionBody?.length === 4) {
229+
return suggestionBody[2];
230+
}
231+
}
232+
222233
public commentEditId() {
223234
return this.commentId;
224235
}
225236

237+
private replaceSuggestion(body: string) {
238+
return body.replace(/```suggestion(\n|\r\n)(.*)(\n|\r\n)```/, (_substring: string, ...args: any[]) => {
239+
return `***
240+
Suggested change:
241+
\`\`\`
242+
${args[1]}
243+
\`\`\`
244+
***`;
245+
});
246+
}
247+
226248
set body(body: string | vscode.MarkdownString) {
227249
this._rawBody = body;
228250
}
229251

230252
get body(): string | vscode.MarkdownString {
231-
if (this._rawBody instanceof vscode.MarkdownString) {
253+
if (this.mode === vscode.CommentMode.Editing) {
232254
return this._rawBody;
233255
}
256+
if (this._rawBody instanceof vscode.MarkdownString) {
257+
return new vscode.MarkdownString(this.replaceSuggestion(this._rawBody.value));
258+
}
234259
const linkified = this._rawBody.replace(/([^\[]|^)\@([^\s]+)/, (substring) => {
235260
const username = substring.substring(substring.startsWith('@') ? 1 : 2);
236261
return `${substring.startsWith('@') ? '' : substring.charAt(0)}[@${username}](${path.dirname(this.rawComment.user!.url)}/${username})`;
237262
});
238-
return new vscode.MarkdownString(linkified);
263+
264+
return new vscode.MarkdownString(this.replaceSuggestion(linkified));
239265
}
240266

241267
protected getCancelEditBody() {

‎src/view/reviewCommentController.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,22 @@ export class ReviewCommentController
889889
}
890890

891891
// #endregion
892+
893+
async applySuggestion(comment: GHPRComment) {
894+
const suggestion = comment.suggestion;
895+
if (!suggestion) {
896+
throw new Error('Comment doesn\'t contain a suggestion');
897+
}
898+
const range = comment.parent.range;
899+
const editor = vscode.window.visibleTextEditors.find(editor => comment.parent.uri.toString() === editor.document.uri.toString());
900+
if (!editor) {
901+
throw new Error('Cannot find the editor to apply the suggestion to.');
902+
}
903+
await editor.edit(builder => {
904+
builder.replace(range, suggestion);
905+
});
906+
}
907+
892908
public dispose() {
893909
unregisterCommentHandler(this._commentHandlerId);
894910
this._localToDispose.forEach(d => d.dispose());

0 commit comments

Comments
 (0)