タスクプロバイダー
通常、ユーザーは 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
の 2 つの属性があります。task
は Rake タスクの名前で、file
はタスクを含む Rakefile
を指します。task
プロパティは必須で、file
プロパティはオプションです。file
属性が省略された場合、ワークスペースフォルダーのルートにある Rakefile
が使用されます。
When 句
タスク定義には、オプションで when
プロパティを含めることができます。when
プロパティは、このタイプのタスクが利用可能になる条件を指定します。when
プロパティは、VS Code の他の場所 と同じように機能し、when
プロパティが存在します。タスク定義を作成する際には、次のコンテキストを常に考慮する必要があります。
shellExecutionSupported
: VS Code がShellExecution
タスクを実行できる場合は true。たとえば、VS Code がデスクトップアプリケーションとして実行されている場合や、Dev Containers などのリモート拡張機能のいずれかを使用している場合などです。processExecutionSupported
: VS Code がProcessExecution
タスクを実行できる場合は true。たとえば、VS Code がデスクトップアプリケーションとして実行されている場合や、Dev Containers などのリモート拡張機能のいずれかを使用している場合などです。現在、常にshellExecutionSupported
と同じ値になります。customExecutionSupported
: VS Code がCustomExecution
を実行できる場合は true。これは常に true です。
タスクプロバイダー
拡張機能がコード補完をサポートできるようにする言語プロバイダーと同様に、拡張機能はタスクプロバイダーを登録して、利用可能なすべてのタスクを計算できます。これは、次のコードスニペットに示すように、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
と同様に、resolveTask
メソッドは、拡張機能からタスクを取得するために VS Code によって呼び出されます。resolveTask
は provideTasks
の代わりに呼び出すことができ、それを実装するプロバイダーにオプションのパフォーマンス向上を提供することを目的としています。たとえば、ユーザーが拡張機能によって提供されたタスクを実行するキーバインドを持っている場合、VS Code がそのタスクプロバイダーに対して resolveTask
を呼び出し、すべてのタスクを提供するために拡張機能が provideTasks
を呼び出すのを待つ代わりに、1 つのタスクをすばやく取得する方が適切です。ユーザーが個々のタスクプロバイダーをオフにできる設定を用意することが推奨されており、これは一般的です。ユーザーは、特定のプロバイダーからのタスクの取得が遅いことに気付き、プロバイダーをオフにする可能性があります。この場合でも、ユーザーは 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
は、OS に固有のシェルで rake compile
コマンドを実行します (たとえば、Windows の場合は PowerShell で、Ubuntu の場合は bash で実行されます)。タスクが (シェルを起動せずに) プロセスを直接実行する必要がある場合は、vscode.ProcessExecution
を使用できます。ProcessExecution
には、拡張機能がプロセスに渡される引数を完全に制御できるという利点があります。ShellExecution
を使用すると、シェルコマンドの解釈 (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 にあります。