任务提供者
用户通常在 Visual Studio Code 的 tasks.json
文件中定义 任务。但是,在软件开发过程中,某些任务可以由具有任务提供者的 VS Code 扩展自动检测。当从 VS Code 运行 **任务:运行任务** 命令时,所有活动的任务提供者都会贡献用户可以运行的任务。虽然 tasks.json
文件允许用户为特定文件夹或工作区手动定义任务,但任务提供者可以检测有关工作区的信息,然后自动创建相应的 VS Code 任务。例如,任务提供者可以检查是否存在特定的构建文件,例如 make
或 Rakefile
,并创建一个构建任务。本主题介绍扩展如何自动检测并向最终用户提供任务。
本指南教你如何构建一个任务提供者,该提供者会自动检测在 Rakefile 中定义的任务。完整的源代码位于:https://github.com/microsoft/vscode-extension-samples/tree/main/task-provider-sample。
任务定义
为了在系统中唯一标识任务,贡献任务的扩展需要定义标识任务的属性。在 Rake 示例中,任务定义如下所示
"taskDefinitions": [
{
"type": "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."
}
}
}
]
这为 rake
任务贡献了一个任务定义。任务定义具有两个属性 task
和 file
。task
是 Rake 任务的名称,file
指向包含该任务的 Rakefile
。task
属性是必需的,file
属性是可选的。如果省略 file
属性,则使用工作区文件夹根目录中的 Rakefile
。
When 子句
任务定义可以选择具有 when
属性。when
属性指定此类任务可用的条件。when
属性的工作方式与 VS Code 中其他位置 具有 when
属性的位置相同。在创建任务定义时,应始终考虑以下上下文
shellExecutionSupported
:当 VS Code 可以运行ShellExecution
任务时为真,例如当 VS Code 作为桌面应用程序运行或使用其中一个远程扩展(例如 Dev Containers)时。processExecutionSupported
:当 VS Code 可以运行ProcessExecution
任务时为真,例如当 VS Code 作为桌面应用程序运行或使用其中一个远程扩展(例如 Dev Containers)时。目前,它始终具有与shellExecutionSupported
相同的值。customExecutionSupported
:当 VS Code 可以运行CustomExecution
时为真。这始终为真。
任务提供者
类似于允许扩展支持代码补全的语言提供者,扩展可以注册任务提供者来计算所有可用的任务。这使用 vscode.tasks
命名空间完成,如下面的代码片段所示
import * as vscode from 'vscode';
let rakePromise: Thenable<vscode.Task[]> | undefined = undefined;
const taskProvider = vscode.tasks.registerTaskProvider('rake', {
provideTasks: () => {
if (!rakePromise) {
rakePromise = getRakeTasks();
}
return rakePromise;
},
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;
}
});
与 provideTasks
一样,VS Code 会调用 resolveTask
方法来从扩展获取任务。resolveTask
可以代替 provideTasks
调用,旨在为实现它的提供者提供可选的性能提升。例如,如果用户有一个键绑定运行扩展提供的任务,那么 VS Code 调用 resolveTask
用于该任务提供者并快速获取一个任务会更好,而不是必须调用 provideTasks
并等待扩展提供所有任务。让用户可以关闭单个任务提供者的设置是一种良好的做法,因此这很常见。用户可能会注意到,来自特定提供者的任务获取速度较慢,并关闭该提供者。在这种情况下,用户仍可能在其 tasks.json
中引用来自该提供者的某些任务。如果未实现 resolveTask
,那么会出现一个警告,指出其 tasks.json
中的任务未创建。使用 resolveTask
,扩展仍然可以为 tasks.json
中定义的任务提供任务。
getRakeTasks
实现执行以下操作
- 使用
rake -AT -f Rakefile
命令列出每个工作区文件夹中Rakefile
中定义的所有 rake 任务。 - 解析 stdio 输出。
- 对于每个列出的任务,创建一个
vscode.Task
实现。
由于 Rake 任务实例化需要 package.json
文件中定义的任务定义,因此 VS Code 还使用以下 TypeScript 接口定义结构
interface RakeTaskDefinition extends vscode.TaskDefinition {
/**
* The task name
*/
task: string;
/**
* The rake file containing the task
*/
file?: string;
}
假设输出来自第一个工作区文件夹中的名为 compile
的任务,则相应的任务创建如下所示
let task = new vscode.Task(
{ type: 'rake', task: 'compile' },
vscode.workspace.workspaceFolders[0],
'compile',
'rake',
new vscode.ShellExecution('rake compile')
);
对于输出中列出的每个任务,使用上述模式创建一个相应的 VS Code 任务,然后返回 getRakeTasks
调用中所有任务的数组。
ShellExecution
在特定于操作系统的 shell 中执行 rake compile
命令(例如,在 Windows 下,命令将在 PowerShell 中执行,在 Ubuntu 下将在 bash 中执行)。如果任务应直接执行进程(不生成 shell),则可以使用 vscode.ProcessExecution
。ProcessExecution
的优点是扩展可以完全控制传递给进程的参数。使用 ShellExecution
利用 shell 命令解释(例如,在 bash 下进行通配符扩展)。如果使用单个命令行创建 ShellExecution
,那么扩展需要确保命令内部的正确引用和转义(例如,处理空格)。
CustomExecution
通常,最好使用 ShellExecution
或 ProcessExecution
,因为它们很简单。但是,如果您的任务需要在运行之间保存大量状态,不能很好地用作单独的脚本或进程,或者需要对输出进行广泛的处理,那么 CustomExecution
可能很合适。CustomExecution
的现有用途通常用于复杂的构建系统。CustomExecution
只有一个在运行任务时执行的回调。这允许任务执行更多灵活的操作,但也意味着任务提供者负责需要发生的任何进程管理和输出解析。任务提供者还负责实现 Pseudoterminal
并将其从 CustomExecution
回调中返回。
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)
);
}
)
);
完整的示例,包括 Pseudoterminal
的实现,位于 https://github.com/microsoft/vscode-extension-samples/tree/main/task-provider-sample/src/customTaskProvider.ts。