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

透過任務與外部工具整合

現存許多工具可以自動化諸如程式碼檢查(linting)、構建、打包、測試或部署軟體系統等任務。例如 TypeScript 編譯器,像 ESLintTSLint 這樣的 linter,以及像 MakeAntGulpJakeRakeMSBuild 這樣的構建系統。

VS Code can talk to a variety of external tools

這些工具大多從命令列執行,用於自動化軟體開發內部迴圈(編輯、編譯、測試和除錯)內外的工作。鑑於它們在開發生命週期中的重要性,能夠在 VS Code 內部執行這些工具並分析其結果是非常有幫助的。VS Code 中的任務可以配置為執行指令碼和啟動程序,這樣許多現有的工具就可以在 VS Code 內部使用,而無需進入命令列或編寫新程式碼。特定於工作區或資料夾的任務是在工作區的 .vscode 資料夾中的 tasks.json 檔案裡配置的。

擴充套件也可以使用 任務提供程式(Task Provider)來貢獻任務,而這些貢獻的任務可以新增在 tasks.json 檔案中定義的特定於工作區的配置。

注意: 任務支援僅在處理工作區資料夾時可用。在編輯單個檔案時不可用。

TypeScript Hello World

讓我們從一個簡單的“Hello World” TypeScript 程式開始,我們希望將其編譯成 JavaScript。

建立一個空資料夾“mytask”,生成一個 tsconfig.json 檔案,然後從該資料夾啟動 VS Code。

mkdir mytask
cd mytask
tsc --init
code .

現在建立一個 HelloWorld.ts 檔案,內容如下

function sayHello(name: string): void {
  console.log(`Hello ${name}!`);
}

sayHello('Dave');

按下 ⇧⌘B (Windows, Linux Ctrl+Shift+B) 或從全域性終端選單執行執行生成任務,會顯示以下選擇器

TypeScript Build Task

第一個條目執行 TypeScript 編譯器,並將 TypeScript 檔案轉換為 JavaScript 檔案。當編譯器完成後,應該會有一個 HelloWorld.js 檔案。第二個條目以監視模式啟動 TypeScript 編譯器。每次儲存 HelloWorld.ts 檔案都會重新生成 HelloWorld.js 檔案。

你也可以將 TypeScript 構建或監視任務定義為預設的構建任務,這樣在觸發執行生成任務 (⇧⌘B (Windows, Linux Ctrl+Shift+B)) 時就會直接執行。為此,請從全域性終端選單中選擇配置預設生成任務。這會顯示一個包含可用構建任務的選擇器。選擇 tsc: buildtsc: watch,VS Code 將生成一個 tasks.json 檔案。下面顯示的檔案將 tsc: build 任務設為預設構建任務

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "typescript",
      "tsconfig": "tsconfig.json",
      "problemMatcher": ["$tsc"],
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}

上面的 tasks.json 示例沒有定義新任務。它只是將 VS Code 的 TypeScript 擴充套件貢獻的 tsc: build 任務標註為預設構建任務。現在你可以透過按 ⇧⌘B (Windows, Linux Ctrl+Shift+B) 來執行 TypeScript 編譯器。

任務自動檢測

VS Code 目前能自動檢測以下系統的任務:Gulp、Grunt、Jake 和 npm。我們正在與相應的擴充套件作者合作,以增加對 Maven 和 C# dotnet 命令的支援。如果你使用 Node.js 作為執行時開發 JavaScript 應用程式,通常會有一個 package.json 檔案來描述你的依賴項和要執行的指令碼。如果你克隆了 eslint-starter 示例,那麼從全域性選單執行執行任務會顯示以下列表

Tasks ESLint starter

如果你還沒有這樣做,請透過執行 npm install 安裝必要的 npm 模組。現在開啟 server.js 檔案,在一個語句的末尾新增一個分號(注意 ESLint starter 假設語句沒有分號),然後再次執行執行任務。這次選擇 npm: lint 任務。當提示使用哪個問題匹配器時,選擇 ESLint stylish

Tasks ESLint Problem Matcher Selection

執行該任務會產生一個錯誤,顯示在問題檢視中

Tasks ESLint Problem

此外,VS Code 建立了一個 tasks.json 檔案,內容如下

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "npm",
      "script": "lint",
      "problemMatcher": ["$eslint-stylish"]
    }
  ]
}

這指示 VS Code 使用 ESLint stylish 格式掃描 npm lint 指令碼的輸出以查詢問題。

對於 Gulp、Grunt 和 Jake,任務自動檢測的工作方式相同。下面是一個為 vscode-node-debug 擴充套件檢測到的任務示例。

Gulp task auto-detection

提示: 你可以透過快速開啟 (⌘P (Windows, Linux Ctrl+P)) 執行你的任務,方法是輸入 'task'、空格和命令名稱。在本例中,即 'task lint'。

任務自動檢測可以透過以下設定停用

