上篇文章说,采用html、css、js就能开发谷歌浏览器插件,打破前端职业瓶颈,拓宽开发思路。那么这次依然是使用前端常用的js、html来开发vscode插件。
vscode是前端开发中重要的IDE工具,自行安装好多功能插件,让日常开发写代码速度突飞猛进,再加上现在流行的AI工具,根据提示大大节省开发时间。这就是插件的魅力。
接下来开始研究研究vscode插件的开发,只使用前端日常使用的基础html、css、js就可以完成。是不是又打破了前端职业界限,给你的简历又添加新的亮点。
开发环境初始化
要开发VSCode插件,您可以按照以下步骤进行:
1:安装Node.js:确保计算机上安装Node环境。
2:安装Yeoman和VS Code Extension Generator:使用Node.js的包管理器npm全局安装Yeoman和VS Code Extension Generator。在命令行中运行以下命令来安装:
npm install -g yo generator-code
生成VS Code插件项目:在命令行中运行以下命令来生成一个新的VS Code插件项目:
yo code
按照提示输入您的插件项目的名称等信息。
- 开发插件:在生成的插件项目中,您可以编辑代码来实现您的插件功能。VS Code插件可以使用JavaScript或TypeScript编写。
- 调试插件:您可以使用VS Code自带的调试功能来调试您的插件。在插件项目中,您可以设置调试配置并启动调试会话。
- 发布插件:完成插件开发后,您可以将插件发布到VS Code的插件市场,让其他用户安装和使用您的插件。
基础插件helloWorld的实现
功能实现
接下来我们是创建项目的命令,进行初始化项目
yo code
_-----_ ╭──────────────────────────╮
| | │ Welcome to the Visual │
|--(o)--| │ Studio Code Extension │
`---------´ │ generator! │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? hello-world //项目名称
? What's the identifier of your extension? hello-world
? What's the description of your extension? this is my first vscode plugin //项目描述
? Initialize a git repository? No //是否初始化git仓库,这个可以以后在package.json中进行设置
? Bundle the source code with webpack? Yes //是否使用vscode开发
? Which package manager to use? npm //包管理工具
初始化完成后,项目目录结构如下:
通过查看package.json,可以看到启动命令为watch
启动项目后,进行测试
接下来会打开一个新的窗口页面,用于调试
在新的窗口中,使用commond+shift+p打开调用插件命令,在输入框中输入helloWorld
选择命令后,可以看到右下角出现弹窗
说明我们实现的第一个插件hello world已经完成。
代码分析
从package.json
中可以看出代码入口文件,”main”: “./dist/extension.js”, dist目录是通过webpack工具进行生成。
接下来查看webpack.config.js
配置文件,可以看出代码的入口文件'./src/extension.ts'
,从而可以知道插件的入口文件./src/extension.ts
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('hello-world.helloWorld', () => {
vscode.window.showInformationMessage('Hello World from hello-world!');
});
context.subscriptions.push(disposable);
}
export function deactivate() {}
除掉注释和空格,不到10行代码。
该文件导出2个方法,activate
和deactivate
;这是vscode官方的激活插件和销毁插件的方法。
接下来主要使用activate方法实现插件功能。
- vscode.commands.registerCommand:注册插件的启动命令
- vscode.window.showInformationMessage:显示弹出消息
- context.subscriptions.push:在上下文context中添加注册命令的实例
package.json中代码的配置
想要实现通过命令启动插件,还需要在package中配置
"contributes": {
"commands": [
{
"command": "hello-world.helloWorld",
"title": "Hello World"
}
]
},
contributes是用来扩展vscode方法的配置,在示例中,注册了commands,也就是说配置了一个命令,hello-world.helloWorld,title则是这个命令在vscode正式环境中使用的名称,也就是用户使用时的名称。除了commands外,vscode还支持注册menus,keybindings,languages
等多个配置,更多配置可以点击Contributes Point来查看。
改造command方法中的内容
创造出html的页面结构,将内容写入项目的index.html文件中
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
'hello-world.helloWorld',
() => {
const content = `<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1>Hello World</h1>
<div>vscode plugin create html page</div>
</body>
</html>
`;
// 获取项目路径地址
const uri = vscode.workspace!.workspaceFolders![0].uri.fsPath;
console.log(uri);
fs.writeFile(path.join(uri, 'index.html'), content, (err) => {
if (err) {
vscode.window.showErrorMessage('创建失败');
}
vscode.window.showInformationMessage('创建成功');
});
}
);
context.subscriptions.push(disposable);
}
export function deactivate() {}
执行编译代码,在调试窗口中通过命令查看插件。执行hello world命令,可以看到在项目中生成新的页面。
配合一起脚本命令,生成动态数据,就可以放入html文件中进行展示,比如当前时间,git当前用户,git的提交记录,代码量分析等。
webview展示
除了上面生成html页面,vscode还支持生成webview页面。
使用vscode.window.createWebviewPanel 创建一些简单的页面内容, 效果类似于欢迎页, vscdoe的版本发行说明, 如下
代码:
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
'hello-world.helloWorld',
() => {
// 创建一个 weilcome
const panel = vscode.window.createWebviewPanel(
'welcome', // webview面板的类型, 内部使用
'自定义欢迎页面标题', // table 标题
vscode.ViewColumn.One, // 显示在第一个编辑器
{
enableScripts: true, // 控制脚本是否在webview内容中启用, 默认为false(脚本禁用)
retainContextWhenHidden: true, // webview被隐藏时保持状态,避免被重置
}
);
panel.webview.html = `<h1>hello world</h1><div>welcome to vscode!</div>`;
}
);
context.subscriptions.push(disposable);
}
export function deactivate() {}
重新编译,查看插件内容。
webView动态显示内容
同时配置git命令,可以查看当前项目的分支。
通过使用node的子进程,可以使用spawn命令执行git命令。
新建gitTools类。
在gitTools中实现runGitCommand方法,内部通过spawn来实现。
class GitTools {
private cwd: string;
private user: string;
constructor(cwd: string) {
this.cwd = cwd;
this.user = '';
}
// 只简单实现一个git branch的命令,做测试使用
async branch(){
const res = await this.runGitCommand('git branch');
return res;
}
runGitCommand(command:string) {
return new Promise((resolve, reject) => {
var process = spawn(command, {
cwd: this.cwd,
shell: true,
});
var logMessage = `${command}`;
var cmdMessage = '';
process.stdout.on('data', (data) => {
console.log(`${logMessage} start ---`, data);
if (!data) {
reject(`${logMessage} error1 : ${data}`);
} else {
cmdMessage = data.toString();
}
});
process.on('close', (data) => {
console.log(`${logMessage} close ---`, data);
if (data) {
reject(`${logMessage} error2 ! ${data}`);
} else {
console.log(`${logMessage} success !`);
resolve(cmdMessage);
}
});
});
}
}
完整的extension.ts
代码
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import { spawn } from 'child_process';
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
'hello-world.helloWorld',
() => {
// 创建一个 weilcome
const panel = vscode.window.createWebviewPanel(
'gitBranch', // webview面板的类型, 内部使用
'查看项目分支', // table 标题
vscode.ViewColumn.One, // 显示在第一个编辑器
{
enableScripts: true, // 控制脚本是否在webview内容中启用, 默认为false(脚本禁用)
retainContextWhenHidden: true, // webview被隐藏时保持状态,避免被重置
}
);
const git = new GitTools(vscode.workspace!.workspaceFolders![0].uri.fsPath);
git.branch().then((res)=>{
console.log(res);
panel.webview.html = `<h1>hello world</h1>
<div>welcome to vscode! </div>
<p>${res}</p>`;
});
}
);
context.subscriptions.push(disposable);
}
class GitTools {
private cwd: string;
private user: string;
constructor(cwd: string) {
this.cwd = cwd;
this.user = '';
}
async branch(){
const res = await this.runGitCommand('git branch');
return res;
}
runGitCommand(command:string) {
return new Promise((resolve, reject) => {
var process = spawn(command, {
cwd: this.cwd,
shell: true,
});
var logMessage = `${command}`;
var cmdMessage = '';
process.stdout.on('data', (data) => {
console.log(`${logMessage} start ---`, data);
if (!data) {
reject(`${logMessage} error1 : ${data}`);
} else {
cmdMessage = data.toString();
}
});
process.on('close', (data) => {
console.log(`${logMessage} close ---`, data);
if (data) {
reject(`${logMessage} error2 ! ${data}`);
} else {
console.log(`${logMessage} success !`);
resolve(cmdMessage);
}
});
});
}
}
export function deactivate() {}
查看调试窗口,可以看到本地的分支。
sideBar展示treeView
上面是在窗口中展示webView功能,那么如何在侧边拦显示相关数据呢?比如下面的源码管理
要实现这一功能,主要利用[vscode.window.createTreeView](https://code.visualstudio.com/api/references/vscode-api#window)
这个API。需要2个参数
- viewId:这个id是在package.json中进行自定义设置
- options:提供的treeData数据
新建TreeProvider
类
class TreeProvider implements vscode.TreeDataProvider<vscode.TreeItem> {
// 创建一个事件发射器,用于通知树数据发生变化
private _onDidChangeTreeData: vscode.EventEmitter<
vscode.TreeItem | undefined
> = new vscode.EventEmitter<vscode.TreeItem | undefined>();
// 定义一个只读的事件,允许外部订阅树数据变化事件
readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | undefined> =
this._onDidChangeTreeData.event;
// 定义刷新方法,用于通知视图数据发生变化
refresh(): void {
this._onDidChangeTreeData.fire(undefined);
}
// 获取树中的单个项目,这里可以定义如何显示单个项目
getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
return element;
}
// 获取树的子元素,可以是一个异步操作
getChildren(element?: vscode.TreeItem): Promise<vscode.TreeItem[]> {
return new Promise(async (resolve, reject) => {
const gitTool = new GitTools(
vscode.workspace.workspaceFolders![0].uri.fsPath
);
const userList: string = (await gitTool.allUser()) as string;
const result = userList
.split('\n')
.filter(Boolean)
.map((item: string) => {
return new vscode.TreeItem(
item,
vscode.TreeItemCollapsibleState.None
);
});
resolve(result);
});
}
}
使用到了gitTool的allUser方法,在GitTool中添加下allUser方法
async allUser() {
// git log --pretty=format:"%an <%ae>"| sort -u
try {
const res = await this.runGitCommand(
`git log --pretty=format:"%an <%ae>"| sort -u`
);
return res;
} catch (err) {
console.error(err);
}
return false;
}
设置package.json
package.json中添加”contributes”值:
- “viewsContainers”
- “activitybar”:
- “id”:这个id值是下面views对象中的key
- “title”:显示的标题
- “icon”:用于侧边展示的图标ICON
- “activitybar”:
- “views”
- “user-list”:这个key值是在
activitybar
中定义的id
- “id”:这个id是创建treeview的标志
- “name”:创建treeView的名称
- “user-list”:这个key值是在
这里需要特别注意:
- 定义views的key,一定要和activitybar的id对应;
- views下key中的id,是创建treeView的标志,一定要对应上。
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "hello-list",
"title": "helloWorld",
"icon": "images/search.svg"
}
]
},
"views": {
"hello-list": [
{
"id": "git-userlist",
"name": "项目成员"
}
]
},
}
重新编译项目,并运行启动调试
可以在新窗口中看到如下
sideBar的标题是有:activitybar.title+views.name 组合而成。
对treeView中数据进行事件操作
获取到了tree数据,就需要对每一个item的数据进行操作处理。常见的就是在item后添加处理事件。
可以通过在package.json中添加”menus”配置
- “view/title”: 表示在sideBar的顶部添加按钮
- “view/item/context”: 表示treeData数据的item每一项后添加事件按钮
"menus": {
"view/title": [
{
"when": "view == git-userlist",
"command": "userList.add",
"group": "navigation"
}
],
"view/item/context": [
{
"when": "view == git-userlist",
"command": "userList.item.check",
"group": "inline"
}
]
},
"commands": [
{
"command": "hello-world.helloWorld",
"title": "Hello World"
},
{
"command": "userList.item.check",
"title": "查看"
}
]
同时要在commands中进行对应配置
重新编译调试查看项目
然后在项目中添加对应的点击事件
在context.subscriptions
中添加事件处理
export function activate(context: vscode.ExtensionContext) {
// 创建treeView
vscode.window.createTreeView('git-userlist', {
treeDataProvider: new TreeProvider(),
});
let disposable = vscode.commands.registerCommand(
'hello-world.helloWorld',
() => {
// 创建一个 welcome
const panel = vscode.window.createWebviewPanel(
'gitBranch', // webview面板的类型, 内部使用
'查看项目分支', // table 标题
vscode.ViewColumn.One, // 显示在第一个编辑器
{
enableScripts: true, // 控制脚本是否在webview内容中启用, 默认为false(脚本禁用)
retainContextWhenHidden: true, // webview被隐藏时保持状态,避免被重置
}
);
const git = new GitTools(
vscode.workspace!.workspaceFolders![0].uri.fsPath
);
git.branch().then((res) => {
console.log(res);
panel.webview.html = `<h1>hello world</h1>
<div>welcome to vscode! </div>
<p>${res}</p>`;
});
}
);
context.subscriptions.push(disposable,
// 为了简便,直接添加事件注册
vscode.commands.registerCommand(`helloList.add`, () => {
console.log(vscode.workspace!.workspaceFolders![0].uri.fsPath, 'add to git');
}),
vscode.commands.registerCommand(`helloList.item.check`, (user) => {
let userName = user.label.split(' ')[0];
console.log(user, 'checkcheckcheckcheck');
vscode.window.showInformationMessage(`Hello ${userName}`);
})
);
}
设置Options选择框
有时候为了方便用户的输入,可以做出快速选择的内容提示框。如日期的选择,有本月,上个月,半年等。
使用的是vscode.window.showQuickPick
API
import * as vscode from "vscode";
export function activate(context: vscode.ExtensionContext) {
// 注册一个命令 learn-vscode-extends.quickPick
const disposable = vscode.commands.registerCommand("learn-vscode-extends.quickPick", () => {
// 打开一个快速选择列表
vscode.window.showQuickPick(
// ["选项一", "选项二", "选项三"], // 简单的显示多个选项
[
{ // 对象的形式可以配置更多东西
label: "选项一",
description: "选项一描述$(bug)", // 可以指定官方提供的图标id https://code.visualstudio.com/api/references/icons-in-labels#icon-listing
detail: "选项一详细信息",
mate: { // 这里也可以放一些自定义的对象
script: "learn-vscode-extends.helloWrold"
},
},
{
label: "选项二",
description: "选项二描述",
detail: "选项二详细信息$(gear)",
}
],
{
title: "请选择一个选项", // 标题
placeHolder: "用户类型", // 占位符文本
canPickMany: false, // 是否可以多选
}
).then((res: vscode.QuickPickItem | undefined) => {
if (!res) return;
console.log(res); // 这里就是上面数组中对应的对象信息
})
});
context.subscriptions.push(disposable);
}
export function deactivate() { }
实战案例开发插件gitCodeStatics
gitCodeStatics插件使用
方便日常开发中统计代码提交量,以前使用git log再加上shell的统计计算,那么是不是就可以将这些功能做成一个插件更加方便使用呢,插件显示出所有的作者,选择作者后可以选择需要统计的日期。
以上代码统计记录来自 github.com/microsoft/v…, 微软官方的vscode插件示例项目。
插件已经发布,可以自行下载查看使用。marketplace.visualstudio.com/items?itemN…
开发流程
使用webpack和ts创建开发模板
参考上边helloWorld项目创建流程
创建gitTools类
统计项目下用户
这里使用node的 spawnSync 获取git log数据。
import { spawn, spawnSync } from 'child_process';
import dayjs = require('dayjs');
export class GitTools {
private cwd: string;
private user: string;
constructor(cwd: string) {
this.cwd = cwd;
this.user = '';
this.init();
}
async init() {
this.startChildProcess('git', ['remote', '-v'])
.then(async (res) => {
this.user = await this.startChildProcess('git', [
'config',
'user.name',
]);
console.log(this.user, 'username');
})
.catch((err) => {
console.error('git no remote', err);
});
}
async remote() {
try {
var params = ['remote', '-v'];
let result = await this.startChildProcess('git', params);
return result;
} catch (err) {
console.error(err);
}
}
async logMonth(params: any) {
// const res = await this.startChildProcess('echo', ['-e', 'A line1\nB line 2', '|', 'awk', 'BEGIN{ print "Start" } { print } END{ print "End" }']);
// 根据日期,作者,统计代码的提交行数
const res: unknown = await this.startChildProcessNoParams(
`git log --author="${params.author}" --pretty=tformat: --numstat --since=${params.since} --until=${params.until}`
);
let resArr: any[] = [];
(res as string).split('\n').forEach((item: any) => {
item && resArr.push(item.split('\t'));
});
if (resArr.length === 0) {
return 'No submitted data!';
}
// 处理添加行数据
let addLineNum = resArr.reduce((pre, current) => {
return (
(isNaN(parseInt(pre)) ? 0 : parseInt(pre)) +
(isNaN(parseInt(current[0])) ? 0 : parseInt(current[0]))
);
}, 0);
// 处理删除行数据
let delLineNum = resArr.reduce((pre, current) => {
return (
(isNaN(parseInt(pre)) ? 0 : parseInt(pre)) +
(isNaN(parseInt(current[1])) ? 0 : parseInt(current[1]))
);
}, 0);
return `add line: ${addLineNum} remove line: ${delLineNum} total edit line: ${
addLineNum + delLineNum
}`;
}
// 跨越多个月份
async logAcrossMonths(userName: string, since: string, until: string) {
let resultArr: Array<{ date: string; value: number | null }> = [];
let monthArr: any[] = [];
monthArr.push({
label: dayjs(since).format('YYYY-MM'),
start: since,
end: dayjs(since).endOf('month').format('YYYY-MM-DD'),
});
let diff = dayjs(until).diff(since, 'month');
for (let i = 1; i <= diff; i++) {
monthArr.push({
label: dayjs(since).add(i, 'month').format('YYYY-MM'),
start: dayjs(since)
.add(i, 'month')
.startOf('month')
.format('YYYY-MM-DD'),
end: dayjs(since).add(i, 'month').endOf('month').format('YYYY-MM-DD'),
});
}
if (
monthArr[monthArr.length - 1].label !== dayjs(until).format('YYYY-MM')
) {
monthArr.push({
label: dayjs(until).format('YYYY-MM'),
start: dayjs(until).startOf('month').format('YYYY-MM-DD'),
end: until,
});
}
/* await Promise.all(fileNames.map(async (file) => {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
})); */
const promiseAll = await Promise.all(
monthArr.map(async (item) => {
let res: any = await this.startChildProcessNoParams(
`git log --author="${userName}" --pretty=tformat: --numstat --since=${item.start} --until=${item.end}`
);
if (res.length === '') {
resultArr.push({
date: item.label,
value: null,
});
}
let resArr: any[] = [];
(res as string).split('\n').forEach((item: any) => {
item && resArr.push(item.split('\t'));
});
// 处理添加行数据
let addLineNum = resArr.reduce((pre, current) => {
return (
(isNaN(parseInt(pre)) ? 0 : parseInt(pre)) +
(isNaN(parseInt(current[0])) ? 0 : parseInt(current[0]))
);
}, 0);
// 处理删除行数据
let delLineNum = resArr.reduce((pre, current) => {
return (
(isNaN(parseInt(pre)) ? 0 : parseInt(pre)) +
(isNaN(parseInt(current[1])) ? 0 : parseInt(current[1]))
);
}, 0);
resultArr.push({
date: item.label,
value: addLineNum + delLineNum,
});
return resultArr;
})
);
// 查询代码量
// let returnVal = resultArr.filter((item) => Boolean(item.value));
// resolve(returnVal)
return promiseAll[0].filter((item) => Boolean(item.value));
}
async branch() {
try {
var params = ['branch', '-a'];
let result = await this.startChildProcess('git', params);
return result.toString();
} catch (err) {
console.error(err);
}
return false;
}
async allUser() {
// git log --pretty=format:"%an <%ae>"| sort -u
try {
const res = await this.startChildProcessNoParams(
`git log --pretty=format:"%an <%ae>"`
);
let resArr = (res as string).split('\n');
let resSet = new Set();
resArr.forEach((item) => {
item && resSet.add(item);
});
return [...resSet]
.sort((a: unknown, b: unknown) => {
return (a as string)[0] > (b as string)[0] ? 1 : -1;
})
.join('\n');
} catch (err) {
console.error(err);
}
return false;
}
async status() {
try {
var params = ['status', '-s'];
let result = await this.startChildProcess('git', params);
return result;
} catch (err) {
console.error(err);
}
return false;
}
startChildProcess(command: string, params: string[]): Promise<string> {
return new Promise((resolve, reject) => {
var process = spawn(command, params, {
cwd: this.cwd,
shell: true,
});
var logMessage = `${command} ${params[0]}`;
var cmdMessage = '';
process.stdout.on('data', (data) => {
console.log(`${logMessage} start ---`, data);
if (!data) {
reject(`${logMessage} error1 : ${data}`);
} else {
cmdMessage = data.toString();
}
});
process.on('close', (data) => {
console.log(`${logMessage} close ---`, data);
if (data) {
reject(`${logMessage} error2 ! ${data}`);
} else {
console.log(`${logMessage} success !`);
resolve(cmdMessage);
}
});
});
}
startChildProcessNoParams(command: string) {
return new Promise((resolve, reject) => {
var process = spawnSync(command, {
cwd: this.cwd,
shell: true,
encoding: 'utf8',
});
var logMessage = `${command}`;
var cmdMessage = '';
if (process.error) {
console.log('ERROR: ', process.error);
reject(process.error);
}
resolve(process.stdout);
});
}
}
进行user用户统计时,开始使用代码155行的shell命令进行,还有行数的统计使用了shell的awk进行统计。这样开发完成在Mac上可以正常使用,但是到了widonws系统就报错,windows的默认执行脚本工具是powershell,无法解析shell命令。
然后采用纯js进行数据解析处理。将log数据全部读取再进行相加。坑是一个一个的来,接下来遇到编辑器默认现在的log条数有限制,不能一次显示完,那这样就又无法统计。
只能采用node的 spawnSync
APi强制把数据读取完全。所以新增了startChildProcessNoParams
方法;才算是把踩的坑给填上,这样就能兼容windows系统了。
统计用户提交代码行数
默认实现功能:
- 统计用户当月代码
- 统计用户上个月的代码
- 统计用户最近6个月的代码,可以作为图表显示
- 自定义需要的统计日期
以上只是根据传入的查询日期不同,进行的分类统计
创建treeData类
将用户数据做成tree展示
主要用来显示项目的用户,可以放到vscode的左侧栏中,将用户数据做成tree展示。
查询数据来自 github.com/microsoft/v…,微软提供的vscode官方插件案例项目。
async allUser() {
// 使用| sort -u shell命令进行统计,不兼容windows
// git log --pretty=format:"%an <%ae>"| sort -u
try {
const res = await this.startChildProcessNoParams(
`git log --pretty=format:"%an <%ae>"`
);
let resArr = (res as string).split('\n');
let resSet = new Set();
resArr.forEach((item) => {
item && resSet.add(item);
});
return [...resSet]
.sort((a: unknown, b: unknown) => {
return (a as string)[0] > (b as string)[0] ? 1 : -1;
})
.join('\n');
} catch (err) {
console.error(err);
}
return false;
}
查询出数据,将数据放入到TreeData.ts类中;
import * as vscode from 'vscode';
import { GitTools } from './gitTools';
// 创建一个类 TreeProvider,实现了 TreeDataProvider 接口
export default class TreeProvider
implements vscode.TreeDataProvider<vscode.TreeItem>
{
// 创建一个事件发射器,用于通知树数据发生变化
private _onDidChangeTreeData: vscode.EventEmitter<
vscode.TreeItem | undefined
> = new vscode.EventEmitter<vscode.TreeItem | undefined>();
// 定义一个只读的事件,允许外部订阅树数据变化事件
readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | undefined> =
this._onDidChangeTreeData.event;
// 定义刷新方法,用于通知视图数据发生变化
refresh(): void {
this._onDidChangeTreeData.fire(undefined);
}
// 获取树中的单个项目,这里可以定义如何显示单个项目
getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
return element;
}
// 获取树的子元素,可以是一个异步操作
getChildren(element?: vscode.TreeItem): Promise<vscode.TreeItem[]> {
// 在这里实现获取树的子元素的逻辑
// 可以返回一个 Promise 来异步获取子元素
// 如果没有子元素,可以返回一个空数组
// 在实际使用中,你需要根据你的插件逻辑来实现这个方法
return new Promise(async (resolve, reject) => {
const gitTool = new GitTools(vscode.workspace.workspaceFolders![0].uri.fsPath);
const userList: string = await gitTool.allUser() as string;
const result = userList.split("\n").filter(Boolean).map((item: string) => {
return new vscode.TreeItem(item, vscode.TreeItemCollapsibleState.None)
});
resolve(result)
});
}
}
添加查看、刷新命令
修改package.json中添加配置
- 在package.json中添加配置,contributes下新增
viewsContainers
和views
views的key是activitybar
的id值,要相对应。
- 给menus添加属性配置
- 在commands中添加2个对应的命令
这里定义的命令,稍后在extension.js中使用
修改extension.js
的代码
context.subscriptions.push(
disposable,
commands.registerCommand(`userList.refresh`, () => {
// console.log(workspace!.workspaceFolders![0].uri.fsPath, 'refresh to git');
// 刷新数据列表
window.createTreeView('gitcode-userlist', {
treeDataProvider: new TreeProvider(),
});
}),
commands.registerCommand(`userList.item.search`, (user) => {
let userName = user.label.split(' ')[0];
// 打开一个快速选择列表
window
.showQuickPick(
[
{
label: 'Current month',
detail: 'Submit code statistics current month',
},
{
label: 'Last month',
detail: 'Submit code statistics Last month',
},
{
label: 'Past six months',
detail: 'Submit code statistics Last six month',
},
{
label: 'Custom date query',
detail: 'setting custom query date ',
},
],
{
title: 'Query date', // 标题
placeHolder: 'Please select an option!', // 占位符文本
canPickMany: false, // 是否可以多选
}
)
.then((res: QuickPickItem | undefined) => {
if (!res) return;
const { label } = res;
// console.log(res, userName, dayjs().format(), 'userNameuserName'); // 这里就是上面数组中对应的对象信息
if (label === 'Current month') {
searchBySetDate(
userName,
dayjs().startOf('month').format('YYYY-MM-DD'),
dayjs().endOf('month').format('YYYY-MM-DD')
);
} else if (label === 'Last month') {
searchBySetDate(
userName,
dayjs().add(-1, 'month').startOf('month').format('YYYY-MM-DD'),
dayjs().add(-1, 'month').endOf('month').format('YYYY-MM-DD')
);
} else if (label === 'Past six months') {
searchByCustomDate(
userName,
dayjs().add(-5, 'month').startOf('month').format('YYYY-MM-DD'),
dayjs().endOf('month').format('YYYY-MM-DD')
);
} else {
// 自定义日期
searchByCustomDate(userName);
}
});
})
);
难点【踩坑】记录:
- shell进行统计,系统不兼容。只能采用纯git log命令,然后再进行js处理。
- 使用node的spawn返回log返回日志时,由于vscode缓存限制,只显示20条左右;然后改用了spawnsync同步显示的方法,将数据显示完整,然后进行js 统计
- 目前使用网络url引用chart插件,还没有找到本地引入js库的方案。【确保网络正常是可以使用,以后有解决方案了再调整】
原文链接:https://juejin.cn/post/7352075755821498394 作者:北鸟南游