🚀 在 VS Code 中取得

工作提供者

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

本指南教您如何建置一個工作提供者,以自動偵測在 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 工作貢獻了一個工作定義。工作定義有兩個屬性 taskfiletask 是 Rake 工作的名稱,而 file 指向包含該工作的 Rakefiletask 屬性是必要的,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 並等待擴充功能提供其所有工作。最好有一個設定,允許使用者關閉個別的工作提供者,這很常見。使用者可能會注意到來自特定提供者的工作速度較慢,並關閉該提供者。在這種情況下,使用者可能仍然在其 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.ProcessExecutionProcessExecution 的優點是擴充功能可以完全控制傳遞給程序的引數。使用 ShellExecution 可以使用 Shell 命令解釋(例如 bash 下的萬用字元展開)。如果 ShellExecution 是使用單行命令列建立的,則擴充功能需要確保命令內部的正確引號和逸出(例如處理空白字元)。

CustomExecution

一般而言,最好使用 ShellExecutionProcessExecution,因為它們很簡單。但是,如果您的工作需要在執行之間儲存大量狀態,作為單獨的腳本或程序無法良好運作,或者需要廣泛處理輸出,則 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