{
  "typescript.tsc.autoDetect": "off",
  "grunt.autoDetect": "off",
  "jake.autoDetect": "off",
  "gulp.autoDetect": "off",
  "npm.autoDetect": "off"
}

自定義任務

並非所有任務或指令碼都能在你的工作區中被自動檢測到。有時需要定義自己的自定義任務。假設你有一個指令碼來執行你的測試,以便正確設定某些環境。該指令碼儲存在工作區內的一個 script 資料夾中,在 Linux 和 macOS 上名為 test.sh,在 Windows 上名為 test.cmd。從全域性終端選單執行配置任務,並選擇從模板建立 tasks.json 檔案條目。這將開啟以下選擇器

Configure Task Runner

注意: 如果你看不到任務執行器模板列表,你可能已經在你的資料夾中有了一個 tasks.json 檔案,並且它的內容會在編輯器中開啟。關閉該檔案,併為本示例刪除或重新命名它。

我們正在努力支援更多的自動檢測,所以這個列表將來會越來越短。因為我們想編寫自己的自定義任務,所以從列表中選擇其他。這將開啟一個帶有任務骨架的 tasks.json 檔案。將內容替換為以下內容

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Run tests",
      "type": "shell",
      "command": "./scripts/test.sh",
      "windows": {
        "command": ".\\scripts\\test.cmd"
      },
      "group": "test",
      "presentation": {
        "reveal": "always",
        "panel": "new"
      }
    }
  ]
}

任務的屬性具有以下語義

  • label:任務的標籤,用於使用者介面。
  • type:任務的型別。對於自定義任務,這可以是 shellprocess。如果指定了 shell,則命令被解釋為 shell 命令(例如:bash、cmd 或 PowerShell)。如果指定了 process,則命令被解釋為要執行的程序。
  • command:要執行的實際命令。
  • windows:任何 Windows 特定的屬性。當命令在 Windows 作業系統上執行時,將使用這些屬性代替預設屬性。
  • group:定義任務屬於哪個組。在示例中,它屬於 test 組。屬於測試組的任務可以透過從命令面板執行執行測試任務來執行。
  • presentation:定義任務輸出在使用者介面中的處理方式。在此示例中,顯示輸出的整合終端 always(總是)顯示,並且每次任務執行時都會建立一個 new(新)終端。
  • options:覆蓋 cwd(當前工作目錄)、env(環境變數)或 shell(預設 shell)的預設值。選項可以按任務設定,也可以全域性或按平臺設定。此處配置的環境變數只能在你的任務指令碼或程序中引用,如果它們是你 args、command 或其他任務屬性的一部分,將不會被解析。
  • runOptions:定義任務何時以及如何執行。
  • hide:在“執行任務”快速選擇框中隱藏任務,這對於複合任務中不能獨立執行的元素很有用。

你可以在你的 tasks.json 檔案中使用 IntelliSense 檢視完整的任務屬性和值。使用觸發建議 (⌃Space (Windows, Linux Ctrl+Space)) 調出建議,並在懸停時或透過閱讀更多... ('i') 彈出視窗閱讀說明。

tasks.json IntelliSense

你也可以檢視 tasks.json 結構

當命令和引數包含空格或其他特殊字元(如 $)時,Shell 命令需要特殊處理。預設情況下,任務系統支援以下行為

  • 如果提供單個命令,任務系統會將該命令原樣傳遞給底層 shell。如果命令需要引號或轉義才能正常工作,則命令需要包含適當的引號或跳脫字元。例如,要列出一個名稱中包含空格的資料夾的目錄,在 bash 中執行的命令應如下所示:ls 'folder with spaces'
{
  "label": "dir",
  "type": "shell",
  "command": "dir 'folder with spaces'"
}
  • 如果提供了命令和引數,當命令或引數包含空格時,任務系統將使用單引號。對於 cmd.exe,則使用雙引號。像下面這樣的 shell 命令將在 PowerShell 中作為 dir 'folder with spaces' 執行。
{
  "label": "dir",
  "type": "shell",
  "command": "dir",
  "args": ["folder with spaces"]
}
  • 如果你想控制引數的引用方式,引數可以是一個指定值和引用樣式的字面量。下面的例子對一個帶空格的引數使用轉義而不是引用。
{
  "label": "dir",
  "type": "shell",
  "command": "dir",
  "args": [
    {
      "value": "folder with spaces",
      "quoting": "escape"
    }
  ]
}

