通过任务与外部工具集成

有许多工具可以自动化任务,如代码检查、构建、打包、测试或部署软件系统。例如,TypeScript Compiler,代码检查工具如ESLintTSLint,以及构建系统如MakeAntGulpJakeRakeMSBuild

VS Code 可以与多种外部工具通信

这些工具大多从命令行运行,并自动化内部和外部软件开发循环(编辑、编译、测试和调试)中的工作。鉴于它们在开发生命周期中的重要性,能够在VS Code中运行工具并分析其结果是非常有帮助的。VS Code中的任务可以配置为运行脚本和启动进程,因此许多现有工具可以在VS Code中使用,而无需进入命令行或编写新代码。工作区或文件夹特定的任务通过工作区的.vscode文件夹中的tasks.json文件进行配置。

扩展还可以使用任务提供者贡献任务,这些贡献的任务可以添加在tasks.json文件中定义的工作区特定配置。

注意:任务支持仅在处理工作区文件夹时可用。在编辑单个文件时不可用。

TypeScript 你好世界

让我们从一个简单的“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) 或从全局 Terminal 菜单中运行 Run Build Task 将显示以下选择器:

TypeScript 构建任务

第一个条目执行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 示例,那么从全局菜单执行 运行任务 将显示以下列表:

ESLint 启动任务

如果尚未安装,请通过运行npm install来安装必要的npm模块。现在打开server.js文件并在语句末尾添加分号(注意ESLint启动器假设语句没有分号),然后再次执行运行任务。这次选择npm: lint任务。当提示选择要使用的问题匹配器时,选择ESLint stylish

ESLint问题匹配器选择任务

执行任务时产生了一个错误,显示在问题视图中:

ESLint问题任务

此外,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任务自动检测

提示: 您可以通过快速打开 (⌘P (Windows, Linux Ctrl+P)) 输入 'task',空格 和命令名称来运行您的任务。在这种情况下,'task lint'。

可以使用以下设置禁用任务自动检测:

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

自定义任务

并非所有任务或脚本都能在您的工作区中自动检测到。有时需要定义自己的自定义任务。假设您有一个脚本来运行测试以正确设置某些环境。该脚本存储在工作区内的脚本文件夹中,对于Linux和macOS命名为test.sh,对于Windows命名为test.cmd。从全局终端菜单中运行配置任务,并选择从模板创建tasks.json文件条目。这将打开以下选择器:

配置任务运行器

注意: 如果您没有看到任务运行器模板的列表,可能是因为您的文件夹中已经有一个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: 定义任务输出在用户界面中的处理方式。在此示例中,显示输出的集成终端始终被显示,并且在每次任务运行时都会创建一个新的终端。
  • options: 覆盖cwd(当前工作目录)、env(环境变量)或shell(默认shell)的默认值。选项可以按任务设置,也可以全局或按平台设置。这里配置的环境变量只能从您的任务脚本或进程中引用,如果它们是您的参数、命令或其他任务属性的一部分,则不会被解析。
  • runOptions: 定义任务何时以及如何运行。
  • hide: 从“运行任务快速选择”中隐藏任务,这对于复合任务中不可独立运行的元素非常有用。

你可以在tasks.json文件中使用IntelliSense查看完整的任务属性和值。使用触发建议⌃Space (Windows, Linux Ctrl+Space))来显示建议,并通过悬停或阅读更多...('i')弹出窗口阅读描述。

tasks.json IntelliSense

你也可以查看tasks.json schema

当涉及到包含空格或其他特殊字符(如$)的命令和参数时,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)来获取有效属性的概览。

任务智能感知

除了全局菜单栏外,任务命令还可以通过命令面板⇧⌘P(Windows, Linux Ctrl+Shift+P)访问。您可以过滤“task”并查看各种与任务相关的命令。

命令面板中的任务

复合任务

你也可以使用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"]
}

用户级别任务

您可以使用Tasks: Open User Tasks命令创建不绑定到特定工作区或文件夹的用户级别任务。这里只能使用shellprocess任务,因为其他任务类型需要工作区信息。

输出行为

