Adds an experimental custom view (wip)

This commit is contained in:
Eric Amodio
2017-06-12 12:25:09 -04:00
parent 3081632815
commit c812a56eac
20 changed files with 1939 additions and 1401 deletions

View File

@@ -0,0 +1,41 @@
'use strict';
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
import { ExplorerNode, ResourceType } from './explorerNode';
import { GitCommit, GitService, GitUri, IGitStatusFile } from '../gitService';
export class CommitFileNode extends ExplorerNode {
readonly resourceType: ResourceType = 'commit-file';
command: Command;
constructor(public status: IGitStatusFile, public commit: GitCommit, uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
this.command = {
title: 'Compare File with Previous',
command: Commands.DiffWithPrevious,
arguments: [
GitUri.fromFileStatus(this.status, this.commit.repoPath),
{
commit: commit,
showOptions: {
preserveFocus: true,
preview: true
}
} as DiffWithPreviousCommandArgs
]
};
}
getChildren(): Promise<ExplorerNode[]> {
return Promise.resolve([]);
}
getTreeItem(): TreeItem {
const item = new TreeItem(`${GitUri.getFormattedPath(this.status.fileName)}`, TreeItemCollapsibleState.None);
item.contextValue = this.resourceType;
item.command = this.command;
return item;
}
}

54
src/views/commitNode.ts Normal file
View File

@@ -0,0 +1,54 @@
'use strict';
import { Iterables } from '../system';
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitFileNode } from './commitFileNode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { CommitFormatter, GitCommit, GitService, GitUri } from '../gitService';
export class CommitNode extends ExplorerNode {
readonly resourceType: ResourceType = 'commit';
command: Command;
constructor(public commit: GitCommit, uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
// this.command = {
// title: 'Compare File with Previous',
// command: Commands.DiffWithPrevious,
// arguments: [
// Uri.file(commit.uri.fsPath),
// {
// commit: commit,
// showOptions: {
// preserveFocus: true,
// preview: true
// }
// } as DiffWithPreviousCommandArgs
// ]
// };
}
async getChildren(): Promise<ExplorerNode[]> {
const log = await this.git.getLogForRepo(this.commit.repoPath, this.commit.sha, 1);
if (log === undefined) return [];
const commit = Iterables.first(log.commits.values());
if (commit === undefined) return [];
return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.uri, this.context, this.git))];
}
getTreeItem(): TreeItem {
const label = CommitFormatter.fromTemplate(this.git.config.explorer.commitFormat, this.commit, this.git.config.defaultDateFormat);
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
item.iconPath = {
dark: this.context.asAbsolutePath('images/dark/icon-commit.svg'),
light: this.context.asAbsolutePath('images/light/icon-commit.svg')
};
item.command = this.command;
return item;
}
}

15
src/views/explorerNode.ts Normal file
View File

@@ -0,0 +1,15 @@
'use strict';
import { ExtensionContext, TreeItem } from 'vscode';
import { GitService, GitUri } from '../gitService';
export declare type ResourceType = 'status' | 'branches' | 'repository' | 'branch-history' | 'file-history' | 'stash-history' | 'commit' | 'stash-commit' | 'commit-file';
export abstract class ExplorerNode {
abstract readonly resourceType: ResourceType;
constructor(public uri: GitUri, protected context: ExtensionContext, protected git: GitService) { }
abstract getChildren(): ExplorerNode[] | Promise<ExplorerNode[]>;
abstract getTreeItem(): TreeItem | Promise<TreeItem>;
}

View File

