提交 31760c4f 编写于 作者: P Peng Lyu

Singleton and commands registration

上级 bbb4adaa
......@@ -71,6 +71,10 @@
"command": "pr.pick",
"title": "Checkout Source Branch"
},
{
"command": "pr.openInGitHub",
"title": "Open in GitHub"
},
{
"command": "review.openFile",
"title": "Open File",
......@@ -83,12 +87,21 @@
"menus": {
"view/title": [{
"command": "pr.refreshList",
"when": "view == pr"
}],
"view/item/context": [{
"command": "pr.pick",
"when": "view == pr && viewItem == pullrequest"
"when": "view == pr",
"group": "navigation"
}],
"view/item/context": [
{
"command": "pr.pick",
"when": "view == pr && viewItem == pullrequest"
},{
"command": "pr.openInGitHub",
"when": "view == pr && viewItem == pullrequest"
},{
"command": "pr.openInGitHub",
"when": "view =~ /(pr|prStatus)/ && viewItem == filechange"
}
],
"editor/title": [{
"command": "review.openFile",
"group": "navigation",
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import { PullRequestModel } from './common/models/pullRequestModel';
import { FileChangeTreeItem } from './common/treeItems';
import { ReviewManager } from './review/reviewManager';
export function registerCommands(context: vscode.ExtensionContext) {
// initialize resources
context.subscriptions.push(vscode.commands.registerCommand('pr.openInGitHub', (e: PullRequestModel | FileChangeTreeItem) => {
if (e instanceof PullRequestModel) {
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(e.html_url));
} else {
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(e.blobUrl));
}
}));
context.subscriptions.push(vscode.commands.registerCommand('pr.pick', async (pr: PullRequestModel) => {
vscode.window.withProgress({
location: vscode.ProgressLocation.SourceControl,
title: `Switching to Pull Request #${pr.prNumber}`,
}, async (progress, token) => {
await ReviewManager.instance.switch(pr);
});
}));
}
......@@ -275,9 +275,11 @@ export async function parseDiff(reviews: any[], repository: Repository, parentCo
try {
let originalContent = await getFileContent(repository.path, parentCommit, fileName);
let richFileChange = await parseModifiedHunkComplete(originalContent, review.patch, fileName, fileName);
richFileChange.blobUrl = review.blob_url;
richFileChanges.push(richFileChange);
} catch (e) {
let richFileChange = await parseModifiedHunkFast(review.patch, fileName, fileName);
richFileChange.blobUrl = review.blob_url;
richFileChanges.push(richFileChange);
}
} else if (review.status === 'removed') {
......@@ -296,6 +298,7 @@ export async function parseDiff(reviews: any[], repository: Repository, parentCo
let originalFilePath = await writeTmpFile(contentArray.join('\n'), path.extname(fileName));
let filePath = await writeTmpFile('', path.extname(fileName));
let richFileChange = new RichFileChange(filePath, originalFilePath, GitChangeType.DELETE, fileName, review.patch);
richFileChange.blobUrl = review.blob_url;
richFileChanges.push(richFileChange);
} else {
// added
......@@ -314,6 +317,7 @@ export async function parseDiff(reviews: any[], repository: Repository, parentCo
let oriFilePath = await writeTmpFile('', path.extname(fileName));
let filePath = await writeTmpFile(contentArray.join('\n'), path.extname(fileName));
let richFileChange = new RichFileChange(filePath, oriFilePath, GitChangeType.ADD, fileName, review.patch);
richFileChange.blobUrl = review.blob_url;
richFileChanges.push(richFileChange);
}
}
......
......@@ -45,6 +45,7 @@ export class SlimFileChange {
}
export class RichFileChange {
public blobUrl: string;
constructor(
public readonly filePath: string,
public readonly originalFilePath: string,
......
......@@ -26,6 +26,7 @@ export enum PullRequestStateEnum {
export class PullRequestModel {
public prNumber: number;
public title: string;
public html_url: string;
public state: PullRequestStateEnum = PullRequestStateEnum.Open;
public commentCount: number;
public commitCount: number;
......@@ -47,6 +48,7 @@ export class PullRequestModel {
constructor(public readonly otcokit: any, public readonly remote: Remote, public prItem: any) {
this.prNumber = prItem.number;
this.title = prItem.title;
this.html_url = prItem.html_url;
this.author = {
login: prItem.user.login,
isUser: prItem.user.type === 'User',
......
......@@ -69,7 +69,7 @@ export class Repository {
private statusTimeout: any;
private disposables: vscode.Disposable[] = [];
constructor(path: string, workspaceState: vscode.Memento) {
constructor(path: string) {
this.path = path;
const fsWatcher = vscode.workspace.createFileSystemWatcher('**');
......
......@@ -6,6 +6,37 @@
import * as vscode from 'vscode';
import { GitChangeType } from './models/file';
import { PullRequestModel, PRType } from './models/pullRequestModel';
import { Resource } from './resources';
export enum PRGroupActionType {
Empty,
More
}
export class PRGroupActionTreeItem implements vscode.TreeItem {
public readonly label: string;
public collapsibleState: vscode.TreeItemCollapsibleState;
public iconPath?: { light: string | vscode.Uri; dark: string | vscode.Uri };
public type: PRGroupActionType;
constructor(type: PRGroupActionType) {
this.type = type;
this.collapsibleState = vscode.TreeItemCollapsibleState.None;
switch (type) {
case PRGroupActionType.Empty:
this.label = '0 pull request in this category';
break;
case PRGroupActionType.More:
this.label = 'Load more';
this.iconPath = {
light: Resource.icons.light.fold,
dark: Resource.icons.dark.fold
};
break;
default:
break;
}
}
}
export class PRGroupTreeItem implements vscode.TreeItem {
public readonly label: string;
......@@ -42,6 +73,7 @@ export class FileChangeTreeItem implements vscode.TreeItem {
public parentSha: string;
public command?: vscode.Command;
public comments?: any[];
public contextValue: string;
get letter(): string {
switch (this.status) {
......@@ -61,15 +93,17 @@ export class FileChangeTreeItem implements vscode.TreeItem {
}
constructor(
public readonly prItem: any,
public readonly pullRequest: PullRequestModel,
public readonly label: string,
public readonly status: GitChangeType,
public readonly fileName: string,
public blobUrl: string,
public readonly filePath: vscode.Uri,
public readonly parentFilePath: vscode.Uri,
public readonly workspaceRoot: string,
public readonly patch: string
) {
this.contextValue = 'filechange';
this.command = {
title: 'show diff',
command: 'vscode.diff',
......
......@@ -11,6 +11,7 @@ import { Configuration } from './configuration';
import { Resource } from './common/resources';
import { ReviewManager } from './review/reviewManager';
import { CredentialStore } from './credentials';
import { registerCommands } from './commands';
export async function activate(context: vscode.ExtensionContext) {
// initialize resources
......@@ -35,7 +36,7 @@ export async function activate(context: vscode.ExtensionContext) {
})
);
const repository = new Repository(rootPath, context.workspaceState);
const repository = new Repository(rootPath);
let repositoryInitialized = false;
repository.onDidRunGitStatus(async e => {
if (repositoryInitialized) {
......@@ -44,7 +45,8 @@ export async function activate(context: vscode.ExtensionContext) {
repositoryInitialized = true;
let credentialStore = new CredentialStore(configuration);
await repository.connectGitHub(credentialStore);
let reviewManager = new ReviewManager(context, repository, context.workspaceState);
await (new PRProvider(context, configuration, reviewManager)).activate(repository);
ReviewManager.initialize(context, repository);
PRProvider.initialize(context, configuration, repository);
registerCommands(context);
});
}
......@@ -11,48 +11,49 @@ import { Comment } from '../common/models/comment';
import * as _ from 'lodash';
import { Configuration } from '../configuration';
import { parseComments } from '../common/comment';
import { PRGroupTreeItem, FileChangeTreeItem } from '../common/treeItems';
import { PRGroupTreeItem, FileChangeTreeItem, PRGroupActionTreeItem, PRGroupActionType } from '../common/treeItems';
import { Resource } from '../common/resources';
import { ReviewManager } from '../review/reviewManager';
import { toPRUri } from '../common/uri';
import * as fs from 'fs';
import { PullRequestModel, PRType } from '../common/models/pullRequestModel';
import { PullRequestGitHelper } from '../common/pullRequestGitHelper';
export class PRProvider implements vscode.TreeDataProvider<PRGroupTreeItem | PullRequestModel | FileChangeTreeItem>, vscode.TextDocumentContentProvider, vscode.DecorationProvider {
private repository: Repository;
private _onDidChangeTreeData = new vscode.EventEmitter<PRGroupTreeItem | PullRequestModel | FileChangeTreeItem | undefined>();
export class PRProvider implements vscode.TreeDataProvider<PRGroupTreeItem | PullRequestModel | PRGroupActionTreeItem | FileChangeTreeItem>, vscode.TextDocumentContentProvider, vscode.DecorationProvider {
private static _instance: PRProvider;
private _onDidChangeTreeData = new vscode.EventEmitter<PRGroupTreeItem | PullRequestModel | PRGroupActionTreeItem | FileChangeTreeItem | undefined>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
get onDidChange(): vscode.Event<vscode.Uri> { return this._onDidChange.event; }
constructor(
private constructor(
private context: vscode.ExtensionContext,
private configuration: Configuration,
private reviewManager: ReviewManager,
private repository: Repository
) {
vscode.workspace.registerTextDocumentContentProvider('pr', this);
vscode.window.registerDecorationProvider(this);
}
async activate(repository: Repository) {
this.repository = repository;
this.context.subscriptions.push(vscode.window.registerTreeDataProvider<PRGroupTreeItem | PullRequestModel | FileChangeTreeItem>('pr', this));
this.context.subscriptions.push(vscode.commands.registerCommand('pr.pick', async (pr: PullRequestModel) => {
vscode.window.withProgress({
location: vscode.ProgressLocation.SourceControl,
title: `Switching to Pull Request #${pr.prNumber}`,
}, async (progress, token) => {
await this.reviewManager.switch(pr);
});
context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('pr', this));
context.subscriptions.push(vscode.window.registerDecorationProvider(this));
context.subscriptions.push(vscode.commands.registerCommand('pr.refreshList', _ => {
this._onDidChangeTreeData.fire();
}));
this.context.subscriptions.push(vscode.window.registerTreeDataProvider<PRGroupTreeItem | PullRequestModel | PRGroupActionTreeItem | FileChangeTreeItem>('pr', this));
this.context.subscriptions.push(this.configuration.onDidChange(e => {
this._onDidChangeTreeData.fire();
}));
}
getTreeItem(element: PRGroupTreeItem | PullRequestModel | FileChangeTreeItem): vscode.TreeItem {
if (element instanceof PRGroupTreeItem) {
static initialize(
context: vscode.ExtensionContext,
configuration: Configuration,
repository: Repository) {
PRProvider._instance = new PRProvider(context, configuration, repository);
}
static get instance() {
return PRProvider._instance;
}
getTreeItem(element: PRGroupTreeItem | PullRequestModel | PRGroupActionTreeItem | FileChangeTreeItem): vscode.TreeItem {
if (element instanceof PRGroupTreeItem || element instanceof PRGroupActionTreeItem) {
return element;
}
......@@ -74,7 +75,7 @@ export class PRProvider implements vscode.TreeDataProvider<PRGroupTreeItem | Pul
}
}
async getChildren(element?: PRGroupTreeItem | PullRequestModel | FileChangeTreeItem): Promise<(PRGroupTreeItem | PullRequestModel | FileChangeTreeItem)[]> {
async getChildren(element?: PRGroupTreeItem | PullRequestModel | PRGroupActionTreeItem | FileChangeTreeItem): Promise<(PRGroupTreeItem | PullRequestModel | PRGroupActionTreeItem | FileChangeTreeItem)[]> {
if (!element) {
return Promise.resolve([
new PRGroupTreeItem(PRType.RequestReview),
......@@ -85,11 +86,16 @@ export class PRProvider implements vscode.TreeDataProvider<PRGroupTreeItem | Pul
}
if (!this.repository.remotes || !this.repository.remotes.length) {
return Promise.resolve([]);
return Promise.resolve([new PRGroupActionTreeItem(PRGroupActionType.Empty)]);
}
if (element instanceof PRGroupTreeItem) {
return this.getPRs(element);
let prItems = await this.getPRs(element);
if (prItems && prItems.length) {
return prItems;
} else {
return [new PRGroupActionTreeItem(PRGroupActionType.Empty)];
}
}
if (element instanceof PullRequestModel) {
......@@ -101,10 +107,11 @@ export class PRProvider implements vscode.TreeDataProvider<PRGroupTreeItem | Pul
let fileChanges = richContentChanges.map(change => {
let fileInRepo = path.resolve(this.repository.path, change.fileName);
let changedItem = new FileChangeTreeItem(
element.prItem,
element,
change.fileName,
change.status,
change.fileName,
change.blobUrl,
toPRUri(vscode.Uri.file(change.filePath), fileInRepo, change.fileName, true),
toPRUri(vscode.Uri.file(change.originalFilePath), fileInRepo, change.fileName, false),
this.repository.path,
......
......@@ -29,6 +29,7 @@ export interface ReviewState {
}
export class ReviewManager implements vscode.DecorationProvider {
private static _instance: ReviewManager;
private _documentCommentProvider: vscode.Disposable;
private _workspaceCommentProvider: vscode.Disposable;
private _command: vscode.Disposable;
......@@ -62,7 +63,7 @@ export class ReviewManager implements vscode.DecorationProvider {
return this._statusBarItem;
}
constructor(
private constructor(
private _context: vscode.ExtensionContext,
private _repository: Repository,
private _workspaceState: vscode.Memento
......@@ -86,6 +87,17 @@ export class ReviewManager implements vscode.DecorationProvider {
this.pollForStatusChange();
}
static initialize(
_context: vscode.ExtensionContext,
_repository: Repository
) {
ReviewManager._instance = new ReviewManager(_context, _repository, _context.workspaceState);
}
static get instance() {
return ReviewManager._instance;
}
private pollForStatusChange() {
setTimeout(async () => {
await this.updateComments();
......@@ -93,7 +105,7 @@ export class ReviewManager implements vscode.DecorationProvider {
}, 1000 * 10);
}
async validateState() {
private async validateState() {
let localInfo = await PullRequestGitHelper.getPullRequestForCurrentBranch(this._repository);
if (!localInfo) {
......@@ -277,10 +289,11 @@ export class ReviewManager implements vscode.DecorationProvider {
const richContentChanges = await parseDiff(data, this._repository, baseSha);
this._localFileChanges = richContentChanges.map(change => {
let changedItem = new FileChangeTreeItem(
pr.prItem,
pr,
change.fileName,
change.status,
change.fileName,
change.blobUrl,
toGitUri(vscode.Uri.parse(change.fileName), null, change.status === GitChangeType.DELETE ? '' : pr.prItem.head.sha, {}),
toGitUri(vscode.Uri.parse(change.fileName), null, change.status === GitChangeType.ADD ? '' : pr.prItem.base.sha, {}),
this._repository.path,
......@@ -367,7 +380,7 @@ export class ReviewManager implements vscode.DecorationProvider {
return {};
}
registerCommentProvider() {
private registerCommentProvider() {
this._documentCommentProvider = vscode.workspace.registerDocumentCommentProvider({
onDidChangeCommentThreads: this._onDidChangeCommentThreads.event,
provideDocumentComments: async (document: vscode.TextDocument, token: vscode.CancellationToken) => {
......@@ -443,7 +456,7 @@ export class ReviewManager implements vscode.DecorationProvider {
});
}
async switch(pr: PullRequestModel): Promise<void> {
public async switch(pr: PullRequestModel): Promise<void> {
let isDirty = await this._repository.isDirty();
if (isDirty) {
vscode.window.showErrorMessage('Your local changes would be overwritten by checkout, please commit your changes or stash them before you switch branches');
......@@ -481,7 +494,7 @@ export class ReviewManager implements vscode.DecorationProvider {
await this.validateState();
}
clear(quitReviewMode: boolean) {
private clear(quitReviewMode: boolean) {
this._prNumber = null;
if (this._command) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册