使用语言配置自定义编辑器行为

你可以通过使用语言配置来启用特定于语言的语法操作,从而在 Visual Studio 编辑器中实现自定义的语言特定语法。 与使用语言服务器相比,使用语言配置可以提高性能,因为它的所有操作都是本地操作。

什么是语言配置

Visual Studio 通过语言扩展为各种编程语言提供智能编辑功能。 语言配置补充了使用语言服务器协议 (LSP) 的服务器,并提供声明性数据,使 Visual Studio 编辑器能够做出格式设置、着色和完成决策,而不会延迟对 LSP 服务器进行异步查询。 声明性语言功能在配置文件中定义。 例如,与 Visual Studio 捆绑在一起的 HTML、CSS 和打字稿基本扩展可提供以下部分声明性语言功能:

  • 语法突出显示
  • 片段完成
  • 括号匹配
  • 自动添加结束括号
  • 注释切换
  • 自动缩进

Visual Studio 提供了为任何编程语言定义语言配置的扩展功能。 语言配置文件控制基本的编辑功能,例如注释切换、括号匹配和包围。

使用语言配置有助于:

  • 用户输入时进行同步工作
  • 简单性:带有正则表达式的短 JSON 文件比复杂的算法更容易维护
  • 可移植性:在 Visual Studio Code 和 Visual Studio 之间不需要进行更改或只需进行极少量更改

此外,语言配置文件提供了一种简单的方法来扩展 Visual Studio,以通过易于阅读的 JSON 文件支持一些基本的重构功能。

向 Visual Studio 扩展添加语言配置支持

向 Visual Studio 扩展添加语言配置支持分为三个部分:

  1. 创建 VSIX 项目
  2. 创建语言配置文件
  3. 添加语法文件
  4. 更新 pkgdef 文件

您可以在语言配置示例中探索工作示例。

创建 VSIX 项目

要使用语言配置创建语言服务扩展,首先请确保为 VS 实例安装了 Visual Studio 扩展开发工作负载。

接下来,通过导航到文件>新建项目来创建一个新的 VSIX 项目,搜索“vsix”并查找 VSIX 项目

Screenshot showing how to create a VSIX project.

创建语言配置文件

在制作自己的语言配置文件时,您可以选择要在 JSON 文件中加入哪些方面的内容。 例如,您可以选择支持注释切换、自动添加右括号或本节中描述的可用功能的任意组合。

要添加对扩展的支持,你首先需要创建一个语言配置文件。 文件名必须遵循以下标准:使用连字符分隔文件名中的单词,并确保以 language-configuration.json 结尾。

以下代码显示了示例语言配置文件。

{
    "comments": {
      "lineComment": "***",
      "blockComment": ["{*", "*}"]
    },
    "brackets": [
      ["@", "@"],
      ["#", "#"],
      ["$", "$"],
      ["(", ")"]
    ],
    "autoClosingPairs": [
      { "open": "{", "close": "}" },
      { "open": "@", "close": "@" },
      { "open": "#", "close": "#" },
      { "open": "$", "close": "$" },
      { "open": "(", "close": ")" },
      { "open": "'", "close": "'", "notIn": ["string", "comment"] },
      { "open": "\"", "close": "\"", "notIn": ["string"] },
    ],
    "autoCloseBefore": ";:.,=}])>` \n\t",
    "surroundingPairs": [
      ["@", "@"],
      ["#", "#"],
      ["$", "$"],
      ["[", "]"],
      ["(", ")"],
      ["'", "'"],
      ["\"", "\""],
      ["`", "`"]
    ],
    "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)",
    "indentationRules": {
      "increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$",
      "decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$"
    }
  }

配置设置

以下部分描述了语言配置文件中可用的设置。

注释切换

语言配置文件提供了两个用于注释切换的命令。 切换行注释切换块注释。 您可以指定 comments.blockCommentcomments.lineComment 来控制 Visual Studio 应如何注释掉行/块。

{
  "comments": {
    "lineComment": "//",
    "blockComment": ["/*", "*/"]
  }
}