@@ -0,0 +1,29 @@
'use strict';
import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitNode } from './commitNode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService';
export class FileHistoryNode extends ExplorerNode {
static readonly rootType: ResourceType = 'file-history';
readonly resourceType: ResourceType = 'file-history';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
}
async getChildren(): Promise<CommitNode[]> {
const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha);
if (log === undefined) return [];
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.uri, this.context, this.git))];
}
getTreeItem(): TreeItem {
const item = new TreeItem(`History of ${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
item.contextValue = this.resourceType;
return item;
}
}

62
src/views/gitExplorer.ts Normal file
View File

@@ -0,0 +1,62 @@
'use strict';
import { Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
import { UriComparer } from '../comparers';
import { ExplorerNode, FileHistoryNode, RepositoryNode, ResourceType, StashNode } from './gitExplorerNodes';
import { GitService, GitUri } from '../gitService';
export * from './gitExplorerNodes';
export class GitExplorer implements TreeDataProvider<ExplorerNode> {
private _onDidChangeTreeData = new EventEmitter<ExplorerNode>();
public get onDidChangeTreeData(): Event<ExplorerNode> {
return this._onDidChangeTreeData.event;
}
private _roots: ExplorerNode[] = [];
constructor(private context: ExtensionContext, private git: GitService) {
const editor = window.activeTextEditor;
const uri = (editor !== undefined && editor.document !== undefined)
? new GitUri(editor.document.uri, { repoPath: git.repoPath, fileName: editor.document.uri.fsPath })
: new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath });
this._roots.push(new RepositoryNode(uri, context, git));
}
async getTreeItem(node: ExplorerNode): Promise<TreeItem> {
return node.getTreeItem();
}
async getChildren(node?: ExplorerNode): Promise<ExplorerNode[]> {
if (this._roots.length === 0) return [];
if (node === undefined) return this._roots;
return node.getChildren();
}
addHistory(uri: GitUri) {
this._add(uri, FileHistoryNode);
}
addStash(uri: GitUri) {
this._add(uri, StashNode);
}
private _add<T extends ExplorerNode>(uri: GitUri, type: { new (uri: GitUri, context: ExtensionContext, git: GitService): T, rootType: ResourceType }) {
if (!this._roots.some(_ => _.resourceType === type.rootType && UriComparer.equals(uri, _.uri))) {
this._roots.push(new type(uri, this.context, this.git));
}
this._onDidChangeTreeData.fire();
}
clear() {
this._roots = [];
this._onDidChangeTreeData.fire();
}
refresh() {
this._onDidChangeTreeData.fire();
}
}

View File

@@ -0,0 +1,9 @@
'use strict';
export * from './explorerNode';
export * from './commitFileNode';
export * from './commitNode';
export * from './fileHistoryNode';
export * from './repositoryNode';
export * from './stashCommitNode';
export * from './stashNode';

105
src/views/repositoryNode.ts Normal file
View File

@@ -0,0 +1,105 @@
'use strict';
import { Iterables, Strings } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitNode } from './commitNode';
import { GlyphChars } from '../constants';
import { ExplorerNode, ResourceType } from './explorerNode';
import { GitBranch, GitService, GitUri } from '../gitService';
import { StashNode } from './stashNode';
export class RepositoryNode extends ExplorerNode {
static readonly rootType: ResourceType = 'repository';
readonly resourceType: ResourceType = 'repository';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
}
async getChildren(): Promise<ExplorerNode[]> {
return [
new StatusNode(this.uri, this.context, this.git),
new StashNode(this.uri, this.context, this.git),
new BranchesNode(this.uri, this.context, this.git)
];
}
getTreeItem(): TreeItem {
const item = new TreeItem(`Repository ${GlyphChars.Dash} ${this.uri.repoPath}`, TreeItemCollapsibleState.Expanded);
item.contextValue = this.resourceType;
return item;
}
}
export class BranchesNode extends ExplorerNode {
readonly resourceType: ResourceType = 'branches';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
}
async getChildren(): Promise<BranchHistoryNode[]> {
const branches = await this.git.getBranches(this.uri.repoPath!);
if (branches === undefined) return [];
return [...Iterables.filterMap(branches.sort(_ => _.current ? 0 : 1), b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.context, this.git))];
}
getTreeItem(): TreeItem {
const item = new TreeItem(`Branches`, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
return item;
}
}
export class BranchHistoryNode extends ExplorerNode {
readonly resourceType: ResourceType = 'branch-history';
constructor(public branch: GitBranch, uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
}
async getChildren(): Promise<CommitNode[]> {
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name);
if (log === undefined) return [];
return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.uri, this.context, this.git))];
}
getTreeItem(): TreeItem {
const item = new TreeItem(`${this.branch.name}${this.branch.current ? ` ${GlyphChars.Dash} current` : ''}`, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
return item;
}
}
export class StatusNode extends ExplorerNode {
readonly resourceType: ResourceType = 'status';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
}
async getChildren(): Promise<ExplorerNode[]> {
return [];
// const status = await this.git.getStatusForRepo(this.uri.repoPath!);
// if (status === undefined) return [];
// return [...Iterables.map(status.files, b => new CommitFile(b, this.uri, this.context, this.git))];
}
async getTreeItem(): Promise<TreeItem> {
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
let suffix = '';
if (status !== undefined) {
suffix = ` ${GlyphChars.Dash} ${GlyphChars.ArrowUp} ${status.state.ahead} ${GlyphChars.ArrowDown} ${status.state.behind} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${status.branch} ${GlyphChars.ArrowLeftRight} ${status.upstream}`;
}
const item = new TreeItem(`Status${suffix}`, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
return item;
}
}

View File

@@ -0,0 +1,40 @@
'use strict';
import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitFileNode } from './commitFileNode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { CommitFormatter, GitService, GitStashCommit, GitUri } from '../gitService';
export class StashCommitNode extends ExplorerNode {
readonly resourceType: ResourceType = 'stash-commit';
command: Command;
constructor(public commit: GitStashCommit, uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
// this.command = {
// title: 'Show Stash Details',
// command: Commands.ShowQuickCommitDetails,
// arguments: [
// new GitUri(commit.uri, commit),
// {
// commit: commit,
// sha: commit.sha
// } as ShowQuickCommitDetailsCommandArgs
// ]
// };
}
getChildren(): Promise<CommitFileNode[]> {
return Promise.resolve((this.commit as GitStashCommit).fileStatuses.map(_ => new CommitFileNode(_, this.commit, this.uri, this.context, this.git)));
}
getTreeItem(): TreeItem {
const label = CommitFormatter.fromTemplate(this.git.config.explorer.stashFormat, this.commit, this.git.config.defaultDateFormat);
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
item.command = this.command;
return item;
}
}

29
src/views/stashNode.ts Normal file
View File

@@ -0,0 +1,29 @@
'use strict';
import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { ExplorerNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService';
import { StashCommitNode } from './stashCommitNode';
export class StashNode extends ExplorerNode {
static readonly rootType: ResourceType = 'stash-history';
readonly resourceType: ResourceType = 'stash-history';
constructor(uri: GitUri, context: ExtensionContext, git: GitService) {
super(uri, context, git);
}
async getChildren(): Promise<StashCommitNode[]> {
const stash = await this.git.getStashList(this.uri.repoPath!);
if (stash === undefined) return [];
return [...Iterables.map(stash.commits.values(), c => new StashCommitNode(c, this.uri, this.context, this.git))];
}
getTreeItem(): TreeItem {
const item = new TreeItem(`Stashed Changes`, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
return item;
}
}