mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-02-13 03:28:35 -05:00
Adds an experimental custom view (wip)
This commit is contained in:
41
src/views/commitFileNode.ts
Normal file
41
src/views/commitFileNode.ts
Normal 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
54
src/views/commitNode.ts
Normal 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
15
src/views/explorerNode.ts
Normal 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>;
|
||||
}
|
||||
29
src/views/fileHistoryNode.ts
Normal file
29
src/views/fileHistoryNode.ts
Normal 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
62
src/views/gitExplorer.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
9
src/views/gitExplorerNodes.ts
Normal file
9
src/views/gitExplorerNodes.ts
Normal 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
105
src/views/repositoryNode.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
40
src/views/stashCommitNode.ts
Normal file
40
src/views/stashCommitNode.ts
Normal 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
29
src/views/stashNode.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user