想让VSCode识别自己的编程语言?立马安排 您所在的位置:网站首页 自己编程 想让VSCode识别自己的编程语言?立马安排

想让VSCode识别自己的编程语言?立马安排

2024-07-07 19:18| 来源: 网络整理| 查看: 265

VSCode语法高亮插件 前言一、安装Yeoman generator二、插件项目搭建三、项目结构四、Syntaxes4.1 $schema4.2 patterns4.3 repository 五、安装插件结语

前言

大家好!最近我在做自己的毕业设计,简单来说需要自制一个编程语言,当然具体情况还要更复杂。现在语言本身已经有一定完成度了,但是在我测试的时候,看白底黑字的Notepad总是感觉差那么点意思。VSCode那多彩的高亮,自动补全括号真是用了就忘不掉,那有没有办法让我们自己的语言也能被VSCode高亮呢?

当然有!本篇博客我就来教大家如何使用Yeoman generator来制作VSCode代码高亮插件。

一、安装Yeoman generator

Yeoman是一个通用型脚手架工具,简单来说就是帮你搭建一个项目的基本结构。在Yeoman所能完成的各种项目中,就包括VSCode插件,这也是我们所需要的:

npm install -g yo generator-code

这里我们使用nodejs安装Yeoman以及对应的Generator,下面开始搭建项目。

二、插件项目搭建

在全局安装好Yeoman之后,我们打开一个项目文件夹,然后执行:

yo code

这时你会看到如下界面,我们选择New Language Support:

yo_code

回车后会开始让我们回答一系列问题,分别为:

引入文件/URL,或者留空以搭建新项目。我们直接回车留空

该扩展的名字是什么。给我们自己的扩展起一个名字,比如: “MyLang Syntax Highlighting”

该扩展的标识符是什么。这个是当你安装插件后,在VSCode的扩展一栏中的显示名。一般会根据你上面的起名给一个默认的名字,我这里就是用默认名了

请描述你的扩展。写点东西让别人直到是干什么的啦

请输入你的语言的id。注意,这里的id唯一标记了你的语言,格式要求为单词(无空格)、小写。比如:“mylang”

请输入你的语言的名字。当你在VSCode中新建了一个文件,它会提示“选择语言以开始”,这里的起名就是显示在这里的。没有格式要求,随意起啦

请输入启用扩展的文件后缀。顾名思义,C++扩展为.cpp启用,Python扩展为.py启用。这里你需要指定哪些文件后缀启用我们自定义的扩展,比如:".ml"

请输入语法根作用域名。这个一般就是source.{7中定义的后缀},比如:“source.ml”

是否需要初始化一个git仓库。如果只是为了自己使用,选no就好了

现在,你会看到自动生成了一个以3中自定义标识符为名的文件夹,我们所需要的项目文件就全在里面了!

三、项目结构

大体看一下文件目录:

D:. │ .vscodeignore │ CHANGELOG.md │ language-configuration.json │ package.json │ README.md │ vsc-extension-quickstart.md │ ├─.vscode │ launch.json │ └─syntaxes mylang.tmLanguage.json

package.json,保存了我们在第二步中给出的答案,你也可以在此处进行修改。

几个Markdown文件,为你提供了标准的通知模板,如果你有留意过VSCode下载插件界面的内容,会发现与该模板大差不差。

language-configuration.json,该文件中的内容,相当于全局定义,它包括以下几部分:注释方式、允许的括号、自动补全的括号、选中文字后输入会自动括起来的符号。

/syntexes/mylang.tmLanguage.json,该文件是本项目的重点,我们需要在此处定义编程语言的文法,下面我们将展开讲述这一部分。

四、Syntaxes

TextMate官方文档指路

tm是TextMate的缩写,原本是一个Mac系统上的一个编辑器,支持一系列用户自定义功能。微软为VSCode添加了一个vscode-textmate的解释器,使得VSCode也可以使用TextMate文法定义的扩展组件。

