我如何制作一个不难理解的 VSCode 语法荧光笔?
How would I make a VSCode syntax highlighter that is not extremely hard to understand?
我正在尝试为我自己的标记语言制作自定义语法荧光笔。所有的例子都很复杂,缺少步骤并且非常非常难以理解。
是否有完整的文档说明如何制作语法高亮器?
(对于 VSCode,顺便说一句)
例如,这个视频 https://www.youtube.com/watch?v=5msZv-nKebI 中间有一个非常大的跳跃,并没有真正解释太多。
我当前使用 Yeoman 生成器制作的代码是:
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "BetterMarkupLanguage",
"patterns": [
{
"include": "#keywords"
},
{
"include": "#strings"
}
],
"repository": {
"keywords": {
"patterns": [{
"name": "entity.other.bml",
"match": "\b({|}|\\|//)\b"
}]
},
"strings": {
"name": "string.quoted.double.bml",
"begin": "`",
"end": "`"
}
},
"scopeName": "source.bml"
}
剧情简介
我不确定你在什么层次上解决了这个问题,但基本上有两种 syntax-highlighting:
- 只需识别规范可识别标记(字符串、数字、可能的运算符、保留字、注释)的小块并突出显示,或者
- 做前者,还添加上下文感知。
tmLanguage
引擎基本上有两个工作:
- 分配范围。
- 维护上下文堆栈。
示例
假设您使用以下模式定义整数:
"integers": {
"patterns": [{
"name": "constant.numeric.integer.bml",
"match": "[+-]\d+"
}]
},
当引擎匹配这样的整数时,它将匹配到正则表达式的末尾,从 "name"
分配范围,然后在相同的上下文中继续匹配 中的内容。
将其与您的“字符串”定义进行比较:
"strings": {
"name": "string.quoted.double.bml", // should be string.quoted.backtick.bml
"begin": "`",
"end": "`"
},
那些 "begin"
和 "end"
标记表示 tmLanguage
堆栈中的更改。您已推入一个新上下文 字符串内部。
现在,在此上下文中没有配置任何匹配项,但您可以通过添加带有一些 "match"
或 "include"
的 "patterns"
键来实现。 "include"
s 是其他匹配集,例如您在别处定义的“整数”。您可以将它添加到“字符串”模式以匹配字符串中的整数。匹配整数可能很愚蠢,但想想转义的反引号:你想限定它们的范围并留在“字符串”中的相同上下文中。您不希望那些过早地弹出。
运算顺序
您最终会注意到遇到的第一个模式是匹配的。还记得整数集吗?当你有 45.125
时会发生什么?它将决定将 45
和 125
匹配为整数 并完全忽略 .
。如果你有一个“浮点数”模式,你想在 before 你的原始整数模式中包含它。下面的这两个“数字”定义是等价的,但其中一个可以让你 re-use 独立的浮点数和整数(如果这对你的语言有用的话):
-
"numbers": {
"patterns": [
{"include": "#floats"},
{"include": "#integers"}
]
},
"integers": {
"patterns": [{
"name": "constant.numeric.integer.bml",
"match": "[+-]\d+"
}]
},
"floats": {
"patterns": [{
"name": "constant.numeric.float.bml",
"match": "[+-]\d+\.\d*"
}]
},
-
"numbers": {
"patterns": [{
"name": "constant.numeric.float.bml",
"match": "[+-]\d+\.\d*"
}, {
"name": "constant.numeric.integer.bml",
"match": "[+-]\d+"
}]
},
做对了
“数字”/“整数”/“浮点数”是微不足道的,但是 well-designed 语法定义将定义实用程序组,"include"
等同于 re-usability:
一个普通的编程语言会有这样的东西
- 可以直接执行的所有事物的“语句”组。这可能会或可能不会 (language-dependent) 包括...
- 可以放在作业 right-hand-side 中的一组“表达式”,其中肯定包括...
- 字符串、数字、字符等的“原子”组也可能是有效的语句,但这也取决于您的语言。
- "function-definitions" 可能不会出现在“表达式”中(除非它们是 lambda),但可能 会出现在“语句”中。函数定义可能会推入允许您
return
等的上下文。
像你这样的标记语言可能有
- 一个“内联”组,用于跟踪一个块在 中可以拥有的所有标记。
- 一个“块”组来保存列表、引用、段落,headers。
- ...
虽然您可以学到更多(捕获组、注入、作用域约定等),但希望这是一个实用的入门概述。
结论
当你写语法高亮的时候,你自己想一想:匹配这个标记是否让我处于一个可以再次匹配类似东西的地方?或者它是否将我置于不同的地方,应该匹配不同的东西(更多 或 更少)?如果是后者,那我returns原来的那套火柴呢?
我正在尝试为我自己的标记语言制作自定义语法荧光笔。所有的例子都很复杂,缺少步骤并且非常非常难以理解。
是否有完整的文档说明如何制作语法高亮器?
(对于 VSCode,顺便说一句)
例如,这个视频 https://www.youtube.com/watch?v=5msZv-nKebI 中间有一个非常大的跳跃,并没有真正解释太多。
我当前使用 Yeoman 生成器制作的代码是:
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "BetterMarkupLanguage",
"patterns": [
{
"include": "#keywords"
},
{
"include": "#strings"
}
],
"repository": {
"keywords": {
"patterns": [{
"name": "entity.other.bml",
"match": "\b({|}|\\|//)\b"
}]
},
"strings": {
"name": "string.quoted.double.bml",
"begin": "`",
"end": "`"
}
},
"scopeName": "source.bml"
}
剧情简介
我不确定你在什么层次上解决了这个问题,但基本上有两种 syntax-highlighting:
- 只需识别规范可识别标记(字符串、数字、可能的运算符、保留字、注释)的小块并突出显示,或者
- 做前者,还添加上下文感知。
tmLanguage
引擎基本上有两个工作:
- 分配范围。
- 维护上下文堆栈。
示例
假设您使用以下模式定义整数:
"integers": {
"patterns": [{
"name": "constant.numeric.integer.bml",
"match": "[+-]\d+"
}]
},
当引擎匹配这样的整数时,它将匹配到正则表达式的末尾,从 "name"
分配范围,然后在相同的上下文中继续匹配 中的内容。
将其与您的“字符串”定义进行比较:
"strings": {
"name": "string.quoted.double.bml", // should be string.quoted.backtick.bml
"begin": "`",
"end": "`"
},
那些 "begin"
和 "end"
标记表示 tmLanguage
堆栈中的更改。您已推入一个新上下文 字符串内部。
现在,在此上下文中没有配置任何匹配项,但您可以通过添加带有一些 "match"
或 "include"
的 "patterns"
键来实现。 "include"
s 是其他匹配集,例如您在别处定义的“整数”。您可以将它添加到“字符串”模式以匹配字符串中的整数。匹配整数可能很愚蠢,但想想转义的反引号:你想限定它们的范围并留在“字符串”中的相同上下文中。您不希望那些过早地弹出。
运算顺序
您最终会注意到遇到的第一个模式是匹配的。还记得整数集吗?当你有 45.125
时会发生什么?它将决定将 45
和 125
匹配为整数 并完全忽略 .
。如果你有一个“浮点数”模式,你想在 before 你的原始整数模式中包含它。下面的这两个“数字”定义是等价的,但其中一个可以让你 re-use 独立的浮点数和整数(如果这对你的语言有用的话):
-
"numbers": { "patterns": [ {"include": "#floats"}, {"include": "#integers"} ] }, "integers": { "patterns": [{ "name": "constant.numeric.integer.bml", "match": "[+-]\d+" }] }, "floats": { "patterns": [{ "name": "constant.numeric.float.bml", "match": "[+-]\d+\.\d*" }] },
-
"numbers": { "patterns": [{ "name": "constant.numeric.float.bml", "match": "[+-]\d+\.\d*" }, { "name": "constant.numeric.integer.bml", "match": "[+-]\d+" }] },
做对了
“数字”/“整数”/“浮点数”是微不足道的,但是 well-designed 语法定义将定义实用程序组,"include"
等同于 re-usability:
一个普通的编程语言会有这样的东西
- 可以直接执行的所有事物的“语句”组。这可能会或可能不会 (language-dependent) 包括...
- 可以放在作业 right-hand-side 中的一组“表达式”,其中肯定包括...
- 字符串、数字、字符等的“原子”组也可能是有效的语句,但这也取决于您的语言。
- "function-definitions" 可能不会出现在“表达式”中(除非它们是 lambda),但可能 会出现在“语句”中。函数定义可能会推入允许您
return
等的上下文。
像你这样的标记语言可能有
- 一个“内联”组,用于跟踪一个块在 中可以拥有的所有标记。
- 一个“块”组来保存列表、引用、段落,headers。
- ...
虽然您可以学到更多(捕获组、注入、作用域约定等),但希望这是一个实用的入门概述。
结论
当你写语法高亮的时候,你自己想一想:匹配这个标记是否让我处于一个可以再次匹配类似东西的地方?或者它是否将我置于不同的地方,应该匹配不同的东西(更多 或 更少)?如果是后者,那我returns原来的那套火柴呢?