除了轉義,還支援以下值

  • strong:使用 shell 的強引用機制,它會抑制字串內的所有求值。在 PowerShell 以及 Linux 和 macOS 的 shell 下,使用單引號(')。對於 cmd.exe,使用 "
  • weak:使用 shell 的弱引用機制,它仍然會求值字串內的表示式(例如,環境變數)。在 PowerShell 以及 Linux 和 macOS 的 shell 下,使用雙引號(")。cmd.exe 不支援弱引用,所以 VS Code 也使用 "

如果命令本身包含空格,VS Code 預設也會對命令進行強引用。與引數一樣,使用者可以使用相同的字面量樣式來控制命令的引用。

還有更多的任務屬性可以配置你的工作流程。你可以使用 IntelliSense 和 ⌃Space (Windows, Linux Ctrl+Space) 來獲取有效屬性的概覽。

Tasks IntelliSense

除了全域性選單欄,任務命令還可以透過命令面板 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 訪問。你可以篩選 'task' 並看到各種與任務相關的命令。

Tasks in Command Palette

複合任務

你也可以使用 dependsOn 屬性將簡單的任務組合成複合任務。例如,如果你的工作區有一個客戶端和一個伺服器資料夾,並且兩者都包含一個構建指令碼,你可以建立一個任務,在不同的終端中啟動這兩個構建指令碼。如果你在 dependsOn 屬性中列出了多個任務,它們預設會並行執行。

tasks.json 檔案如下所示

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Client Build",
      "command": "gulp",
      "args": ["build"],
      "options": {
        "cwd": "${workspaceFolder}/client"
      }
    },
    {
      "label": "Server Build",
      "command": "gulp",
      "args": ["build"],
      "options": {
        "cwd": "${workspaceFolder}/server"
      }
    },
    {
      "label": "Build",
      "dependsOn": ["Client Build", "Server Build"]
    }
  ]
}

如果你指定 "dependsOrder": "sequence",那麼你的任務依賴項將按照它們在 dependsOn 中列出的順序執行。任何在 dependsOn 中與 "dependsOrder": "sequence" 一起使用的後臺/監視任務都必須有一個問題匹配器來跟蹤它們何時“完成”。以下任務將執行任務二、任務三,然後是任務一。

{
  "label": "One",
  "type": "shell",
  "command": "echo Hello ",
  "dependsOrder": "sequence",
  "dependsOn": ["Two", "Three"]
}

使用者級別任務

你可以使用任務: 開啟使用者任務命令建立不與特定工作區或資料夾繫結的使用者級別任務。這裡只能使用 shellprocess 型別的任務,因為其他任務型別需要工作區資訊。

輸出行為

有時你希望控制執行任務時整合終端面板的行為。例如,你可能希望最大化編輯器空間,只在認為有問題時才檢視任務輸出。終端的行為可以透過任務的 presentation 屬性來控制。它提供以下屬性

  • reveal: 控制是否將整合終端面板帶到前臺。有效值為
    • always - 面板總是被帶到前臺。這是預設值。
    • never - 使用者必須使用檢視 > 終端命令 (⌃` (Windows, Linux Ctrl+`)) 顯式地將終端面板帶到前臺。
    • silent - 僅當輸出未被掃描以查詢錯誤和警告時,終端面板才被帶到前臺。
  • revealProblems:控制在執行此任務時是否顯示“問題”面板。優先於選項 reveal。預設為 never
    • always - 執行此任務時總是顯示“問題”面板。
    • onProblem - 僅在發現問題時才顯示“問題”面板。
    • never - 執行此任務時從不顯示“問題”面板。
  • focus:控制終端是否獲得輸入焦點。預設為 false
  • echo:控制執行的命令是否在終端中回顯。預設為 true
  • showReuseMessage:控制是否顯示“終端將被任務重用,按任意鍵關閉”的訊息。
  • panel:控制終端例項是否在任務執行之間共享。可能的值有
    • shared - 終端是共享的,其他任務執行的輸出會新增到同一個終端。
    • dedicated - 終端專用於特定任務。如果該任務再次執行,終端將被重用。但是,不同任務的輸出會呈現在不同的終端中。
    • new - 該任務的每次執行都使用一個新的乾淨終端。
  • clear:控制在執行此任務之前是否清除終端。預設為 false
  • close:控制任務執行所在的終端在任務退出時是否關閉。預設為 false
  • group:控制任務是否在特定的終端組中使用分割窗格執行。同一組中的任務(由字串值指定)將使用分割終端來呈現,而不是一個新的終端面板。

你也可以為自動檢測到的任務修改終端面板行為。例如,如果你想更改上面 ESLint 示例中 npm: run lint 的輸出行為,可以向其新增 presentation 屬性

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "npm",
      "script": "lint",
      "problemMatcher": ["$eslint-stylish"],
      "presentation": {
        "reveal": "never"
      }
    }
  ]
}

你也可以將自定義任務與檢測到的任務配置混合使用。一個配置了 npm: run lint 任務並添加了一個自定義 Run Test 任務的 tasks.json 檔案如下所示

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "npm",
      "script": "lint",
      "problemMatcher": ["$eslint-stylish"],
      "presentation": {
        "reveal": "never"
      }
    },
    {
      "label": "Run tests",
      "type": "shell",
      "command": "./scripts/test.sh",
      "windows": {
        "command": ".\\scripts\\test.cmd"
      },
      "group": "test",
      "presentation": {
        "reveal": "always",
        "panel": "new"
      }
    }
  ]
}

