complete tree provider refactor

This commit is contained in:
kingecg 2025-03-08 19:25:58 +08:00
parent cbab380bb0
commit c315c18b4e
7 changed files with 146 additions and 61 deletions

View File

@ -31,7 +31,8 @@
"title": "Show as list"
},
{
"command": "gitcommitfilter.compare"
"command": "gitcommitfilter.compare",
"title": "比较文件变化"
}
],
"menus": {

View File

@ -35,6 +35,10 @@ export class GitService {
this._repoPath = workspaceFolders[0].uri.fsPath;
}
get repoPath(): string {
return this._repoPath;
}
private transformFilter(filter: GitCommitFilter): string[] {
const gitArgs: string[] = [];
if(filter.committer) {
@ -54,7 +58,8 @@ export class GitService {
return gitArgs;
}
getDateStr(d:Date) :string {
getDateStr(d:Date|undefined) :string {
if(!d) {return '';}
return d.toLocaleDateString('zh-CN');
}
private defaultFilter(): string[] {
@ -75,17 +80,11 @@ export class GitService {
return result.toString().trim();
}
public async getCommits(filter: string = ""): Promise<any[]> {
const filterParts = filter.split(" ");
let nFilter: string[] = [];
filterParts.forEach((part) => {
const t = part.split(":");
const key = t[0];
const v = t[1];
if (key in GitFilter) {
nFilter.push(`--${GitFilter[key]}=${v}`);
}
});
public async getCommits(filter: GitCommitFilter = {}): Promise<any[]> {
let nFilter :string[] = [];
if(Object.keys(filter).length !== 0){
nFilter = this.transformFilter(filter);
}
if(nFilter.length === 0){
nFilter = this.defaultFilter();
}
@ -97,6 +96,9 @@ export class GitService {
"--date=format:%Y-%m-%d",
"--name-only",
];
if(filter.path) {
gitArgs.push(filter.path);
}
const that = this;
return new Promise((resolve, reject) => {
@ -146,7 +148,7 @@ export class GitService {
if(lines.length > 0){
const commit = makeCommit(lines);
if(commit){
that.logger.info('push commit', commit.hash)
that.logger.info('push commit', commit.hash);
commits.push(commit);
}
@ -154,11 +156,11 @@ export class GitService {
lines = [];
lines.push(nline);
} else if (nline.startsWith("---") ) {
continue
continue;
}else if( nline.trim() === ""){
const commit = makeCommit(lines);
if(commit){
that.logger.info('push commit 2', commit.hash)
that.logger.info('push commit 2', commit.hash);
commits.push(commit);
}
lines = [];
@ -174,7 +176,7 @@ export class GitService {
const commit = makeCommit(lines);
if(commit){
that.logger.info('push commit 3', commit.hash)
that.logger.info('push commit 3', commit.hash);
commits.push(commit);
}
}

71
src/commands.ts Normal file
View File

@ -0,0 +1,71 @@
import path from "path";
import { EventBus } from "./event-bus";
import { GitService } from "./GitService";
import * as vscode from "vscode";
export class Commander {
constructor(private _gitService: GitService, private eventBus: EventBus) {
console.log("Commander");
}
public async compare(commit: string, file: string) {
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (!workspaceFolder) {
vscode.window.showErrorMessage('Please open a workspace folder first');
return;
}
try {
const rawPrevCommit = await this._gitService.getPreviousCommit(commit);
const previousCommitId = rawPrevCommit?.trim(); // 去除可能的换行符
if (!previousCommitId) {
vscode.window.showErrorMessage('Cannot find previous commit');
return;
}
// 编码处理
const encodedCommit = encodeURIComponent(commit.trim());
const encodedPrevCommit = encodeURIComponent(previousCommitId);
const sanitizedWorkPath = workspaceFolder.replace(/\\/g, '/')
.replace(/'/g, "'")
.replace(/ /g, " ");
const sanitizedFilePath = file.replace(/\\/g, '/')
.replace(/'/g, "'")
.replace(/ /g, " ");
// 构造 URI
const currentUri = this.makeUri(sanitizedFilePath, encodedCommit);
const previousUri = this.makeUri(sanitizedFilePath, encodedPrevCommit);
await vscode.commands.executeCommand(
"vscode.diff",
previousUri,
currentUri,
`Changes: ${path.basename(file)} (${previousCommitId.slice(0, 7)}${commit.slice(0, 7)})`
);
} catch (error) {
vscode.window.showErrorMessage(`Failed to open diff: ${error instanceof Error ? error.message : error}`);
}
}
private makeUri(filePath: string, commit: string) {
const repoPath = this._gitService.repoPath;
const repoUri = vscode.Uri.file(repoPath);
const fUri = vscode.Uri.joinPath(repoUri, filePath);
const params = {
path: fUri.fsPath,
ref: commit
}
return fUri.with({ scheme: 'git', path: fUri.path, query: JSON.stringify(params) });
}
public changeToList() {
// set state to list
vscode.commands.executeCommand('setContext', 'gitcommitfilter:viewState', 'list');
this.eventBus.emit('changeView', 'list');
}
public changeToTree() {
// set state to tree
vscode.commands.executeCommand('setContext', 'gitcommitfilter:viewState', 'tree');
this.eventBus.emit('changeView', 'tree');
}
}

View File

@ -8,6 +8,7 @@ export class CommitListProvider implements vscode.TreeDataProvider<CommitItem> {
private state:string = "list";
private filter: any = {};
private _commits: GitCommit[] = [];
private _items: CommitItem[] = [];
private _onDidChangeTreeData: vscode.EventEmitter<CommitItem | undefined | void> = new vscode.EventEmitter<CommitItem | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<CommitItem | undefined | void> = this._onDidChangeTreeData.event;
@ -18,17 +19,30 @@ export class CommitListProvider implements vscode.TreeDataProvider<CommitItem> {
this.filter = filter;
this.refresh();
});
this.event.on("refresh",()=>{
this.event.on("changeView",(state)=>{
this.state = state;
this._makeCommitItems();
this._onDidChangeTreeData.fire();
});
}
_makeCommitItems() {
this._items = this._commits.map(commit => {
const item = new CommitItem(
commit.msg+' -- '+commit.author+'@'+commit.hash+' '+ commit.date,
vscode.TreeItemCollapsibleState.None,
commit.hash||'',
this.state,
'',
commit.files||[]
);
return item;
});
}
refresh(): void {
async refresh() {
this._commits = await this._gitService.getCommits(this.filter);
this._makeCommitItems();
this._onDidChangeTreeData.fire();
}
@ -43,12 +57,9 @@ export class CommitListProvider implements vscode.TreeDataProvider<CommitItem> {
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));
return this._items;
// 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 [];
return element.children||[];
}
}

View File

@ -43,6 +43,7 @@ import * as path from "path";
export class CommitItem extends vscode.TreeItem {
public iconPath?: string | vscode.IconPath | undefined = 'file';
public children?: CommitItem[];
public command?: vscode.Command | undefined = undefined
constructor(
public readonly label:string,
public state : vscode.TreeItemCollapsibleState,
@ -54,6 +55,11 @@ export class CommitItem extends vscode.TreeItem {
super(label, state);
if(this.isFileItem()){
this.tooltip = this.fpath;
this.command = {
command: 'gitcommitfilter.compare',
title: '比较文件变化',
arguments: [this.commitHash, this.fpath]
};
}
if(this.isCommitItem()) {
this.iconPath = 'git-commit';

View File

@ -2,6 +2,9 @@ import * as vscode from 'vscode';
import { GitService } from './GitService';
import { GViewProvider } from './view.provider';
import { EventBus } from './event-bus';
import { CommitListProvider } from './commit-list.provider';
import { Commander } from './commands';
function getGitApi(){
@ -22,9 +25,25 @@ export async function activate(context: vscode.ExtensionContext) {
return;
}
const logger = vscode.window.createOutputChannel('GitCommitFilter',{log: true});
const eventBus = new EventBus();
const gitService = new GitService(gitPath,logger);
const commitlist = new CommitListProvider(gitService,eventBus)
const commander = new Commander(gitService,eventBus);
context.subscriptions.push(vscode.commands.registerCommand('gitcommitfilter.showaslist', () => {
commander.changeToList();
}));
context.subscriptions.push(vscode.commands.registerCommand('gitcommitfilter.showastree', () => {
commander.changeToTree();
}));
context.subscriptions.push(vscode.commands.registerCommand('gitcommitfilter.compare', (commit, file) => {
commander.compare(commit, file);
}));
//get view gitcommitfilter.commit-list
const viewProvider = new GViewProvider(context, gitService,logger);
const d= vscode.window.registerTreeDataProvider('gitcommitfilter.commit-list', commitlist);
context.subscriptions.push(d);
commitlist.refresh();
const activityBarIcon = vscode.window.registerWebviewViewProvider(GViewProvider.viewType, viewProvider);
@ -33,28 +52,3 @@ export async function activate(context: vscode.ExtensionContext) {
export function deactivate() {}
// 实现TreeDataProvider接口
class CommitTreeDataProvider implements vscode.TreeDataProvider<vscode.TreeItem> {
private _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | undefined | void> = new vscode.EventEmitter<vscode.TreeItem | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | undefined | void> = this._onDidChangeTreeData.event;
private gitService: GitService;
constructor(gitService: GitService) {
this.gitService = gitService;
}
refresh(): void {
this._onDidChangeTreeData.fire();
}
getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
return element;
}
getChildren(element?: vscode.TreeItem): vscode.ProviderResult<vscode.TreeItem[]> {
if (element) {
return [];
}
}
}

View File

@ -19,7 +19,7 @@ export class GViewProvider implements vscode.WebviewViewProvider {
) {
webviewView.webview.options = { enableScripts: true };
this._view = webviewView;
webviewView.webview.html = this.generateHtml(webviewView);
webviewView.webview.html = '';//this.generateHtml(webviewView);
// 添加事件监听器
webviewView.webview.onDidReceiveMessage(
@ -261,13 +261,13 @@ private async openDiff(commitId: string, filePath: string) {
}
private async filterCommits(filterText: string) {
if (this._view) {
const commits = await this._gitService.getCommits(filterText);
this._logger.info(`filteredCommits:, ${JSON.stringify(commits)}"`);
this._view.webview.postMessage({
command: "updateCommits",
commits
});
}
// if (this._view) {
// const commits = await this._gitService.getCommits(filterText);
// this._logger.info(`filteredCommits:, ${JSON.stringify(commits)}"`);
// this._view.webview.postMessage({
// command: "updateCommits",
// commits
// });
// }
}
}