一、customTaskProvider使用介绍
简介:customTaskProvider是一种任务提供者,用于为特定应用程序或插件提供自定义任务。它可以让用户通过编写自己的脚本代码、插件或者应用程序来定义自己的任务。
1、在package.json中添加taskDefinitions
package.json
{
"contributes": { //用于在 Visual Studio Code 的扩展中注册提供者(如自定义任务)。
"taskDefinitions": [ //定义一个任务,指定其名称、描述、属性等。
{
"type": "custombuildscript", //任务类型被命名为 custombuildscript,支持自定义
"required": [ //一个列表,列出了在执行此任务之前必须设置的所有属性。
"flavor"
],
"properties": { //一个对象,其中定义了可在任务定义中设置的所有属性名及其类型和描述。
"flavor": {
"type": "string",
"description": "The build flavor. Should be either '32' or '64'."
},
"flags": {
"type": "array",
"description": "Additional build flags."
}
}
}
]
}
}
添加后在vscode->终端->运行任务列表中可以查看所配置的任务
2、在extension.ts中注册任务
extension.ts
import * as vscode from 'vscode';
//导入 CustomBuildTaskProvider 类,该类实现了自定义任务提供者,并用于实现自定义任务。
import { CustomBuildTaskProvider } from './customTaskProvider';
//定义一个全局变量 customTaskProvider,默认为未定义。
let customTaskProvider: vscode.Disposable | undefined;
//导出 activate 函数,它将被VS Code在加载扩展时调用。
export function activate(_context: vscode.ExtensionContext): void {
//如果未在任何工作区下打开此扩展,则返回。
const workspaceRoot = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0))
? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined;
if (!workspaceRoot) {
return;
}
//对 customTaskProvider 进行初始化并跟踪注册的自定义任务提供者。使用 registerTaskProvider 注册任务提供者时需要两个参数,第一个是任务类型的名称,它必须与任务定义 (taskDefinitions) 中的类型字段对应。第二个参数是实现自定义任务提供者的对象,因此在这里使用了 CustomBuildTaskProvider 实例。
customTaskProvider = vscode.tasks.registerTaskProvider(CustomBuildTaskProvider.CustomBuildScriptType, new CustomBuildTaskProvider(workspaceRoot));
}
export function deactivate(): void {
//VS Code在卸载扩展时调用。该函数负责按顺序释放所有占用扩展上下文处理资源,并将其标记为回收。在这里,如果 customTaskProvider 已定义,则被释放。
if (customTaskProvider) {
customTaskProvider.dispose();
}
}
3、实现CustomBuildTaskProvider 类
customTaskProvider.ts
import * as path from 'path';
import * as vscode from 'vscode';
//定义了接口 CustomBuildTaskDefinition,它继承了 vscode.TaskDefinition 接口,并包括两个属性:flavor 是字符串类型的构建配置,flags 是一个可选的字符串数组,表示传递给构建命令的附加标志。
interface CustomBuildTaskDefinition extends vscode.TaskDefinition {
/**
* The build flavor. Should be either '32' or '64'.
*/
flavor: string;
/**
* Additional build flags
*/
flags?: string[];
}
//定义了一个 CustomBuildTaskProvider 类,并实现了 vscode.TaskProvider 接口。
export class CustomBuildTaskProvider implements vscode.TaskProvider {
//定义了一个静态变量 CustomBuildScriptType,它代表这个自定义任务提供者注册的任务类型。
static CustomBuildScriptType = 'custombuildscript';
//定义了该类的成员变量:tasks 表示 VS Code 中构建任务的集合
private tasks: vscode.Task[] | undefined;
//sharedState 是一个状态共享变量,它在任务执行之间保存共享状态值。
private sharedState: string | undefined;
//定义了 constructor 构造函数,它的作用是接收一个构建脚本所在的工作目录路径,并将该路径保存到类的成员变量 workspaceRoot 中。
constructor(private workspaceRoot: string) { }
//实现了 provideTasks 方法,该方法返回一个数组 Task[],其中包含所有该提供者支持的任务。
public async provideTasks(): Promise<vscode.Task[]> {
return this.getTasks();
}
//实现了 resolveTask 方法,该方法接收一个任务作为参数,并返回一个新的 Task 对象,用于再次运行任务。
public resolveTask(_task: vscode.Task): vscode.Task | undefined {
const flavor: string = _task.definition.flavor;
if (flavor) {
const definition: CustomBuildTaskDefinition = <any>_task.definition;
return this.getTask(definition.flavor, definition.flags ? definition.flags : [], definition);
}
return undefined;
}
//实现了 getTasks 方法,该方法返回 Task[] 数组,其中包含了所有支持的构建任务。
private getTasks(): vscode.Task[] {
if (this.tasks !== undefined) {
return this.tasks;
}
// In our fictional build, we have two build flavors
const flavors: string[] = ['32', '64'];
// Each flavor can have some options.
const flags: string[][] = [['watch', 'incremental'], ['incremental'], []];
this.tasks = [];
flavors.forEach(flavor => {
flags.forEach(flagGroup => {
this.tasks!.push(this.getTask(flavor, flagGroup));
});
});
return this.tasks;
}
//实现了 getTask 方法,该方法返回一个新的 Task 对象,用于执行指定的构建任务。
private getTask(flavor: string, flags: string[], definition?: CustomBuildTaskDefinition): vscode.Task {
if (definition === undefined) {
definition = {
type: CustomBuildTaskProvider.CustomBuildScriptType,
flavor,
flags
};
}
return new vscode.Task(definition, vscode.TaskScope.Workspace, `${flavor} ${flags.join(' ')}`,
CustomBuildTaskProvider.CustomBuildScriptType, new vscode.CustomExecution(async (): Promise<vscode.Pseudoterminal> => {
// When the task is executed, this callback will run. Here, we setup for running the task.
return new CustomBuildTaskTerminal(this.workspaceRoot, flavor, flags, () => this.sharedState, (state: string) => this.sharedState = state);
}));
}
}
//定义了 CustomBuildTaskTerminal 类,这个类实现了 vscode.Pseudoterminal 接口,并用于执行自定义构建任务的实际过程。
class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
private writeEmitter = new vscode.EventEmitter<string>();
onDidWrite: vscode.Event<string> = this.writeEmitter.event;
private closeEmitter = new vscode.EventEmitter<number>();
onDidClose?: vscode.Event<number> = this.closeEmitter.event;
private fileWatcher: vscode.FileSystemWatcher | undefined;
//实现了 CustomBuildTaskTerminal 类的构造函数,并接收三个参数:workspaceRoot 表示构建脚本所在的工作目录路径,flavor 表示构建配置(32 或 64 位),flags 表示传递给构建命令的附加标志。
constructor(private workspaceRoot: string, private flavor: string, private flags: string[], private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) {
}
//实现了 CustomBuildTaskTerminal 类的 open方法,用于打开终端后执行的操作。
open(initialDimensions: vscode.TerminalDimensions | undefined): void {
// At this point we can start using the terminal.
//如果该任务处于监听状态,则当监听的文件做出相应改变后将会触发对应的响应函数。
if (this.flags.indexOf('watch') > -1) {
const pattern = path.join(this.workspaceRoot, 'customBuildFile');
this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern);
this.fileWatcher.onDidChange(() => this.doBuild());
this.fileWatcher.onDidCreate(() => this.doBuild());
this.fileWatcher.onDidDelete(() => this.doBuild());
}
this.doBuild();
}
//实现了 CustomBuildTaskTerminal 类的 close方法,用于关闭终端后执行的操作。
close(): void {
// The terminal has been closed. Shutdown the build.
if (this.fileWatcher) {
this.fileWatcher.dispose();
}
}
//具体任务业务处理方法。实现了 doBuild 方法,该方法用于执行构建任务,并在终端中显示构建结果。该方法返回一个 Promise 对象,用于在任务执行完毕后告知任务的状态。
private async doBuild(): Promise<void> {
return new Promise<void>((resolve) => {
this.writeEmitter.fire('Starting build...\r\n');
//性能优化,根据执行任务的incremental属性来判断是否使用上次的结果。
let isIncremental = this.flags.indexOf('incremental') > -1;
if (isIncremental) {
if (this.getSharedState()) {
this.writeEmitter.fire('Using last build results: ' + this.getSharedState() + '\r\n');
} else {
isIncremental = false;
this.writeEmitter.fire('No result from last build. Doing full build.\r\n');
}
}
// Since we don't actually build anything in this example set a timeout instead.
//模拟具体打包业务逻辑。
setTimeout(() => {
const date = new Date();
this.setSharedState(date.toTimeString() + ' ' + date.toDateString());
this.writeEmitter.fire('Build complete.\r\n\r\n');
如果任务不是监听状态,则当执行完任务后及时更新任务为完成状态。
if (this.flags.indexOf('watch') === -1) {
this.closeEmitter.fire(0);
resolve();
}
}, isIncremental ? 1000 : 4000);
});
}
}
总之,CustomBuildTaskProvider
类实现了 vscode.TaskProvider
接口,用于为用户提供在 VS Code 中执行自定义构建脚本的功能。CustomBuildTaskTerminal
类用于在终端中执行实际的构建任务。每个构建任务都在单独的终端中执行,并且如果构建过程中需要在终端中显示输出,则可以使用 CustomBuildTaskProvider
类中的 CustomBuildTaskTerminal
类中的writeEmitter
进行终端提示。
4、在外部使用CustomBuildTaskProvider 类其中定义的任务
需求:监听customBuildFile文件,并且任务为非执行状态。需要通过vscode工作区进行监听文件,当文件由未保存状态更改为保存状态时触发任务,并当任务执行完成后及时关闭任务。
extension.ts
// 创建组件实例
const CustomBuildTask = new CustomBuildTaskProvider(workspaceRoot);
// 监听文件保存事件
vscode.workspace.onDidSaveTextDocument(async (document) => {
const fileNameArr = document.fileName.split("\\");
if (fileNameArr[fileNameArr.length - 1] === "customBuildFile") {
// 调用任务
const tasks = await CustomBuildTask.provideTasks();
vscode.tasks.executeTask(tasks[0]);
}
});
二、rakeTaskProvider使用介绍
简介:rakeTaskProvider是一种在 VS Code 编辑器中更方便地运行 Ruby on Rails 项目的方式。rakeTaskProvider 是 Visual Studio Code 内置的一种扩展,它使得开发人员可以直接在编辑器中定义和运行 rake 任务。在 Ruby on Rails 项目中,rake 是一个非常有用的工具,用于运行各种任务,例如生成数据库迁移、启动服务器、运行测试等等。
rakeTaskProvider 扩展提供了一组简单的 API 用于注册和管理 rake 任务。它允许开发人员在项目的顶层目录中定义 Rakefile,描述可用的 rake 任务及其参数。一旦注册了任务,就可以通过 VS Code 命令面板或终端面板轻松运行并调试这些任务。另外,该扩展还支持启动任务时的动态参数。
1、在package.json中添加taskDefinitions
package.json
{
"contributes": { //用于在 Visual Studio Code 的扩展中注册提供者(如自定义任务)。
"taskDefinitions": [ //定义一个任务,指定其名称、描述、属性等。
{
"type": "rake", // 任务类型被命名为 rake,支持自定义
"required": [ //一个列表,列出了在执行此任务之前必须设置的所有属性。
"task"
],
"properties": { //一个对象,其中定义了可在任务定义中设置的所有属性名及其类型和描述。
"task": {
"type": "string",
"description": "The Rake task to customize"
},
"file": {
"type": "string",
"description": "The Rake file that provides the task. Can be omitted."
}
}
}
]
}
}
添加后在vscode->终端->运行任务列表中可以查看所配置的任务
2、在extension.ts中注册任务
extension.ts
import * as vscode from 'vscode';
//导入 RakeTaskProvider 类,该类实现了自定义任务提供者,并用于实现自定义任务。
import { RakeTaskProvider } from './rakeTaskProvider';
//定义一个全局变量 rakeTaskProvider,默认为未定义。
let rakeTaskProvider: vscode.Disposable | undefined;
//导出 activate 函数,它将被VS Code在加载扩展时调用。
export function activate(_context: vscode.ExtensionContext): void {
//如果未在任何工作区下打开此扩展,则返回。
const workspaceRoot = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0))
? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined;
if (!workspaceRoot) {
return;
}
//对 rakeTaskProvider 进行初始化并跟踪注册的自定义任务提供者。使用 registerTaskProvider 注册任务提供者时需要两个参数,第一个是任务类型的名称,它必须与任务定义 (taskDefinitions) 中的类型字段对应。第二个参数是实现自定义任务提供者的对象,因此在这里使用了 rakeTaskProvider 实例。
rakeTaskProvider = vscode.tasks.registerTaskProvider(RakeTaskProvider.RakeType, new RakeTaskProvider(workspaceRoot));
export function deactivate(): void {
//VS Code在卸载扩展时调用。该函数负责按顺序释放所有占用扩展上下文处理资源,并将其标记为回收。在这里,如果 rakeTaskProvider 已定义,则被释放。
if (rakeTaskProvider) {
rakeTaskProvider.dispose();
}
}
3、实现RakeTaskProvider类
rakeTaskProvider.ts
import * as path from 'path';
import * as fs from 'fs';
import * as cp from 'child_process';
import * as vscode from 'vscode';
import { error } from 'console';
//定义了一个 RakeTaskProvider 类,并实现了 vscode.TaskProvider 接口。
export class RakeTaskProvider implements vscode.TaskProvider {
//定义了一个静态变量 RakeType,它代表这个自定义任务提供者注册的任务类型。
static RakeType = 'rake';
//定义了该类的成员变量:rakePromise 表示 Rake 任务的 Promise集合
private rakePromise: Thenable<vscode.Task[]> | undefined = undefined;
//定义了 constructor 构造函数,它的作用是接收一个构建脚本所在的工作目录路径,并监听该路径下的Rakefile文件,根据对文件的不同操作做出相应的处理。
constructor(workspaceRoot: string) {
const pattern = path.join(workspaceRoot, 'Rakefile');
const fileWatcher = vscode.workspace.createFileSystemWatcher(pattern);
fileWatcher.onDidChange(() => this.rakePromise = undefined);
fileWatcher.onDidCreate(() => this.rakePromise = undefined);
fileWatcher.onDidDelete(() => this.rakePromise = undefined);
}
//类中的 provideTasks 方法用于获取所有 Rake 任务并返回一个包含这些任务的 Promise。当调用 provideTasks 时,它首先将获取任务的 Promise 存储在成员变量 rakePromise 中。如果 rakePromise 不存在,则调用 getRakeTasks 函数获取 Rake 任务,并将结果存储在 rakePromise 中。
public provideTasks(): Thenable<vscode.Task[]> | undefined {
if (!this.rakePromise) {
this.rakePromise = getRakeTasks();
}
return this.rakePromise;
}
//类中的 resolveTask 方法用于在 VS Code 中解析特定的 Rake 任务。这个方法检查任务名称,如果名称存在,则将该名称传递给 vscode.Task 构造函数以创建一个新的任务。该任务使用 RakeTaskDefinition 对象作为其定义,并执行指定的 Rake 任务。
public resolveTask(_task: vscode.Task): vscode.Task | undefined {
const task = _task.definition.task;
// A Rake task consists of a task and an optional file as specified in RakeTaskDefinition
// Make sure that this looks like a Rake task by checking that there is a task.
if (task) {
// resolveTask requires that the same definition object be used.
const definition: RakeTaskDefinition = <any>_task.definition;
return new vscode.Task(definition, _task.scope ?? vscode.TaskScope.Workspace, definition.task, 'rake', new vscode.ShellExecution(`rake ${definition.task}`));
}
return undefined;
}
}
//除此之外,RakeTaskProvider 类还定义了几个辅助函数来帮助实现上述功能:
//exists 函数用于检查指定的文件或目录是否存在。
function exists(file: string): Promise<boolean> {
return new Promise<boolean>((resolve, _reject) => {
fs.exists(file, (value) => {
resolve(value);
});
});
}
//exec 函数用于异步执行 shell 命令。
function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: string; stderr: string }> {
return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
cp.exec(command, options, (error, stdout, stderr) => {
if (error) {
reject({ error, stdout, stderr });
}
resolve({ stdout, stderr });
});
});
}
//getOutputChannel 函数用于获取输出通道,以便在任务执行期间将任何错误或警告消息记录到其中。
let _channel: vscode.OutputChannel;
function getOutputChannel(): vscode.OutputChannel {
if (!_channel) {
_channel = vscode.window.createOutputChannel('Rake Auto Detection');
}
return _channel;
}
interface RakeTaskDefinition extends vscode.TaskDefinition {
/**
* The task name
*/
task: string;
/**
* The rake file containing the task
*/
file?: string;
}
//isBuildTask 和 isTestTask 函数用于检查给定任务是否可分配到“构建”或“测试”任务组中。
const buildNames: string[] = ['build', 'compile', 'watch'];
function isBuildTask(name: string): boolean {
for (const buildName of buildNames) {
if (name.indexOf(buildName) !== -1) {
return true;
}
}
return false;
}
const testNames: string[] = ['test'];
function isTestTask(name: string): boolean {
for (const testName of testNames) {
if (name.indexOf(testName) !== -1) {
return true;
}
}
return false;
}
//getRakeTasks 函数通过执行 rake -AT 命令来获取工作区根目录中的所有 Rake 任务,并将其对应的 vscode.Task 对象返回。
async function getRakeTasks(): Promise<vscode.Task[]> {
const workspaceFolders = vscode.workspace.workspaceFolders;
const result: vscode.Task[] = [];
if (!workspaceFolders || workspaceFolders.length === 0) {
return result;
}
for (const workspaceFolder of workspaceFolders) {
const folderString = workspaceFolder.uri.fsPath;
if (!folderString) {
continue;
}
const rakeFile = path.join(folderString, 'Rakefile');
if (!await exists(rakeFile)) {
continue;
}
const commandLine = 'rake -AT -f Rakefile';
try {
const { stdout, stderr } = await exec(commandLine, { cwd: folderString });
if (stderr && stderr.length > 0) {
getOutputChannel().appendLine(stderr);
getOutputChannel().show(true);
}
if (stdout) {
const lines = stdout.split(/\r{0,1}\n/);
for (const line of lines) {
if (line.length === 0) {
continue;
}
const regExp = /rake\s(.*)#/;
const matches = regExp.exec(line);
if (matches && matches.length === 2) {
const taskName = matches[1].trim();
const kind: RakeTaskDefinition = {
type: 'rake',
task: taskName
};
const task = new vscode.Task(kind, workspaceFolder, taskName, 'rake', new vscode.ShellExecution(`rake ${taskName}`));
result.push(task);
const lowerCaseLine = line.toLowerCase();
if (isBuildTask(lowerCaseLine)) {
task.group = vscode.TaskGroup.Build;
} else if (isTestTask(lowerCaseLine)) {
task.group = vscode.TaskGroup.Test;
}
}
}
}
} catch (err: any) {
const channel = getOutputChannel();
if (err.stderr) {
channel.appendLine(err.stderr);
}
if (err.stdout) {
channel.appendLine(err.stdout);
}
channel.appendLine('Auto detecting rake tasks failed.');
channel.show(true);
}
}
return result;
}
总之,这段代码定义了一个任务提供者,该提供者可以自动检测和提供 Rake 任务,使用户能够在 VS Code 中轻松运行这些任务。同时,这个任务提供者还包含了一些助手函数,用于实现特定功能。