執行行為

你可以使用 runOptions 屬性指定任務的執行行為

  • reevaluateOnRerun:控制透過重新執行上一個任務命令執行任務時如何評估變數。預設為 true,意味著當任務重新執行時將重新評估變數。當設定為 false 時,將使用任務上一次執行解析的變數值。
  • runOn:指定任務何時執行。
    • default - 任務僅在透過執行任務命令執行時執行。
    • folderOpen - 當包含該任務的資料夾被開啟時,該任務將被執行。首次開啟包含帶有 folderOpen 任務的資料夾時,系統會詢問你是否允許在該資料夾中自動執行任務。你可以稍後使用管理自動任務命令並在允許自動任務禁止自動任務之間進行選擇來更改你的決定。
  • instanceLimit - 允許同時執行的任務例項數量。預設值為 1

自定義自動檢測的任務

如上所述,你可以在 tasks.json 檔案中自定義自動檢測的任務。通常這樣做是為了修改表示屬性或附加一個問題匹配器來掃描任務的輸出以查詢錯誤和警告。你可以直接從執行任務列表中自定義任務,方法是按右側的齒輪圖示,將相應的任務引用插入到 tasks.json 檔案中。假設你有以下 Gulp 檔案,使用 ESLint 來檢查 JavaScript 檔案(該檔案取自 https://github.com/adametry/gulp-eslint

const gulp = require('gulp');
const eslint = require('gulp-eslint');

gulp.task('lint', () => {
  // ESLint ignores files with "node_modules" paths.
  // So, it's best to have gulp ignore the directory as well.
  // Also, Be sure to return the stream from the task;
  // Otherwise, the task may end before the stream has finished.
  return (
    gulp
      .src(['**/*.js', '!node_modules/**'])
      // eslint() attaches the lint output to the "eslint" property
      // of the file object so it can be used by other modules.
      .pipe(eslint())
      // eslint.format() outputs the lint results to the console.
      // Alternatively use eslint.formatEach() (see Docs).
      .pipe(eslint.format())
      // To have the process exit with an error code (1) on
      // lint error, return the stream and pipe to failAfterError last.
      .pipe(eslint.failAfterError())
  );
});

gulp.task('default', ['lint'], function() {
  // This will only run if the lint task is successful...
});

從全域性終端選單執行執行任務將顯示以下選擇器

Configure Task

按下齒輪圖示。這將建立以下 tasks.json 檔案

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "gulp",
      "task": "default",
      "problemMatcher": []
    }
  ]
}

通常你現在會新增一個問題匹配器(在這種情況下是 $eslint-stylish)或修改表示設定。

使用問題匹配器處理任務輸出

VS Code 可以使用問題匹配器處理任務的輸出。問題匹配器會掃描任務輸出文字中已知的警告或錯誤字串,並在編輯器內和問題面板中內聯報告這些問題。VS Code 內建了幾個問題匹配器

  • TypeScript: $tsc 假設輸出中的檔名是相對於開啟的資料夾的。
  • TypeScript Watch: $tsc-watch 匹配 tsc 編譯器在監視模式下執行時報告的問題。
  • JSHint: $jshint 假設檔名以絕對路徑報告。
  • JSHint Stylish: $jshint-stylish 假設檔名以絕對路徑報告。
  • ESLint Compact: $eslint-compact 假設輸出中的檔名是相對於開啟的資料夾的。
  • ESLint Stylish: $eslint-stylish 假設輸出中的檔名是相對於開啟的資料夾的。
  • Go: $go 匹配 go 編譯器報告的問題。假設檔名是相對於開啟的資料夾的。
  • CSharp and VB Compiler: $mscompile 假設檔名以絕對路徑報告。
  • Lessc compiler: $lessc 假設檔名以絕對路徑報告。
  • Node Sass compiler: $node-sass 假設檔名以絕對路徑報告。

你也可以建立自己的問題匹配器,我們將在後面的部分討論。

將鍵盤快捷鍵繫結到任務

如果你需要頻繁執行某個任務,可以為該任務定義一個鍵盤快捷鍵。

例如,要將 Ctrl+H 繫結到上面的執行測試任務,請將以下內容新增到你的 keybindings.json 檔案中

{
  "key": "ctrl+h",
  "command": "workbench.action.tasks.runTask",
  "args": "Run tests"
}

變數替換

在編寫任務配置時,擁有一組預定義的常用變數會很有用,例如活動檔案 (${file}) 或工作區根資料夾 (${workspaceFolder})。VS Code 支援在 tasks.json 檔案的字串內部進行變數替換,你可以在變數參考中看到預定義變數的完整列表。

注意: 並非所有屬性都接受變數替換。具體來說,只有 commandargsoptions 支援變數替換。