当您按 Ctrl+KCtrl+C 时,此设置会影响 Visual Studio 文本编辑器的行为。

括号定义

当你将光标移动到此处定义的括号时,Visual Studio 会突出显示该括号及其匹配对。

{
  "brackets": [["{", "}"], ["[", "]"], ["(", ")"]]
}

Visual Studio 工具 > 选项对话框中的相关设置在文本编辑器、常规、显示中是启用大括号对着色复选框。

自动关闭

当您键入 ' 时,Visual Studio 会创建一对单引号并将光标放在中间:'|'。 本节定义了此类对。

{
  "autoClosingPairs": [
    { "open": "{", "close": "}" },
    { "open": "[", "close": "]" },
    { "open": "(", "close": ")" },
    { "open": "'", "close": "'", "notIn": ["string", "comment"] },
    { "open": "\"", "close": "\"", "notIn": ["string"] },
    { "open": "`", "close": "`", "notIn": ["string", "comment"] },
    { "open": "/**", "close": " */", "notIn": ["string"] }
  ]
}

notIn 键会在某些代码范围内禁用此功能。 例如,当您编写以下代码时:

// ES6's Template String
`ES6's Template String`;

单引号不会自动关闭。

不需要 notIn 属性的对也可以使用更简单的语法:

{
  "autoClosingPairs": [ ["{", "}"], ["[", "]"] ]
}
在前面自动关闭

