我想知道是否可以直接使用 CFG 或 PEG 语法作为代码完成的基础而无需修改。我听说代码完成在 IDE 中有时会被操纵和按摩,甚至是硬编码,以使其性能更好。
我想在一个小型 DSL 上完成代码,所以我完全理解语法不能帮助具有库函数等知识的代码完成系统。
据我所知,解析器本身至少需要提供一个系统来查询它接下来的期望。
特别是我对使用peg.js 或jison的 javascript 代码完成解决方案感兴趣
我想知道是否可以直接使用 CFG 或 PEG 语法作为代码完成的基础而无需修改。我听说代码完成在 IDE 中有时会被操纵和按摩,甚至是硬编码,以使其性能更好。
我想在一个小型 DSL 上完成代码,所以我完全理解语法不能帮助具有库函数等知识的代码完成系统。
据我所知,解析器本身至少需要提供一个系统来查询它接下来的期望。
特别是我对使用peg.js 或jison的 javascript 代码完成解决方案感兴趣
使用 PEG 语法的代码完成来构建 Javascript 编辑器是相当容易的。我将描述如何使用PEG.js
. 您需要使用一些宽松的解析规则来扩展您的语法,这些规则允许在先前的语句被破坏时提供建议。这些松散的规则需要有条件地处理,否则您将需要两种单独的语法 - 一种用于解析源代码,第二种用于代码完成。您可以使用 Javascript 谓词(在 中提供PEG.js
)来维护一种语法。它看起来像,当flag 为&{return laxParsing}
时,它会导致整个规则被处理。通过设置解析器的内部标志,您可以轻松地在松散和严格解析之间切换。laxParsing
true
为了方便地向用户提供建议,您必须修改稍微生成的PEG.js
解析器(版本 0.5)以接收解析错误结构位置(在列和行旁边)和期望列表(在错误消息旁边)。您可以从https://gist.github.com/1281239复制准备好的片段。
当您拥有解析器时,您可以将其附加到编辑器中,例如 CTRL+SPACE 按键。当在文本源中按下这些时,您需要放置一个特殊的不可解析符号来代替光标(以导致解析错误)并在松散模式下启动解析器。然后,您会收到一个包含建议列表的错误。
一些建议不仅是语法,而且还定义了引用(例如实体、变量)。当找到特定的期望(例如VariableName
)时,您可以触发搜索这些。您可以通过以不同的宽松解析模式(仅过滤变量名)解析相同的源来提供列表。
有关此方法的工作示例和来源,您可以查看https://github.com/mstefaniuk/Concrete-Freetext。
PEG.js 在生成 SyntaxError 时为您提供了相当多的上下文。例如,如果您有一个 SQL 语法并提供如下内容:
FOO > 10 A
然后 PEG.js 将返回:
{
"message": "Expected \"AND\", \"ORDER BY\" or end of input but \"A\" found.",
"expected": [
{
"type": "literal",
"value": "AND",
"description": "\"AND\""
},
{
"type": "literal",
"value": "ORDER BY",
"description": "\"ORDER BY\""
},
{
"type": "end",
"description": "end of input"
}
],
"found": "A",
"offset": 9,
"line": 1,
"column": 10,
"name": "SyntaxError"
}
它的意思是它解析了字符串的第 0 到 9 个字符(“FOO > 10”),但随后在第 10 个字符处遇到了一个意外的标记。它为您提供了它所期望的下一个标记的列表:FOO > 10 AND
、FOO > 10 ORDER BY
、FOO > 10
。如果您将这些附加到查询的有效部分,您将获得一组很好的可能完成:
function getCompletions(pegParse, text) {
var parsedText = pegParse(text);
var completions = [];
if (parsedText.expected) {
var start = text.substr(0, parsedText.offset);
parsedText.expected.forEach(function(expected) {
if (expected.type != 'literal') return;
var completion = start + expected.value;
if (completion.substr(0, text.length) == text) {
completions.push(completion);
}
});
}
return completions;
}
这非常简单——真正的自动完成将不仅仅匹配文字,并且需要某种方式来利用语法不可用的上下文,例如用户正在调用的函数的参数列表。