下面是一個自定義任務配置的示例,它將當前開啟的檔案傳遞給 TypeScript 編譯器。

{
  "label": "TypeScript compile",
  "type": "shell",
  "command": "tsc ${file}",
  "problemMatcher": ["$tsc"]
}

同樣,你可以透過在名稱前加上 ${config: 來引用你的專案配置設定。例如,${config:python.formatting.autopep8Path} 返回 Python 擴充套件設定 formatting.autopep8Path

下面是一個自定義任務配置的示例,它使用 python.formatting.autopep8Path 設定定義的可執行檔案在當前檔案上執行 autopep8

{
  "label": "autopep8 current file",
  "type": "process",
  "command": "${config:python.formatting.autopep8Path}",
  "args": ["--in-place", "${file}"]
}

如果你想為 tasks.jsonlaunch.json 指定 Python 擴充套件使用的所選 Python 直譯器,可以使用 ${command:python.interpreterPath} 命令。

如果簡單的變數替換還不夠,你還可以透過在 tasks.json 檔案中新增一個 inputs 部分來從任務使用者那裡獲取輸入。

Inputs Example

有關 inputs 的更多資訊,請參閱變數參考

特定於作業系統的屬性

任務系統支援定義特定於某個作業系統的值(例如,要執行的命令)。為此,請在 tasks.json 檔案中放入一個特定於作業系統的字面量,並在該字面量內指定相應的屬性。

下面是一個示例,它使用 Node.js 可執行檔案作為命令,並且在 Windows 和 Linux 上的處理方式不同

{
  "label": "Run Node",
  "type": "process",
  "windows": {
    "command": "C:\\Program Files\\nodejs\\node.exe"
  },
  "linux": {
    "command": "/usr/bin/node"
  }
}

有效的作業系統屬性是 windows (Windows)、linux (Linux) 和 osx (macOS)。在特定於作業系統的作用域中定義的屬性會覆蓋在任務或全域性作用域中定義的屬性。

全域性任務

任務屬性也可以在全域性作用域中定義。如果存在,它們將用於特定的任務,除非這些任務定義了具有不同值的相同屬性。在下面的示例中,有一個全域性 presentation 屬性,它定義了所有任務都應在新面板中執行

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "presentation": {
    "panel": "new"
  },
  "tasks": [
    {
      "label": "TS - Compile current file",
      "type": "shell",
      "command": "tsc ${file}",
      "problemMatcher": ["$tsc"]
    }
  ]
}

提示: 要訪問全域性作用域的 tasks.json 檔案,請開啟命令面板 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 並執行任務: 開啟使用者任務命令。

PowerShell 中的字元轉義

當預設 shell 是 PowerShell 時,或者當任務被配置為使用 PowerShell 時,你可能會看到意外的空格和引號轉義。意外的轉義只發生在 cmdlet 上,因為 VS Code 不知道你的命令是否包含 cmdlet。下面的示例 1 顯示了一個你會得到不適用於 PowerShell 的轉義的情況。示例 2 顯示了獲得良好轉義的最佳、跨平臺方式。在某些情況下,你可能無法遵循示例 2,需要像示例 3 中那樣手動進行轉義。

"tasks": [
    {
        "label": "PowerShell example 1 (unexpected escaping)",
        "type": "shell",
        "command": "Get-ChildItem \"Folder With Spaces\""
    },
    {
        "label": "PowerShell example 2 (expected escaping)",
        "type": "shell",
        "command": "Get-ChildItem",
        "args": ["Folder With Spaces"]
    },
    {
        "label": "PowerShell example 3 (manual escaping)",
        "type": "shell",
        "command": "& Get-ChildItem \\\"Folder With Spaces\\\""
    }
]

更改任務輸出的編碼

任務經常與磁碟上的檔案互動。如果這些檔案在磁碟上儲存的編碼與系統編碼不同,你需要讓作為任務執行的命令知道要使用哪種編碼。由於這取決於作業系統和使用的 shell,因此沒有通用的解決方案來控制它。以下是關於如何使其工作的建議和示例。

如果你需要調整編碼,你應該檢查更改作業系統使用的預設編碼是否有意義,或者至少透過調整 shell 的配置檔案來為你使用的 shell 更改它。

如果你只需要為特定任務調整它,那麼將更改編碼所需的特定於作業系統的命令新增到任務命令列中。以下示例適用於 Windows,其預設內碼表為 437。該任務顯示一個包含西里爾字元的檔案的輸出,因此需要內碼表 866。假設預設 shell 設定為 cmd.exe,列出該檔案的任務如下所示

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "more",
      "type": "shell",
      "command": "chcp 866 && more russian.txt",
      "problemMatcher": []
    }
  ]
}

如果任務在 PowerShell 中執行,命令需要寫成這樣 chcp 866; more russian.txt。在 Linux 和 macOS 上,可以使用 locale 命令來檢查區域設定並調整必要的環境變數。