我们打开mylang.tmLanguage.json,看看里面的内容:

{ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "MyLang", "patterns": [ { "include": "#keywords" } ], "repository": { "keywords": { "patterns": [ { "name": "keyword.control.mylang", "match": "\\b(if|while|for|return)\\b" } ] } }, "scopeName": "source.ml" } 4.1 $schema

模式。这个模式所指向的文件,将定义接下来tmLanguage文件中键值对的意义,接受的值类型,以及预定义的高亮主题。

注意该文件最后的枚举值,我们自定义的匹配规则应当总是继承自这些已有的枚举值:

// 限于篇幅,只列出了部分枚举值 { "type": "string", "enum": [ // 注释 "comment", "comment.block", "comment.line", "comment.line.double-dash", "comment.line.double-slash", // 常量 "constant", "constant.character", "constant.character.escape", "constant.language", "constant.numeric", "constant.other", "constant.regexp", "constant.rgb-value", "constant.sha.git-rebase", // 实例 "entity", "entity.name", "entity.name.class", "entity.name.function", "entity.name.method", "entity.name.type", // 无效 "invalid", "invalid.deprecated", "invalid.illegal", // 关键字 "keyword", "keyword.control", "keyword.control.less", "keyword.operator", "keyword.operator.new", // 标记(加粗、斜体等) "markup", "markup.bold", "markup.italic", "markup.list", "markup.quote", "markup.underline", // 存储(外部文件相关,如import) "storage", "storage.modifier", "storage.modifier.import.java", "storage.modifier.package.java", "storage.type", // 字符串 "string", "string.quoted", "string.quoted.single", "string.quoted.double", "string.quoted.triple", "string.regexp", "string.xml", // 内置支持(用于内部函数、内部类) "support", "support.class", "support.constant", "support.function", "support.other", "support.property-value", "support.type", "support.variable", // 变量 "variable", "variable.language", "variable.name", "variable.parameter" ] }

具体的使用示例我将在之后演示。

4.2 patterns

规则。规则是一个列表,每个列表项是一个键值对(字典),这些规则将用来解析我们的代码。一般来讲,我们只需要在patterns中定义,解析该语言需要哪些规则,而规则的具体内容,将在之后的repository中完成。例如:

"patterns": [ { "include": "#comments" }, { "include": "#keywords" }, { "include": "#statements" } { "include": "#strings" } ]

这里我们定义了几个规则,分别是:注释规则、关键字规则、语句规则、字符串规则。接下来我们需要实现这些规则的定义

4.3 repository

仓库中需要我们来实现patterns里面定义的规则,仓库本身是一个字典,它的键值对为:规则:[规则内容],规则内容包含很多可选项,我们举个例子说明:

"keywords": { "patterns": [ { "name": "keyword.control.mylang", "match": "\\b(if|else|while|for|return|and|or|break|continue)\\b" } { "name": "constant.language.boolean.mylang", "match": "\\b(true|false)\\b" } ] }, "strings": { "name": "string.quoted.double.mylang", "begin": "\"", "end": "\"" },

在上面这两个键值中,我们实现了规则中定义的“关键字”和“字符串”,让我们来看看都使用了什么规则内容:

patterns。如果一个规则中包含着很多个子规则,你可以用一个列表对它们进行定义。或者,你也可以使用类似4.2中patterns的语法,include一个子规则,而后再后面继续定义。如果你的语言语法比较复杂,那么建议使用后一种方法,对语法进行细分,更详细的划分方式可以参考VSCode如何为Python撰写的tmLanguage文件

name,你需要为每一个规则提供一个名字。注意,这里的名字需要继承自schema中提供的枚举值。就像上例中的strings规则,它的名字是string.quoted.double.mylang,就是继承自string.quoted.double,双引号括起的字符串。这个名字将决定最后使用主题中定义的哪个颜色为其上色,关于主题,请看VSCode的官方文档。

