參加你附近的 ,瞭解 VS Code 中的 AI 輔助開發。

任務提供程式

使用者通常在 Visual Studio Code 的 tasks.json 檔案中定義任務。然而,在軟體開發過程中,有些任務可以由 VS Code 擴充套件透過任務提供程式自動檢測到。當從 VS Code 執行 Tasks: Run Task 命令時,所有活動任務提供程式都會貢獻使用者可以執行的任務。雖然 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 指向包含該任務的 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 一樣,VS Code 呼叫 resolveTask 方法來從擴充套件中獲取任務。可以呼叫 resolveTask 而不是 provideTasks,其目的是為實現它的提供程式提供可選的效能提升。例如,如果使用者有一個執行擴充套件提供的任務的鍵繫結,那麼 VS Code 呼叫該任務提供程式的 resolveTask 並快速獲取一個任務會更好,而不是必須呼叫 provideTasks 並等待擴充套件提供其所有任務。最好有一個設定允許使用者關閉單個任務提供程式,所以這很常見。使用者可能會注意到來自特定提供程式的任務獲取速度較慢並關閉該提供程式。在這種情況下,使用者可能仍然在他們的 tasks.json 中引用來自此提供程式的一些任務。如果未實現 resolveTask,則會發出警告,指出其 tasks.json 中定義的任務未建立。透過 resolveTask,擴充套件仍然可以為 tasks.json 中定義的任務提供任務。

getRakeTasks 的實現執行以下操作

  • 使用 rake -AT -f Rakefile 命令列出每個工作區資料夾中 Rakefile 中定義的所有 rake 任務。
  • 解析標準輸出。
  • 對於每個列出的任務,建立一個 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 是使用單個命令列建立的,則擴充套件需要確保命令中的正確引用和轉義(例如處理空格)。

自定義執行

一般來說,最好使用 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