任務實踐示例

為了突出任務的強大功能,這裡有幾個 VS Code 如何使用任務來整合外部工具(如 linter 和編譯器)的示例。

將 TypeScript 轉譯為 JavaScript

TypeScript 主題包含一個示例,該示例建立一個任務來將 TypeScript 轉譯為 JavaScript,並從 VS Code 內部觀察任何相關的錯誤。

將 Less 和 SCSS 轉譯為 CSS

CSS 主題提供瞭如何使用任務生成 CSS 檔案的示例。

  1. 使用構建任務手動轉譯
  2. 使用檔案監視器自動化編譯步驟

定義一個問題匹配器

VS Code 內建了一些最常見的問題匹配器。然而,市面上有很多編譯器和程式碼檢查工具,它們都會產生自己風格的錯誤和警告,所以你可能想建立自己的問題匹配器。

我們有一個 helloWorld.c 程式,其中開發者將 printf 誤輸為 prinft。用 gcc 編譯它會產生以下警告

helloWorld.c:5:3: warning: implicit declaration of function ‘prinft’

我們想建立一個問題匹配器,它可以捕獲輸出中的訊息,並在 VS Code 中顯示相應的問題。問題匹配器在很大程度上依賴於正則表示式。下面的部分假設你熟悉正則表示式。

提示: 我們發現 RegEx101 playground 是一個開發和測試正則表示式的好地方,它支援 ECMAScript (JavaScript) 風格。

一個能捕獲上述警告(和錯誤)的匹配器如下所示

{
  // The problem is owned by the cpp language service.
  "owner": "cpp",
  // The file name for reported problems is relative to the opened folder.
  "fileLocation": ["relative", "${workspaceFolder}"],
  // The name that will be shown as the source of the problem.
  "source": "gcc",
  // The actual pattern to match problems in the output.
  "pattern": {
    // The regular expression. Example to match: helloWorld.c:5:3: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
    // The first match group matches the file name which is relative.
    "file": 1,
    // The second match group matches the line on which the problem occurred.
    "line": 2,
    // The third match group matches the column at which the problem occurred.
    "column": 3,
    // The fourth match group matches the problem's severity. Can be ignored. Then all problems are captured as errors.
    "severity": 4,
    // The fifth match group matches the message.
    "message": 5
  }
}

請注意,file、line 和 message 屬性是強制性的。fileLocation 指定任務輸出中產生並由問題匹配器匹配的檔案路徑是 absolute (絕對) 還是 relative (相對)。如果任務同時產生絕對路徑和相對路徑,你可以使用 autoDetect 檔案位置。使用 autoDetect 時,路徑首先作為絕對路徑進行測試,如果檔案不存在,則假定該路徑是相對的。

如果模式不包含嚴重性,severity 會指定使用哪個問題嚴重性。severity 的可能值為 errorwarninginfo

這是一個完整的 tasks.json 檔案,其中包含上面的程式碼(註釋已移除),幷包裝了實際的任務細節

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "command": "gcc",
      "args": ["-Wall", "helloWorld.c", "-o", "helloWorld"],
      "problemMatcher": {
        "owner": "cpp",
        "fileLocation": ["relative", "${workspaceFolder}"],
        "source": "gcc",
        "pattern": {
          "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
          "file": 1,
          "line": 2,
          "column": 3,
          "severity": 4,
          "message": 5
        }
      }
    }
  ]
}

在 VS Code 內部執行它,並按 ⇧⌘M (Windows, Linux Ctrl+Shift+M) 來獲取問題列表,你會得到以下輸出

GCC Problem Matcher

注意: C/C++ 擴充套件包含了針對 GCC 的問題匹配器,因此我們無需定義自己的匹配器。

在模式中還可以使用一些其他屬性。它們是

  • location - 如果問題位置是行或行,列或起始行,起始列,結束行,結束列,那麼可以使用我們的通用位置匹配組。
  • endLine - 問題結束行的匹配組索引。如果編譯器未提供結束行值,可以省略。
  • endColumn - 問題結束列的匹配組索引。如果編譯器未提供結束列值,可以省略。
  • code - 問題程式碼的匹配組索引。如果編譯器未提供程式碼值,可以省略。

你還可以定義一個只捕獲檔案的問題匹配器。為此,定義一個 pattern,並將其可選的 kind 屬性設定為 file。在這種情況下,無需提供 linelocation 屬性。

注意: 如果 kind 屬性設定為 file,一個有效的模式必須至少為 filemessage 提供一個匹配組。如果沒有提供 kind 屬性,或者 kind 屬性設定為 location,一個有效的模式還必須提供一個 linelocation 屬性。

注意: 問題匹配器只解析給定命令的輸出。如果你想解析寫入到單獨檔案(例如日誌檔案)的輸出,請讓你執行的命令在執行結束前打印出該單獨檔案的內容。

