Skip to content

Commit f43a077

Browse files
committed
Fix #40. Open blob in github if we can't open it locally.
1 parent d99eebb commit f43a077

9 files changed

Lines changed: 112 additions & 49 deletions

File tree

src/common/diffHunk.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
*/
99

1010
import * as path from 'path';
11-
import { getFileContent, writeTmpFile } from './file';
12-
import { GitChangeType, RichFileChange } from './file';
11+
import { getFileContent, writeTmpFile, GitChangeType, RichFileChange, SlimFileChange } from './file';
1312
import { Repository } from './repository';
1413
import { Comment } from './comment';
1514

@@ -263,22 +262,27 @@ export function getGitChangeType(status: string): GitChangeType {
263262
}
264263
}
265264

266-
export async function parseDiff(reviews: any[], repository: Repository, parentCommit: string): Promise<RichFileChange[]> {
267-
let richFileChanges: RichFileChange[] = [];
265+
export async function parseDiff(reviews: any[], repository: Repository, parentCommit: string): Promise<(RichFileChange | SlimFileChange)[]> {
266+
let fileChanges: (RichFileChange | SlimFileChange)[] = [];
268267
for (let i = 0; i < reviews.length; i++) {
269268
let review = reviews[i];
269+
if (!review.patch) {
270+
const gitChangeType = getGitChangeType(review.status);
271+
fileChanges.push(new SlimFileChange(review.blob_url, gitChangeType, review.filename));
272+
continue;
273+
}
274+
270275
if (review.status === 'modified') {
271276
let fileName = review.filename;
272-
273277
try {
274278
let originalContent = await getFileContent(repository.path, parentCommit, fileName);
275279
let richFileChange = await parseModifiedHunkComplete(originalContent, review.patch, fileName, fileName);
276280
richFileChange.blobUrl = review.blob_url;
277-
richFileChanges.push(richFileChange);
281+
fileChanges.push(richFileChange);
278282
} catch (e) {
279283
let richFileChange = await parseModifiedHunkFast(review.patch, fileName, fileName);
280284
richFileChange.blobUrl = review.blob_url;
281-
richFileChanges.push(richFileChange);
285+
fileChanges.push(richFileChange);
282286
}
283287
} else if (review.status === 'removed' || review.status === 'added' || review.status === 'renamed') {
284288
if (!review.patch) {
@@ -310,10 +314,10 @@ export async function parseDiff(reviews: any[], repository: Repository, parentCo
310314
new RichFileChange(emptyContentFilePath, contentFilePath, gitChangeType, fileName, diffHunks) :
311315
new RichFileChange(contentFilePath, emptyContentFilePath, gitChangeType, fileName, diffHunks);
312316
richFileChange.blobUrl = review.blob_url;
313-
richFileChanges.push(richFileChange);
317+
fileChanges.push(richFileChange);
314318
}
315319
}
316-
return richFileChanges;
320+
return fileChanges;
317321
}
318322

319323
export function parserCommentDiffHunk(comments: any[]): Comment[] {

src/common/file.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,12 @@ export class RichFileChange {
6464
public readonly fileName: string,
6565
public readonly diffHunks: DiffHunk[]
6666
) { }
67+
}
68+
69+
export class SlimFileChange {
70+
constructor(
71+
public readonly blobUrl: string,
72+
public readonly status: GitChangeType,
73+
public readonly fileName: string
74+
) { }
6775
}

src/common/resources.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as vscode from 'vscode';
77
import * as path from 'path';
88
import { GitChangeType } from './file';
9-
import { FileChangeNode } from '../view/treeNodes/fileChangeNode';
9+
import { FileChangeNode, RemoteFileChangeNode } from '../view/treeNodes/fileChangeNode';
1010

1111
export class Resource {
1212
static icons: any;
@@ -42,7 +42,7 @@ export class Resource {
4242
};
4343
}
4444

45-
static getFileStatusUri(element: FileChangeNode): vscode.Uri | { light: vscode.Uri, dark: vscode.Uri } {
45+
static getFileStatusUri(element: FileChangeNode | RemoteFileChangeNode): vscode.Uri | { light: vscode.Uri, dark: vscode.Uri } {
4646
let iconUri: vscode.Uri;
4747
let iconDarkUri: vscode.Uri;
4848

src/github/pullRequestManager.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -338,17 +338,7 @@ export class PullRequestManager implements IPullRequestManager {
338338
number: pullRequest.prNumber
339339
});
340340

341-
const largeChanges = data.filter(fileChange => !fileChange.patch);
342-
if (largeChanges.length) {
343-
const fileNames = largeChanges.map(change => change.filename).join(', ');
344-
vscode.window.showInformationMessage(`This pull request contains file changes that are too large to load: ${fileNames}`, 'Open in GitHub').then(result => {
345-
if (result === 'Open in GitHub') {
346-
vscode.commands.executeCommand('pr.openPullRequestInGitHub', pullRequest);
347-
}
348-
});
349-
}
350-
351-
return data.filter(fileChange => !!fileChange.patch);
341+
return data;
352342
}
353343

354344
async fullfillPullRequestCommitInfo(pullRequest: IPullRequestModel): Promise<void> {

src/view/prChangesTreeDataProvider.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as vscode from 'vscode';
77
import { Resource } from '../common/resources';
88
import { IPullRequestModel, IPullRequestManager } from '../github/interface';
9-
import { FileChangeNode } from './treeNodes/fileChangeNode';
9+
import { FileChangeNode, RemoteFileChangeNode } from './treeNodes/fileChangeNode';
1010
import { DescriptionNode } from './treeNodes/descriptionNode';
1111
import { TreeNode } from './treeNodes/treeNode';
1212
import { FilesCategoryNode } from './treeNodes/filesCategoryNode';
@@ -18,7 +18,7 @@ export class PullRequestChangesTreeDataProvider extends vscode.Disposable implem
1818
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
1919
private _disposables: vscode.Disposable[] = []
2020

21-
private _localFileChanges: FileChangeNode[] = [];
21+
private _localFileChanges: (FileChangeNode | RemoteFileChangeNode)[] = [];
2222
private _comments: Comment[] = [];
2323
private _pullrequest: IPullRequestModel = null;
2424
private _pullRequestManager: IPullRequestManager;
@@ -32,7 +32,7 @@ export class PullRequestChangesTreeDataProvider extends vscode.Disposable implem
3232
}));
3333
}
3434