默认情况下,Visual Studio 仅在光标后面有空格时自动关闭对。 因此,当您在以下 JSX 代码中输入 { 时,将不会进行自动关闭:

const Component = () =>
  <div className={>
                  ^ Does not get autoclosed by default
  </div>

但是,此定义会替代该行为:

{
  "autoCloseBefore": ";:.,=}])>` \n\t"
}

现在,当您在 > 之前输入 { 时,Visual Studio 会使用 } 自动关闭它。

自动包围

当您在 Visual Studio 中选择一个范围并输入左括号时,Visual Studio 会用一对括号将所选内容括起来。 此功能称为自动包围,您可以在此处为特定语言定义自动包围对:

{
  "surroundingPairs": [
    ["{", "}"],
    ["[", "]"],
    ["(", ")"],
    ["'", "'"],
    ["\"", "\""],
    ["`", "`"]
  ]
}

Visual Studio 工具> 选项中的相关设置在文本编辑器、常规、显示中是键入引号或括号时自动包围所选内容复选框。

字词模式

wordPattern 定义编程语言中被视为字词的内容。 如果设置了 wordPattern,则代码建议功能会使用此设置来确定单词边界。

{
  "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)"
}

缩进规则

indentationRules 定义当您键入、粘贴和移动行时,或者当您使用 Ctrl+KCtrl+D(设置文档格式)和 Ctrl+KCtrl+F(格式选择)设置文本格式时,编辑器应如何调整当前行或下一行的缩进 。

{
  "indentationRules": {
    "increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$",
    "decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$"
  }
}

例如,如果 if (true) { 匹配 increaseIndentPattern,那么如果您在左括号 { 后按 Enter,则编辑器将自动缩进一次,您的代码最终将为:

if (true) {
  console.log();

除了 increaseIndentPatterndecreaseIndentPatter 之外,还有另外两个缩进规则:

  • indentNextLinePattern - 如果一个行与此模式匹配,则仅应将其后的下一行缩进一次。
  • unIndentedLinePattern- 如果某一行与此模式匹配,则不应更改其缩进,也不应根据其他规则对其进行评估。

如果没有为编程语言设置缩进规则,则编辑器会在行以左括号结束时缩进,并在您键入右括号时取消缩进。 此处的括号由 brackets 定义。

按 Enter

onEnterRules 定义在编辑器中按 Enter 时要评估的规则列表。

{
  "onEnterRules": [{
    "beforeText": "^\\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\\s*$",
    "action": { "indent": "indent" }
  }]
}

按 Enter 时,将根据以下属性检查光标之前、之后或上方一行的文本:

  • beforeText(必备)。 匹配光标之前的文本的正则表达式(仅限于当前行)。
  • afterText。 匹配光标之后的文本的正则表达式(仅限于当前行)。
  • previousLineText。 与光标上方一行文本匹配的正则表达式。

如果所有指定的属性都匹配,则认为该规则匹配并且不评估其他 onEnterRulesonEnterRule 可以指定以下操作:

  • indent(必备)。 none, indent, outdent, indentOutdent 其中之一。
    • none 表示新行继承当前行的缩进。
    • indent 表示新行相对于当前行进行缩进。
    • outdent 表示新行相对于当前行没有缩进。
    • indentOutdent 表示插入两个新行,第一行缩进,第二行不缩进。
  • appendText。 附加在新行和缩进之后的字符串。
  • removeText。 要从新行缩进中删除的字符数。

属性设置

在扩展项目中,确保您的 language-configuration.json 文件具有以下属性设置:

Build Action = Content
Include in VSIX = True
Copy to output = Copy always 

(可选)添加语法文件

此外,您还可以添加 TextMate 语法文件来为语言提供语法着色。 TextMate 语法是正则表达式的结构化集合,并以 plist (XML) 或 JSON 文件形式编写。 请参阅语言语法。 如果您不提供特定于语言的语法文件,则使用内置的默认设置。

要添加自定义 TextMate 语法或主题文件,请执行下列步骤:

  1. 在扩展内创建一个名为“Grammars”的文件夹(或者可以是您选择的任何名称)。

  2. Grammars 文件夹中,包含您想要提供自定义着色的任何 *.tmlanguage*.plist*.tmtheme*.json 文件。

    提示

    .tmtheme 文件定义范围如何映射到 Visual Studio 分类(命名的颜色键)。 如需指导,您可以参考 %ProgramFiles(x86)%\Microsoft Visual Studio\<version>\<SKU>\Common7\IDE\CommonExtensions\Microsoft\TextMate\Starterkit\Themesg 目录中的全局 .tmtheme 文件。

创建 pkgdef 文件

接下来,创建 .pkgdef 文件。 .pkgdef 文件包含将以其他方式添加到系统注册表中的所有注册信息。 有关 pkgdef 文件的详细信息,请参阅注册 VSPackages什么是 pkgdef 文件?为什么要使用此文件?pkgdef 文件中,您应该具有 language-configuration.json 文件的路径和语言语法的路径。 LSP 等语言服务会请求编辑器内容类型并通过语言配置获取该类型。 此信息提供了可以与开发工具通信的服务器内部的语言特定智能。 当语言服务不存在时,语言配置引擎将回退到 TextMate 语法。 .pkgdef 文件应如下所示:

[$RootKey$\TextMate\Repositories]
"AspNetCoreRazor="$PackageFolder$\Grammars

// Defines where the language configuration file for a given
// grammar name is (value of the ScopeName tag in the tmlanguage file).
[$RootKey$\TextMate\LanguageConfiguration\GrammarMapping]
"text.aspnetcorerazor"="$PackageFolder$\language-configuration.json"

// Defines where the language configuration file for a given
// language name is (partial value of the content type name).
[$RootKey$\TextMate\LanguageConfiguration\ContentTypeMapping]
"RazorLSP"="$PackageFolder$\language-configuration.json"

[$RootKey$\TextMate\LanguageConfiguration\GrammarMapping]
"text.html.basic"="$PackageFolder$\html-language-configuration.json"
"source.js"="$PackageFolder$\javascript-language-configuration.json"
"source.css"="$PackageFolder$\css-language-configuration.json"
"source.cs"="$PackageFolder$\csharp-language-configuration.json

确保按如下方式设置 pkgdef 文件的属性:

Build Action = Content
Include in VSIX = True
Copy to output = Copy always 

为了使 Visual Studio 能够访问语言配置信息,请在 VSIX 包中包含 language-configuration 文件。 包含此文件意味着它将随 Visual Studio 扩展一起提供。 该文件可让 Visual Studio 知道语言配置可供使用。 要添加此文件,请编辑您的 vsixmanifest,以添加 PKGDEF def 文件,例如:

<Asset Type="Microsoft.VisualStudio.VsPackage" Path="Test.pkgdef"/>