定義一個多行問題匹配器

一些工具會將原始檔中的問題分佈在多行中,尤其是在使用 stylish 報告器時。一個例子是 ESLint;在 stylish 模式下,它會產生如下輸出

test.js
  1:0   error  Missing "use strict" statement                 strict
 1 problems (1 errors, 0 warnings)

我們的問題匹配器是基於行的,所以我們需要用一個正則表示式捕獲檔名(test.js),再用另一個正則表示式捕獲實際的問題位置和訊息(1:0 error Missing "use strict" statement)。

要做到這一點,請為 pattern 屬性使用一個問題模式陣列。這樣,你就可以為要匹配的每一行定義一個模式。

以下問題模式匹配 ESLint 在 stylish 模式下的輸出——但還有一個小問題需要我們接下來解決。下面的程式碼有一個用於捕獲檔名的第一個正則表示式,和第二個用於捕獲行、列、嚴重性、訊息和錯誤程式碼的正則表示式

{
  "owner": "javascript",
  "fileLocation": ["relative", "${workspaceFolder}"],
  "pattern": [
    {
      "regexp": "^([^\\s].*)$",
      "file": 1
    },
    {
      "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
      "line": 1,
      "column": 2,
      "severity": 3,
      "message": 4,
      "code": 5
    }
  ]
}

然而,如果一個資源上有多個問題,這個模式將無法工作。例如,想象一下 ESLint 的以下輸出

test.js
  1:0   error  Missing "use strict" statement                 strict
  1:9   error  foo is defined but never used                  no-unused-vars
  2:5   error  x is defined but never used                    no-unused-vars
  2:11  error  Missing semicolon                              semi
  3:1   error  "bar" is not defined                           no-undef
  4:1   error  Newline required at end of file but not found  eol-last
 6 problems (6 errors, 0 warnings)

該模式的第一個正則表示式將匹配 "test.js",第二個將匹配 "1:0 error ...". 下一行 "1:9 error ..." 會被處理,但不會被第一個正則表示式匹配,因此沒有問題被捕獲。

為了讓它正常工作,多行模式的最後一個正則表示式可以指定 loop 屬性。如果設定為 true,它會指示任務系統將多行匹配器的最後一個模式應用於輸出中的行,只要正則表示式匹配即可。

由第一個模式捕獲的資訊(在本例中匹配 `test.js`)將與每個匹配 `loop` 模式的後續行相結合,以建立多個問題。在這個例子中,將建立六個問題。

這是一個能夠完全捕獲 ESLint stylish 問題的匹配器

{
  "owner": "javascript",
  "fileLocation": ["relative", "${workspaceFolder}"],
  "pattern": [
    {
      "regexp": "^([^\\s].*)$",
      "file": 1
    },
    {
      "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
      "line": 1,
      "column": 2,
      "severity": 3,
      "message": 4,
      "code": 5,
      "loop": true
    }
  ]
}

注意:如果多個問題發生在同一資源的完全相同的行和列上,那麼只會顯示一個問題。這適用於所有問題匹配器,而不僅僅是多行問題匹配器。

修改現有的問題匹配器

如果現有的問題匹配器接近你的需求,你可以在你的 tasks.json 任務中修改它。例如,$tsc-watch 問題匹配器只適用於已關閉的文件。如果你想讓它適用於所有文件,你可以這樣修改它

{
  "type": "npm",
  "script": "watch",
  "problemMatcher": {
    "base": "$tsc-watch",
    "applyTo": "allDocuments"
  },
  "isBackground": true
}

其他可修改的問題匹配器屬性包括 backgroundfileLocationownerpatternseveritysource

後臺 / 監視任務

一些工具支援在後臺執行,同時監視檔案系統的變化,然後在磁碟上的檔案發生變化時觸發一個動作。使用 Gulp,這種功能透過 npm 模組 gulp-watch 提供。TypeScript 編譯器 tsc 透過 --watch 命令列選項內建了對此的支援。

為了提供反饋,表明後臺任務在 VS Code 中是活動的並正在產生問題結果,問題匹配器必須使用額外的資訊來檢測輸出中的這些“狀態”變化。讓我們以 tsc 編譯器為例。當編譯器以監視模式啟動時,它會向控制檯列印以下附加資訊

> tsc --watch
12:30:36 PM - Compilation complete. Watching for file changes.

當磁碟上包含問題的檔案發生變化時,會出現以下輸出

12:32:35 PM - File change detected. Starting incremental compilation...
src/messages.ts(276,9): error TS2304: Cannot find name 'candidate'.
12:32:35 PM - Compilation complete. Watching for file changes.

檢視輸出顯示以下模式

  • 當控制檯打印出 File change detected. Starting incremental compilation... 時,編譯器開始執行。
  • 當控制檯打印出 Compilation complete. Watching for file changes. 時,編譯器停止。
  • 在這兩個字串之間報告了問題。
  • 編譯器在初始啟動時也會執行一次(不向控制檯列印 File change detected. Starting incremental compilation...)。