match,匹配规则。TextMate中的所有匹配规则都以正则表达式的形式定义,又因为我们是在json中以字符串形式传递这些正则表达式,所以不可避免地要进行转义字符。match语句用于定义一条完整的正则表达式,例如上面的:"\\b(true|false)\\b",用于捕获单词true/false,然后设定为boolean高亮。

begin & end。除了使用match定义一条正则语句外,你还可以定义起始匹配标志与终止匹配标志,来实现区间捕获。可以看到上例中对于字符串的处理,就是以双引号起始,双引号终结的一段。

captures。上面的例子很简单,我们一条正则表达式就对应一个语法,但有时候语法可以拆分成很多部分,而且有些部分是可选的,那么就可以使用captures来精确细分:

{ "match": "\\b(func)\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\(([A-Za-z0-9,\\s]*)\\)", "captures": { "1": { "name": "storage.type.function.cploxplox" }, "2": { "name": "entity.name.function.cploxplox" }, "3": { "patterns": [ { "match": "\\b[A-Za-z_][A-Za-z0-9_]*\\b", "name": "variable.parameter.cploxplox" } ] } } },

上面给的这个例子,是我自己的语言cploxplox中对于函数的定义语法,bnf形式表述为:

func (identifier)? "(" (identifier (, identifier)*)? ")" block

这里面可以分出几部分:

func,一个标志着接下来开始定义函数的字符

identifier,如果有就是一个有名函数,否则为匿名函数

参数列表,可以为空

函数体

前三部分,分别对应正则表达式中三个()分割开的捕获分组,它们三个不是用同样的颜色高亮,因此我们使用captures,分别对1、2、3分组做了更进一步的高亮定义。

beginCaptures & endCaptures。captures是针对match的,那么同理针对begin和end也有相应的captures,使用的方法和上面captures的例子类似,不再赘述。

以上便是repository中比较常用的几个规则内容了,当然还有更多,比如为了方便别人理解,你可以添加comment字段;想要为begin和end之间的部分添加高亮,可以使用contentName来完成;引用其他语言的高亮规则,可以使用include一个文件/URL的方式实现。这些就需要你详细的阅读官方文档了。

下面我放出本次教程中我书写的完整tmLanguage文件内容,供大家参考:

{ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "MyLang", "patterns": [ { "include": "#comment" }, { "include": "#statements" }, { "include": "#keywords" }, { "include": "#strings" } ], "repository": { "comment": { "begin": "//", "end": "\\n", "name": "comment.line.double-slash" }, "keywords": { "patterns": [ { "name": "keyword.control.mylanguage", "match": "\\b(if|else|while|for|return|and|or|break|continue)\\b" }, { "name": "constant.language.boolean.mylanguage", "match": "\\b(true|false)\\b" } ] }, "statements": { "match": "\\b(func)\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\(([A-Za-z0-9,\\s]*)\\)", "captures": { "1": { "name": "storage.type.function.mylang" }, "2": { "name": "entity.name.function.mylang" }, "3": { "patterns": [ { "match": "\\b[A-Za-z_][A-Za-z0-9_]*\\b", "name": "variable.parameter.mylang" } ] } } }, "strings": { "name": "string.quoted.double.mylang", "begin": "\"", "end": "\"", "patterns": [ { "name": "constant.character.escape.mylang", "match": "\\\\." } ] } }, "scopeName": "source.ml" } 五、安装插件

好,现在我们已经完成了插件的实现部分,要怎么才能让VSCode识别到呢?非常简单,把整个插件文件夹,拷贝到%USERPROFILE%/.vscode/extensions(Linux是~/.vscode/extensions),重启VSCode就可以啦!

最终效果

结语

不知道大家有没有自制编程语言的兴趣呢?如果有的话,可以来我的仓库中,帮我一同开发cploxplox哦!在仓库的scripts中我提供了扩展cploxplox的指南,有兴趣的小伙伴可以在该项目中出一份力!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有