Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@
"dark": "resources/icons/dark/open-file.svg"
}
},
{
"command": "review.suggestDiff",
"title": "Suggest Edit"
},
{
"command": "pr.refreshList",
"title": "Refresh Pull Requests List",
Expand Down Expand Up @@ -329,6 +333,13 @@
"group": "navigation",
"when": "resourceScheme =~ /^review$/"
}
],
"scm/title": [
{
"command": "review.suggestDiff",
"when": "scmProvider == git && github:inReviewMode",
"group": "inline"
}
]
}
},
Expand Down
15 changes: 15 additions & 0 deletions preview-src/pullRequestOverviewRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,21 @@ class CommentNode {
reviewCommentContainer.appendChild(commentHeader);
reviewCommentContainer.appendChild(this._commentBody);

if (this._comment.body.indexOf('```diff') > -1) {
const replyButton = document.createElement('button');
replyButton.textContent = 'Apply Patch';
replyButton.onclick = _ => {
this._messageHandler.postMessage({
command: 'pr.apply-patch',
args: {
comment: this._comment
}
});
}

this._commentBody.appendChild(replyButton);
}

return this._commentContainer;
}

Expand Down
43 changes: 43 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
'use strict';

import * as vscode from 'vscode';
import * as pathLib from 'path';
import * as Github from '@octokit/rest';
import { ReviewManager } from './view/reviewManager';
import { PullRequestOverviewPanel } from './github/pullRequestOverview';
Expand All @@ -18,6 +19,8 @@ import { getDiffLineByPosition, getZeroBased } from './common/diffPositionMappin
import { DiffChangeType } from './common/diffHunk';
import { DescriptionNode } from './view/treeNodes/descriptionNode';
import { listHosts, deleteToken } from './authentication/keychain';
import { writeFile, unlink } from 'fs';
import Logger from './common/logger';
import { GitErrorCodes } from './typings/git';

const _onDidUpdatePR = new vscode.EventEmitter<Github.PullRequestsGetResponse>();
Expand Down Expand Up @@ -58,6 +61,46 @@ export function registerCommands(context: vscode.ExtensionContext, prManager: IP
telemetry.on('pr.openInGitHub');
}));

context.subscriptions.push(vscode.commands.registerCommand('review.suggestDiff', async (e) => {
try {
const diff = await prManager.repository.diff(true);
if (!diff) {
vscode.window.showWarningMessage('There are no staged changes for suggestions.');
return;
}
const suggestEditMessage = e.inputBox.value ? `${e.inputBox.value}\n` : '';
const suggestEditText = `${suggestEditMessage}\`\`\`diff\n${diff}\n\`\`\``;
await prManager.createIssueComment(prManager.activePullRequest, suggestEditText);
e.inputBox.value = '';

// Reset HEAD and then apply reverse diff
await vscode.commands.executeCommand('git.unstageAll');

const tempFilePath = pathLib.resolve(vscode.workspace.rootPath, '.git', `${prManager.activePullRequest.prNumber}.diff`);
writeFile(tempFilePath, diff, {}, async (writeError) => {
if (writeError) {
throw writeError;
}

try {
await prManager.repository.apply(tempFilePath, true);

unlink(tempFilePath, (err) => {
if (err) {
throw err;
}
});
} catch (err) {
Logger.appendLine(`Applying patch failed: ${err}`);
vscode.window.showErrorMessage(`Applying patch failed: ${formatError(err)}`);
}
});
} catch (err) {
Logger.appendLine(`Applying patch failed: ${err}`);
vscode.window.showErrorMessage(`Applying patch failed: ${formatError(err)}`);
}
}));

context.subscriptions.push(vscode.commands.registerCommand('pr.openFileInGitHub', (e: GitFileChangeNode) => {
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(e.blobUrl));
}));
Expand Down
37 changes: 37 additions & 0 deletions src/github/pullRequestOverview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { onDidUpdatePR } from '../commands';
import { formatError } from '../common/utils';
import { GitErrorCodes } from '../typings/git';
import { Comment } from '../common/comment';
import { writeFile, unlink } from 'fs';
import Logger from '../common/logger';

interface IRequestMessage<T> {
req: string;
Expand Down Expand Up @@ -218,11 +220,46 @@ export class PullRequestOverviewPanel {
return this.deleteComment(message);
case 'pr.edit-description':
return this.editDescription(message);
case 'pr.apply-patch':
return this.applyPatch(message);
case 'pr.edit-title':
return this.editTitle(message);
}
}

private applyPatch(message: IRequestMessage<{ comment: Comment }>): void {
try {
const comment = message.args.comment;
const regex = /```diff\n([\s\S]*)\n```/g;
const matches = regex.exec(comment.body);
const tempFilePath = path.resolve(vscode.workspace.rootPath, '.git', `${comment.id}.diff`);
writeFile(tempFilePath, matches[1], {}, async (writeError) => {
if (writeError) {
throw writeError;
}

try {
await this._pullRequestManager.repository.apply(tempFilePath);

// Need to mark conversation as resolved
unlink(tempFilePath, (err) => {
if (err) {
throw err;
}

this._replyMessage(message, { });
});
} catch (e) {
Logger.appendLine(`Applying patch failed: ${e}`);
vscode.window.showErrorMessage(`Applying patch failed: ${formatError(e)}`);
}
});
} catch (e) {
Logger.appendLine(`Applying patch failed: ${e}`);
vscode.window.showErrorMessage(`Applying patch failed: ${formatError(e)}`);
}
}

private editDescription(message: IRequestMessage<{ text: string }>) {
this._pullRequestManager.editPullRequest(this._pullRequest, { body: message.args.text }).then(result => {
this._replyMessage(message, { text: result.body });
Expand Down
4 changes: 4 additions & 0 deletions src/typings/git.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ export interface Repository {

clean(paths: string[]): Promise<void>;

clean(paths: string[]): Promise<void>;

apply(patch: string, reverse?: boolean): Promise<void>;
diff(cached?: boolean): Promise<string>;
diffWithHEAD(path: string): Promise<string>;
diffWith(ref: string, path: string): Promise<string>;
diffIndexWithHEAD(path: string): Promise<string>;
Expand Down