為了捕獲這些資訊,問題匹配器可以提供一個 background 屬性。

對於 tsc 編譯器,一個合適的 background 屬性看起來像這樣

"background": {
    "activeOnStart": true,
    "beginsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - File change detected\\. Starting incremental compilation\\.\\.\\.",
    "endsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - Compilation complete\\. Watching for file changes\\."
}

除了問題匹配器上的 background 屬性,任務本身必須標記為 isBackground,以便任務在後臺持續執行。

一個完整的為在監視模式下執行的 tsc 任務手工編寫的 tasks.json 檔案如下所示

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "watch",
      "command": "tsc",
      "args": ["--watch"],
      "isBackground": true,
      "problemMatcher": {
        "owner": "typescript",
        "fileLocation": "relative",
        "pattern": {
          "regexp": "^([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$",
          "file": 1,
          "location": 2,
          "severity": 3,
          "code": 4,
          "message": 5
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - File change detected\\. Starting incremental compilation\\.\\.\\.",
          "endsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - Compilation complete\\. Watching for file changes\\."
        }
      }
    }
  ]
}

後續步驟

以上就是任務的內容 - 讓我們繼續...

  • tasks.json 結構 - 你可以檢視完整的 tasks.json 結構及其描述。
  • 基本編輯 - 瞭解功能強大的 VS Code 編輯器。
  • 程式碼導航 - 快速瀏覽您的原始碼。
  • 語言支援 - 瞭解我們支援的程式語言,包括 VS Code 自帶的和透過社群擴充套件提供的。
  • 除錯 - 直接在 VS Code 編輯器中除錯你的原始碼。

常見問題

任務可以使用的 shell 和為整合終端指定的 shell 不同嗎?

可以。你可以使用 "terminal.integrated.automationProfile.*" 設定來設定將用於 VS Code 中所有自動化的 shell,這包括任務。

    "terminal.integrated.automationProfile.windows": {
        "path": "cmd.exe"
    }

另外,你可以使用 options.shell 屬性覆蓋任務的 shell。你可以按任務、全域性或按平臺設定此項。例如,要在 Windows 上使用 cmd.exe,你的 tasks.json 將包含

{
    "version": "2.0.0",
    "windows": {
        "options": {
            "shell": {
                "executable": "cmd.exe",
                "args": [
                    "/d", "/c"
                ]
            }
        }
    },
    ...

後臺任務可以用作 launch.json 中的 prelaunchTask 嗎?

可以。由於後臺任務會一直執行直到被終止,一個後臺任務本身沒有訊號表明它已經“完成”。要將後臺任務用作 prelaunchTask,你必須向該後臺任務新增一個合適的後臺 problemMatcher,以便任務系統和除錯系統有辦法知道該任務“已完成”。

你的任務可以是

{
  "type": "npm",
  "script": "watch",
  "problemMatcher": "$tsc-watch",
  "isBackground": true
}

注意: $tsc-watch 是一個後臺問題匹配器,這對於後臺任務是必需的。

然後你可以在你的 launch.json 檔案中將該任務用作 prelaunchTask

{
  "name": "Launch Extension",
  "type": "extensionHost",
  "request": "launch",
  "runtimeExecutable": "${execPath}",
  "args": ["--extensionDevelopmentPath=${workspaceRoot}"],
  "stopOnEntry": false,
  "sourceMaps": true,
  "outFiles": ["${workspaceRoot}/out/src/**/*.js"],
  "preLaunchTask": "npm: watch"
}

有關後臺任務的更多資訊,請參閱後臺 / 監視任務

為什麼我在執行任務時會收到“command not found”的錯誤?

當您嘗試執行的任務命令未被您的終端識別為可執行的命令時,會出現“command not found”的訊息。這通常是因為該命令是作為您 shell 啟動指令碼的一部分配置的。任務以非登入和非互動方式執行,這意味著您的 shell 的啟動指令碼不會被執行。特別是 nvm 就已知會使用啟動指令碼作為其配置的一部分。

有幾種方法可以解決這個問題

  1. 確保您的命令在您的路徑(path)上,並且不需要啟動指令碼來新增到您的路徑中。這是解決該問題的最徹底的方法,也是推薦的解決方案。
  2. 您可以為您的任務進行一次性修復,使其以登入或互動方式執行。不推薦這樣做,因為它可能會有其他後果。但是,對於單個任務來說,這也可以是一個快速簡便的修復方法。下面是一個使用 bash 作為 shell 執行此操作的任務示例
{
  "type": "npm",
  "script": "watch",
  "options": {
    "shell": {
      "args": ["-c", "-l"]
    }
  }
}

上面的 npm 任務將帶有一個命令(-c)來執行 bash,就像任務系統預設做的那樣。然而,這個任務也以登入 shell(-l)的方式執行 bash