🚀 在 VS Code 中免費取得

工作提供者

使用者通常會在 Visual Studio Code 的 tasks.json 檔案中定義工作。然而,在軟體開發過程中,有些工作可以透過具有工作提供者的 VS Code 擴充功能自動偵測到。當從 VS Code 執行 工作:執行工作 命令時,所有啟用的工作提供者都會貢獻使用者可以執行的工作。雖然 tasks.json 檔案讓使用者可以針對特定資料夾或工作區手動定義工作,但工作提供者可以偵測工作區的詳細資訊,然後自動建立對應的 VS Code 工作。例如,工作提供者可以檢查是否有特定的組建檔案,例如 makeRakefile,並建立組建工作。本主題說明擴充功能如何自動偵測並向終端使用者提供工作。

本指南教您如何建置一個工作提供者,以自動偵測在 Rakefiles 中定義的工作。完整的原始碼位於: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 工作貢獻一個工作定義。工作定義有兩個屬性 taskfiletask 是 Rake 工作的名稱,而 file 指向包含該工作的 `Rakefile`。`task` 屬性是必要的,`file` 屬性是選用的。如果省略 `file` 屬性,則會使用工作區資料夾根目錄中的 `Rakefile`。

When 子句

工作定義可以選擇性地具有 when 屬性。`when` 屬性指定此類型工作可用的條件。`when` 屬性的運作方式與 VS Code 中其他具有 `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` 並等待擴充功能提供其所有工作。最佳實務是設定允許使用者關閉個別工作提供者的設定,這很常見。使用者可能會注意到,從特定提供者取得工作速度較慢,並關閉該提供者。在這種情況下,使用者可能仍在其 `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