有时您希望在运行任务时控制集成终端面板的行为。例如,您可能希望最大化编辑器空间,并且只有在认为有问题时才查看任务输出。可以使用任务的presentation属性来控制终端的行为。它提供了以下属性:

  • reveal: 控制是否将集成终端面板带到前台。有效值为:
    • always - 面板总是被带到前台。这是默认值。
    • never - 用户必须明确使用 View > Terminal 命令将终端面板带到前台 (⌃` (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文件中。假设您有以下使用ESLint来检查JavaScript文件的Gulp文件(该文件取自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...
});

从全局终端菜单执行运行任务将显示以下选择器:

配置任务

按下齿轮图标。这将创建以下 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 监视: $tsc-watch 匹配在监视模式下执行时由 tsc 编译器报告的问题。
  • JSHint: $jshint 假设文件名以绝对路径报告。
  • JSHint Stylish: $jshint-stylish 假设文件名以绝对路径的形式报告。
  • ESLint 简洁模式: $eslint-compact 假设输出中的文件名是相对于打开的文件夹的。
  • ESLint Stylish: $eslint-stylish 假设输出中的文件名是相对于打开的文件夹的。
  • Go: $go 匹配从 go 编译器报告的问题。假设文件名相对于打开的文件夹。
  • CSharp 和 VB 编译器: $mscompile 假设文件名以绝对路径报告。
  • Lessc 编译器: $lessc 假设文件名以绝对路径报告。
  • Node Sass 编译器: $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可执行文件在当前文件上执行autopep8:

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

如果你想指定Python扩展用于tasks.jsonlaunch.json的所选Python解释器,你可以使用${command:python.interpreterPath}命令。

如果简单的变量替换不够用,您还可以通过向tasks.json文件添加inputs部分来从任务用户那里获取输入。

输入示例

有关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,以及用于macOS的osx。在特定操作系统范围内定义的属性会覆盖在任务或全局范围内定义的属性。

全局任务

任务属性也可以在全局范围内定义。如果存在,它们将用于特定任务,除非这些任务定义了具有不同值的相同属性。在下面的示例中,有一个全局的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 时,你可能会看到意外的空格和引号转义。这种意外的转义只发生在 cmdlets 上,因为 VS Code 不知道你的命令是否包含 cmdlets。下面的示例 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的编码。

如果您只需要针对特定任务进行调整,那么请在任务命令行中添加必要的操作系统特定命令以更改编码。以下示例适用于使用代码页437作为默认设置的Windows。该任务显示包含西里尔字符的文件的输出,因此需要代码页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 游乐场,它支持 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
  }
}

请注意,文件、行和消息属性是必需的。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问题匹配器

注意: C/C++ 扩展 包含了 GCC 的问题匹配器,因此无需定义我们自己的。

在模式中还可以使用一些其他属性。这些属性包括:

  • location - 如果问题位置是行或行,列或起始行,起始列,结束行,结束列,那么我们可以使用通用的位置匹配组。
  • endLine - 问题结束行的匹配组索引。如果编译器没有提供结束行值,则可以省略。
  • endColumn - 问题结束列的匹配组索引。如果编译器没有提供结束列值,则可以省略。
  • code - 问题代码的匹配组索引。如果编译器没有提供代码值,则可以省略。

你也可以定义一个只捕获文件的问题匹配器。为此,定义一个带有可选kind属性设置为filepattern。在这种情况下,不需要提供linelocation属性。

注意: 如果kind属性设置为file,则功能模式必须至少为filemessage提供一个匹配组。如果没有提供kind属性或kind属性设置为location,则功能模式还必须提供linelocation属性。

注意: 问题匹配器仅解析来自给定命令的输出。如果您想解析写入单独文件(例如日志文件)的输出,请在命令执行完成之前使其打印出单独文件中的行。

定义多行问题匹配器

一些工具会将源文件中发现的问题分散到多行显示,特别是在使用样式化的报告器时。一个例子是ESLint;在样式化模式下,它会生成如下输出:

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

我们的问题匹配器是基于行的,因此我们需要使用与实际问题位置和消息(1:0 error Missing "use strict" statement)不同的正则表达式来捕获文件名(test.js)。

为此,请使用问题模式的数组作为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的时尚问题:

{
  "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
}

其他可修改的问题匹配器属性包括 background, fileLocation, owner, pattern, severity, 和 source

后台 / 监控任务

一些工具支持在后台运行,同时监视文件系统的变化,然后在磁盘上的文件发生变化时触发操作。使用Gulp时,这种功能通过npm模块gulp-watch提供。TypeScript编译器tsc通过--watch命令行选项内置支持此功能。

为了在VS Code中提供后台任务正在运行并产生问题结果的反馈,问题匹配器必须使用额外的信息来检测输出中的这些state变化。让我们以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,以便任务在后台继续运行。

一个完整的手工制作的tasks.json用于在监视模式下运行的tsc任务看起来像这样:

{
  "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 Schema - 您可以查看完整的tasks.json模式和描述。
  • 基本编辑 - 了解强大的VS Code编辑器。
  • 代码导航 - 快速浏览您的源代码。
  • Language Support - 了解我们支持的编程语言,包括VS Code自带的以及通过社区扩展提供的。
  • Debugging - 直接在 VS Code 编辑器中调试您的源代码。

常见问题

任务可以使用与集成终端指定的不同的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"
                ]
            }
        }
    },
    ...

背景任务可以用作prelaunchTask在launch.json中吗?

是的。由于后台任务会一直运行直到被终止,后台任务本身没有“完成”的信号。要将后台任务用作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. 确保您的命令在您的路径上,并且不需要启动脚本来添加到您的路径中。这是解决问题的最彻底的方法,也是推荐的解决方案。
  2. 您可以为您的任务进行一次性的修复,使其以登录或交互方式运行。这不推荐,因为它可能会产生其他后果。然而,对于单个任务来说,这也可能是一个快速且简单的修复。以下是一个使用bash作为shell执行此操作的示例任务:
{
  "type": "npm",
  "script": "watch",
  "options": {
    "shell": {
      "args": ["-c", "-l"]
    }
  }
}

上述的npm任务将使用命令(-c)运行bash,就像任务系统默认做的那样。然而,这个任务还将bash作为登录shell(-l)运行。