add tree data provider

This commit is contained in:
kingecg 2025-03-06 23:20:43 +08:00
parent 8fcd2366f2
commit cbab380bb0
5 changed files with 258 additions and 0 deletions

View File

@ -21,6 +21,36 @@
"activationEvents": [],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "gitcommitfilter.showastree",
"title": "Show as tree"
},
{
"command": "gitcommitfilter.showaslist",
"title": "Show as list"
},
{
"command": "gitcommitfilter.compare"
}
],
"menus": {
"view/title":[
{
"command": "gitcommitfilter.showastree",
"when": "view == gitcommitfilter.commit-list && gitcommitfilter.viewState=='list'",
"group": "navigation",
"icon":"${list-tree}"
},
{
"command": "gitcommitfilter.showaslist",
"when": "view == gitcommitfilter.commit-list && gitcommitfilter.viewState=='tree'",
"group": "navigation",
"icon":"${list-flat}"
}
]
},
"viewsContainers": {
"activitybar": [
{
@ -37,6 +67,11 @@
"id": "gitcommitfilter.view",
"name": "MyCustomView",
"when": "true"
},
{
"id": "gitcommitfilter.commit-list",
"name": "Commitlist",
"type": "tree"
}
]
}

View File

@ -15,6 +15,15 @@ export interface GitCommit {
files?: string[];
}
export interface GitCommitFilter{
path?:string;
contains?:string;
committer?:string;
dateRange?:[Date, Date]
}
export class GitService {
private _repoPath: string;
@ -25,9 +34,46 @@ export class GitService {
}
this._repoPath = workspaceFolders[0].uri.fsPath;
}
private transformFilter(filter: GitCommitFilter): string[] {
const gitArgs: string[] = [];
if(filter.committer) {
gitArgs.push(`--author=${filter.committer}`);
}
if(filter.dateRange) {
const [from, to] = filter.dateRange;
gitArgs.push(`--since=${this.getDateStr(from)}`);
// get next day of to
const toNextDay = new Date(to);
toNextDay.setDate(toNextDay.getDate() + 1);
gitArgs.push(`--until=${this.getDateStr(toNextDay)}`);
}
if(filter.contains) {
gitArgs.push(`--grep=${filter.contains}`);
}
return gitArgs;
}
getDateStr(d:Date) :string {
return d.toLocaleDateString('zh-CN');
}
private defaultFilter(): string[] {
return ['--author=$(git config user.name)', '--since=\"7 days ago\"'];
}
getCommitters() : string[] {
const command = `git log --format='%aN' | sort -u`;
const result = child_process.execSync(`git log --format='%aN' | sort -u`, {
cwd: this._repoPath,
});
return result.toString().split("\n");
}
getCurrentUser(): string {
const result = child_process.execSync(`git config user.name`, {
cwd: this._repoPath,
});
return result.toString().trim();
}
public async getCommits(filter: string = ""): Promise<any[]> {
const filterParts = filter.split(" ");

View File

@ -0,0 +1,54 @@
import * as vscode from "vscode";
import { GitService, GitCommit } from "./GitService";
import path from "path";
import { CommitItem } from "./commit-tree.item";
import { EventBus } from "./event-bus";
export class CommitListProvider implements vscode.TreeDataProvider<CommitItem> {
private state:string = "list";
private filter: any = {};
private _commits: GitCommit[] = [];
private _onDidChangeTreeData: vscode.EventEmitter<CommitItem | undefined | void> = new vscode.EventEmitter<CommitItem | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<CommitItem | undefined | void> = this._onDidChangeTreeData.event;
private _showFullPath: boolean = false;
constructor(private readonly _gitService: GitService,private event: EventBus) {
this.event.on("filter",(filter)=>{
this.filter = filter;
this.refresh();
});
this.event.on("refresh",()=>{
this._makeCommitItems();
this._onDidChangeTreeData.fire();
});
}
_makeCommitItems() {
}
refresh(): void {
this._onDidChangeTreeData.fire();
}
toggleFullPath(): void {
this._showFullPath = !this._showFullPath;
this.refresh();
}
getTreeItem(element: CommitItem): vscode.TreeItem {
return element;
}
async getChildren(element?: CommitItem): Promise<CommitItem[]> {
if (!element) {
const commits = await this._gitService.getCommits("");
return commits.map(commit => new CommitItem(`${commit.msg} -- ${commit.author}@${commit.hash} ${commit.date}`, vscode.TreeItemCollapsibleState.Expanded, commit.files));
}
if (element.files) {
return element.files.map(file => new CommitItem(this._showFullPath ? file : path.basename(file), vscode.TreeItemCollapsibleState.None, undefined, file));
}
return [];
}
}

115
src/commit-tree.item.ts Normal file
View File

@ -0,0 +1,115 @@
import * as vscode from "vscode";
import * as path from "path";
// interface PathItem {
// fpath?: string;
// label: string;
// children?: PathTree;
// }
// interface PathTree {
// [key: string]: PathItem;
// }
// function makePathTree(paths: string[]): PathTree{
// const pathTree: PathTree = {};
// for (const file of paths) {
// const pathParts = file.split("/");
// if(pathParts.length === 1){
// pathTree[pathParts[0]] = {fpath: file, label: pathParts[0]};
// continue;
// }
// let currentNode = pathTree;
// for (let i = 0; i < pathParts.length; i++) {
// const pathItem = pathParts[i];
// if(!currentNode.chilren){
// currentNode.chilren = {};
// }
// if (!currentNode.chilren[pathItem]) {
// currentNode[pathItem] = {
// label: pathItem,
// children: i !== pathParts.length - 1 ? {} : undefined,
// fpath: i === pathParts.length - 1 ? file : undefined,
// };
// } else
// if(i !== pathParts.length - 1) {
// currentNode = currentNode[pathItem].children;
// }
// }
// }
// return {};
// }
export class CommitItem extends vscode.TreeItem {
public iconPath?: string | vscode.IconPath | undefined = 'file';
public children?: CommitItem[];
constructor(
public readonly label:string,
public state : vscode.TreeItemCollapsibleState,
public readonly commitHash: string,
public readonly style:string,
public readonly fpath?: string,
public readonly files?: string[]
){
super(label, state);
if(this.isFileItem()){
this.tooltip = this.fpath;
}
if(this.isCommitItem()) {
this.iconPath = 'git-commit';
}
}
makeChildren() {
if(this.isCommitItem()) {
if(this.style === "list"){
this.children = this.files?.map(file => {
return new CommitItem(path.basename(file), vscode.TreeItemCollapsibleState.None, this.commitHash, "list", file);
});
} else {
if(!this.files) {
return
}
for(const file of this.files) {
const fileParts = file.split("/");
if(fileParts.length === 1){
this.addChildren(new CommitItem(fileParts[0], vscode.TreeItemCollapsibleState.None, this.commitHash, "tree", file));
continue;
}
let currentNode: CommitItem = this;
for (let i = 0; i < fileParts.length; i++) {
const pathItem = fileParts[i];
const child = currentNode.getChildItem(pathItem);
if(!child) {
const newChild = new CommitItem(pathItem, vscode.TreeItemCollapsibleState.Collapsed, this.commitHash, "tree");
currentNode.addChildren(newChild);
currentNode = newChild;
} else {
currentNode = child;
}
}
}
}
}
}
isCommitItem() {
return this.files && !this.fpath;
}
isFileItem() {
return this.fpath && this.children?.length === 0;
}
isFolderPath() {
return this.fpath && this.children && this.children?.length > 0;
}
addChildren(item: CommitItem) {
this.iconPath = 'folder';
const index = this.children?.findIndex(child => child.label === item.label);
if(index === -1) {
this.children?.push(item);
}
}
getChildItem(label: string) {
return this.children?.find(child => child.label === label);
}
}

8
src/event-bus.ts Normal file
View File

@ -0,0 +1,8 @@
import { EventEmitter } from "events";
export class EventBus extends EventEmitter {
constructor() {
super();
}
}