35-
async showPullRequestFileChanges(pullRequestManager: IPullRequestManager, pullrequest: IPullRequestModel, fileChanges: FileChangeNode[], comments: Comment[]) {
35+
async showPullRequestFileChanges(pullRequestManager: IPullRequestManager, pullrequest: IPullRequestModel, fileChanges: (FileChangeNode | RemoteFileChangeNode)[], comments: Comment[]) {
3636
this._pullRequestManager = pullRequestManager;
3737
this._pullrequest = pullrequest;
3838
this._comments = comments;

src/view/reviewManager.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ import { getDiffLineByPosition, getLastDiffLine, mapCommentsToHead, mapHeadLineT
1010
import { toReviewUri, fromReviewUri } from '../common/uri';
1111
import { groupBy, formatError } from '../common/utils';
1212
import { Comment } from '../common/comment';
13-
import { GitChangeType } from '../common/file';
13+
import { GitChangeType, SlimFileChange } from '../common/file';
1414
import { GitErrorCodes } from '../common/gitError';
1515
import { IPullRequestModel, IPullRequestManager } from '../github/interface';
1616
import { Repository } from '../common/repository';
1717
import { PullRequestChangesTreeDataProvider } from './prChangesTreeDataProvider';
1818
import { GitContentProvider } from './gitContentProvider';
1919
import { DiffChangeType } from '../common/diffHunk';
20-
import { FileChangeNode } from './treeNodes/fileChangeNode';
20+
import { FileChangeNode, RemoteFileChangeNode, fileChangeNodeFilter } from './treeNodes/fileChangeNode';
2121
import Logger from '../common/logger';
2222
import { PullRequestsTreeDataProvider } from './prsTreeDataProvider';
2323
import { Configuration } from '../configuration';
@@ -29,8 +29,8 @@ export class ReviewManager implements vscode.DecorationProvider {
2929
private _disposables: vscode.Disposable[];
3030

3131
private _comments: Comment[] = [];
32-
private _localFileChanges: FileChangeNode[] = [];
33-
private _obsoleteFileChanges: FileChangeNode[] = [];
32+
private _localFileChanges: (FileChangeNode | RemoteFileChangeNode)[] = [];
33+
private _obsoleteFileChanges: (FileChangeNode | RemoteFileChangeNode)[] = [];
3434
private _lastCommitSha: string;
3535
private _updateMessageShown: boolean = false;
3636
private _updateCurrentBranch: boolean = false;
@@ -199,7 +199,7 @@ export class ReviewManager implements vscode.DecorationProvider {
199199
try {
200200
let uri = document.uri;
201201
let fileName = uri.path;
202-
let matchedFiles = this._localFileChanges.filter(fileChange => {
202+
let matchedFiles = fileChangeNodeFilter(this._localFileChanges).filter(fileChange => {
203203
if (uri.scheme === 'review') {
204204
return fileChange.fileName === fileName;
205205
} else {
@@ -356,8 +356,16 @@ export class ReviewManager implements vscode.DecorationProvider {
356356
await this._prManager.fullfillPullRequestCommitInfo(pr);
357357
let baseSha = pr.base.sha;
358358
let headSha = pr.head.sha;
359-
const richContentChanges = await parseDiff(data, this._repository, baseSha);
360-
this._localFileChanges = richContentChanges.map(change => {
359+
const contentChanges = await parseDiff(data, this._repository, baseSha);
360+
this._localFileChanges = contentChanges.map(change => {
361+
if (change instanceof SlimFileChange) {
362+
return new RemoteFileChangeNode(
363+
pr,
364+
change.status,
365+
change.fileName,
366+
change.blobUrl
367+
);
368+
}
361369
let changedItem = new FileChangeNode(
362370
pr,
363371
change.status,
@@ -533,9 +541,9 @@ export class ReviewManager implements vscode.DecorationProvider {
533541
// local file, we only provide active comments
534542
// TODO. for comments in deleted ranges, they should show on top of the first line.
535543
const fileName = document.uri.fsPath;
536-
const matchedFiles = this._localFileChanges.filter(fileChange => path.resolve(this._repository.path, fileChange.fileName) === fileName);
544+
const matchedFiles = fileChangeNodeFilter(this._localFileChanges).filter(fileChange => path.resolve(this._repository.path, fileChange.fileName) === fileName);
537545
if (matchedFiles && matchedFiles.length) {
538-
const matchedFile = matchedFiles[0];
546+
const matchedFile: FileChangeNode = matchedFiles[0];
539547

540548
let contentDiff: string;
541549
if (document.isDirty) {
@@ -674,10 +682,10 @@ export class ReviewManager implements vscode.DecorationProvider {
674682
this._workspaceCommentProvider = vscode.workspace.registerWorkspaceCommentProvider({
675683
onDidChangeCommentThreads: this._onDidChangeCommentThreads.event,
676684
provideWorkspaceComments: async (token: vscode.CancellationToken) => {
677-
const comments = await Promise.all(this._localFileChanges.map(async fileChange => {
685+
const comments = await Promise.all(fileChangeNodeFilter(this._localFileChanges).map(async fileChange => {
678686
return this.commentsToCommentThreads(fileChange.comments);
679687
}));
680-
const outdatedComments = this._obsoleteFileChanges.map(fileChange => {
688+
const outdatedComments = fileChangeNodeFilter(this._obsoleteFileChanges).map(fileChange => {
681689
return this.outdatedCommentsToCommentThreads(fileChange, fileChange.comments);
682690
});
683691
return [...comments, ...outdatedComments].reduce((prev, curr) => prev.concat(curr), []);
@@ -686,9 +694,13 @@ export class ReviewManager implements vscode.DecorationProvider {
686694
});
687695
}
688696

689-
private findMatchedFileChange(fileChanges: FileChangeNode[], uri: vscode.Uri) {
697+
private findMatchedFileChange(fileChanges: (FileChangeNode | RemoteFileChangeNode)[], uri: vscode.Uri): FileChangeNode {
690698
let query = JSON.parse(uri.query);
691699
let matchedFiles = fileChanges.filter(fileChange => {
700+
if (fileChange instanceof RemoteFileChangeNode) {
701+
return false;
702+
}
703+
692704
if (fileChange.fileName !== query.path) {
693705
return false;
694706
}
@@ -708,7 +720,7 @@ export class ReviewManager implements vscode.DecorationProvider {
708720
});
709721

710722
if (matchedFiles && matchedFiles.length) {
711-
return matchedFiles[0];
723+
return matchedFiles[0] as FileChangeNode;
712724
}
713725

714726
return null;
@@ -790,7 +802,7 @@ export class ReviewManager implements vscode.DecorationProvider {
790802

791803
async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
792804
let { path, commit } = fromReviewUri(uri);
793-
let changedItems = this._localFileChanges
805+
let changedItems = fileChangeNodeFilter(this._localFileChanges)
794806
.filter(change => change.fileName === path)
795807
.filter(fileChange => fileChange.sha === commit || (fileChange.parentSha ? fileChange.parentSha : `${fileChange.sha}^`) === commit);
796808

@@ -801,7 +813,7 @@ export class ReviewManager implements vscode.DecorationProvider {
801813
return ret.reduce((prev, curr) => prev.concat(...curr), []).join('\n');
802814
}
803815

804-
changedItems = this._obsoleteFileChanges
816+
changedItems = fileChangeNodeFilter(this._obsoleteFileChanges)
805817
.filter(change => change.fileName === path)
806818
.filter(fileChange => fileChange.sha === commit || (fileChange.parentSha ? fileChange.parentSha : `${fileChange.sha}^`) === commit);
807819

src/view/treeNodes/fileChangeNode.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,35 @@ import { TreeNode } from './treeNode';
1212
import { Comment } from '../../common/comment';
1313
import { getDiffLineByPosition, getZeroBased } from '../../common/diffPositionMapping';
1414

15+
export class RemoteFileChangeNode extends TreeNode implements vscode.TreeItem {
16+
public label: string;
17+
public iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
18+
public command: vscode.Command;
19+
20+
constructor(
21+
public readonly pullRequest: IPullRequestModel,
22+
public readonly status: GitChangeType,
23+
public readonly fileName: string,
24+
public readonly blobUrl: string
25+
) {
26+
super();
27+
this.label = fileName;
28+
this.iconPath = Resource.getFileStatusUri(this);
29+
30+
this.command = {
31+
title: 'show remote file',
32+
command: 'vscode.open',
33+
arguments: [
34+
vscode.Uri.parse(this.blobUrl)
35+
]
36+
};
37+
}
38+
39+
getTreeItem(): vscode.TreeItem {
40+
return this;
41+
}
42+
}
43+
1544
export class FileChangeNode extends TreeNode implements vscode.TreeItem {
1645
public label: string;
1746
public iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
@@ -77,4 +106,8 @@ export class FileChangeNode extends TreeNode implements vscode.TreeItem {
77106
getTreeItem(): vscode.TreeItem {
78107
return this;
79108
}
109+
}
110+
111+
export function fileChangeNodeFilter(nodes: (FileChangeNode | RemoteFileChangeNode)[]): FileChangeNode[] {
112+
return nodes.filter(node => node instanceof FileChangeNode) as FileChangeNode[];
80113
}

src/view/treeNodes/filesCategoryNode.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as vscode from 'vscode';
7-
import { FileChangeNode } from './fileChangeNode';
7+
import { FileChangeNode, RemoteFileChangeNode } from './fileChangeNode';
88
import { TreeNode } from './treeNode';
99

1010
export class FilesCategoryNode extends TreeNode implements vscode.TreeItem {
1111
public label: string = 'Files';
1212
public collapsibleState: vscode.TreeItemCollapsibleState;
1313

14-
constructor(private _fileChanges: FileChangeNode[]) {
14+
constructor(private _fileChanges: (FileChangeNode | RemoteFileChangeNode)[]) {
1515
super();
1616
this.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
1717
}

src/view/treeNodes/pullRequestNode.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ import * as vscode from 'vscode';
88
import { Comment } from '../../common/comment';
99
import { parseDiff } from '../../common/diffHunk';
1010
import { mapHeadLineToDiffHunkPosition, getZeroBased, getAbsolutePosition } from '../../common/diffPositionMapping';
11-
import { RichFileChange } from '../../common/file';
11+
import { RichFileChange, SlimFileChange } from '../../common/file';
1212
import Logger from '../../common/logger';
1313
import { Repository } from '../../common/repository';
1414
import { Resource } from '../../common/resources';
1515
import { toPRUri } from '../../common/uri';
1616
import { groupBy, formatError } from '../../common/utils';
1717
import { IPullRequestManager, IPullRequestModel } from '../../github/interface';
1818
import { DescriptionNode } from './descriptionNode';
19-
import { FileChangeNode } from './fileChangeNode';
19+
import { FileChangeNode, RemoteFileChangeNode } from './fileChangeNode';
2020
import { TreeNode } from './treeNode';
2121

2222
export class PRNode extends TreeNode {
23-
private _richContentChanges: RichFileChange[];
23+
private _contentChanges: (RichFileChange | SlimFileChange)[];
2424
private _commentsCache: Map<String, Comment[]>;
2525
private _documentCommentsProvider: vscode.Disposable;
2626

@@ -47,9 +47,17 @@ export class PRNode extends TreeNode {
4747
const comments = await this._prManager.getPullRequestComments(this.pullRequestModel);
4848
const data = await this._prManager.getPullRequestChangedFiles(this.pullRequestModel);
4949
await this._prManager.fullfillPullRequestCommitInfo(this.pullRequestModel);
50-
this._richContentChanges = await parseDiff(data, this.repository, this.pullRequestModel.base.sha);
50+
this._contentChanges = await parseDiff(data, this.repository, this.pullRequestModel.base.sha);
5151
this._commentsCache = new Map<String, Comment[]>();
52-
let fileChanges = this._richContentChanges.map(change => {
52+
let fileChanges = this._contentChanges.map(change => {
53+
if (change instanceof SlimFileChange) {
54+
return new RemoteFileChangeNode(
55+
this.pullRequestModel,
56+
change.status,
57+
change.fileName,
58+
change.blobUrl
59+
);
60+
}
5361
let fileInRepo = path.resolve(this.repository.path, change.fileName);
5462
let changedItem = new FileChangeNode(
5563
this.pullRequestModel,
@@ -102,12 +110,16 @@ export class PRNode extends TreeNode {
102110
let uri = document.uri;
103111
let params = JSON.parse(uri.query);
104112

105-
let fileChange = this._richContentChanges.find(change => change.fileName === params.fileName);
113+
let fileChange = this._contentChanges.find(change => change.fileName === params.fileName);
106114

107115
if (!fileChange) {
108116
return null;
109117
}
110118

119+
if (fileChange instanceof SlimFileChange) {
120+
return null;
121+
}
122+
111123
let isBase = params && params.base;
112124
let position = mapHeadLineToDiffHunkPosition(fileChange.diffHunks, '', range.start.line + 1, isBase);
113125

@@ -156,11 +168,15 @@ export class PRNode extends TreeNode {
156168
if (document.uri.scheme === 'pr') {
157169
let params = JSON.parse(document.uri.query);
158170
let isBase = params.base;
159-
let fileChange = this._richContentChanges.find(change => change.fileName === params.fileName);
171+
let fileChange = this._contentChanges.find(change => change.fileName === params.fileName);
160172
if (!fileChange) {
161173
return null;
162174
}
163175

176+
if (fileChange instanceof SlimFileChange) {
177+
return null;
178+
}
179+
164180
let commentingRanges: vscode.Range[] = [];
165181
let diffHunks = fileChange.diffHunks;
166182

0 commit comments

Comments
 (0)