customTaskProvider、rakeTaskProvider使用介绍

一、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 中轻松运行这些任务。同时,这个任务提供者还包含了一些助手函数,用于实现特定功能。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值