Swift API,用于 tree-sitter 增量解析系统。
ResolvingQueryMatchSequence
支持查询谓词/指令这个项目实际上分为两个部分:SwiftTreeSitter
和 SwiftTreeSitterLayer
。
SwiftTreeSitter
目标与 C 运行时 API 非常接近。它只添加了少量额外的类型来帮助支持查询。它相当底层,在实际项目中使用它需要做大量工作。
SwiftTreeSitterLayer
是构建在 SwiftTreeSitter
之上的抽象层。它支持包含嵌套语言的文档,以及跨这些嵌套的透明查询。它还支持异步语言解析。虽然仍然是底层,但 SwiftTreeSitterLayer
更易于使用,同时也支持更多功能。
还有更多!如果您正在寻找用于语法高亮和其他语法操作的更高级系统,您可能需要看看 Neon。它更容易与文本系统集成,并且具有许多额外的性能相关功能。
dependencies: [
.package(url: "https://github.com/ChimeHQ/SwiftTreeSitter")
],
targets: [
.target(
name: "MySwiftTreeSitterTarget",
dependencies: ["SwiftTreeSitter"]
),
.target(
name: "MySwiftTreeSitterLayerTarget",
dependencies: [
.product(name: "SwiftTreeSitterLayer", package: "SwiftTreeSitter"),
]
),
]
tree-sitter 运行时操作原始字符串数据。这意味着它处理字节,并且对字符串编码敏感。Swift 的 String
类型是原始数据之上的抽象,不能直接使用。为了克服这个问题,您还需要了解您正在使用的索引类型,以及字符串数据如何来回转换。
为了帮助您,SwiftTreeSitter 支持基本的 tree-sitter 编码功能。您可以通过 Parser.parse(tree:encoding:readBlock:)
控制这一点。但是,默认情况下,这将假定为 UTF-16 编码的数据。这样做是为了提供与 Foundation 字符串和 NSRange
的直接兼容性,它们都使用 UTF-16。
此外,为了帮助处理所有的来回转换,SwiftTreeSitter 包含一些基于 NSRange
的访问器,以及 NSRange
的扩展。除非您自己注意处理编码,否则在使用原生 tree-sitter 类型时必须使用这些。
为了保持清晰,使用了 consistent 的命名和类型。Node.byteRange
返回一个 Range<UInt32>
,这是一个依赖于编码的值。Node.range
是一个 NSRange
,它被定义为使用 UTF-16。
let node = tree.rootNode!
// this is encoding-dependent and cannot be used with your storage
node.byteRange
// this is a UTF-16-assumed translation of the byte ranges
node.range
// converting UTF-16-based changed ranges on re-parse
let ranges: [NSRange] = newtree.changedRanges(from: oldTree)
.map{ $0.bytes.range }
SwiftTreeSitter 尽最大努力解决较差/不正确的查询构造,这些构造出奇地常见。
当使用注入时,子查询范围会自动使用父匹配项进行扩展。这处理了父项的查询以冲突方式与子项重叠的情况。如果没有扩展,则可能会构造出落在子范围内的查询,但会在父匹配项上产生结果。
所有匹配项都按以下顺序排序
即使有了这些,也可能产生导致“不正确”行为的查询,这些行为在查询定义中要么是模棱两可的,要么是未定义的。
tree-sitter 的一个非常常见的用途是进行语法高亮。可以直接使用此库,特别是如果您的源代码文本不更改。这是一个小例子,展示了如何使用 SPM 捆绑的语言进行设置。
首先,了解一下它如何与 SwiftTreeSitterLayer 一起工作。它很复杂,但为您做了很多事情。
// LanguageConfiguration takes care of finding and loading queries in SPM-created bundles.
let markdownConfig = try LanguageConfiguration(tree_sitter_markdown(), name: "Markdown")
let markdownInlineConfig = try LanguageConfiguration(
tree_sitter_markdown_inline(),
name: "MarkdownInline",
bundleName: "TreeSitterMarkdown_TreeSitterMarkdownInline"
)
let swiftConfig = try LanguageConfiguration(tree_sitter_swift(), name: "Swift")
// Unfortunately, injections do not use standardized language names, and can even be content-dependent. Your system must do this mapping.
let config = LanguageLayer.Configuration(
languageProvider: {
name in
switch name {
case "markdown":
return markdownConfig
case "markdown_inline":
return markdownInlineConfig
case "swift":
return swiftConfig
default:
return nil
}
}
)
let rootLayer = try LanguageLayer(languageConfig: markdownConfig, configuration: config)
let source = """
# this is markdown
```swift
func main(a: Int) {
}
```
## also markdown
```swift
let value = "abc"
```
"""
rootLayer.replaceContent(with: source)
let fullRange = NSRange(source.startIndex..<source.endIndex, in: source)
let textProvider = source.predicateTextProvider
let highlights = try rootLayer.highlights(in: fullRange, provider: textProvider)
for namedRange in highlights {
print("\(namedRange.name): \(namedRange.range)")
}
您也可以直接使用 SwiftTreeSitter
let swiftConfig = try LanguageConfiguration(tree_sitter_swift(), name: "Swift")
let parser = Parser()
try parser.setLanguage(swiftConfig.language)
let source = """
func main() {}
"""
let tree = parser.parse(source)!
let query = swiftConfig.queries[.highlights]!
let cursor = query.execute(in: tree)
let highlights = cursor
.resolve(with: .init(string: source))
.highlights()
for namedRange in highlights {
print("range: ", namedRange)
}
Tree-sitter 语言解析器是单独的项目,您可能至少需要一个。更多详细信息请参阅文档。它们的安装和集成方式各不相同。
这是支持 SPM 的解析器列表。既然您在这里,您可能会觉得这很方便。并且 LanguageConfiguration
类型支持直接加载捆绑的查询。
解析器 | Make | SPM | 官方仓库 |
---|---|---|---|
Bash | ✅ | ✅ | |
C | ✅ | ✅ | |
C++ | ✅ | ✅ | |
C# | ✅ | ✅ | |
Clojure | ✅ | ||
CSS | ✅ | ✅ | ✅ |
Dockerfile | ✅ | ✅ | ✅ |
Diff | ✅ | ✅ | |
Elixir | ✅ | ✅ | ✅ |
Elm | ✅ | ✅ | |
Go | ✅ | ✅ | ✅ |
GoMod | ✅ | ✅ | ✅ |
GoWork | ✅ | ||
Haskell | ✅ | ✅ | |
HCL | ✅ | ✅ | |
HTML | ✅ | ✅ | |
Java | ✅ | ✅ | ✅ |
Javascript | ✅ | ✅ | |
JSON | ✅ | ✅ | ✅ |
JSDoc | ✅ | ✅ | |
Julia | ✅ | ✅ | |
Kotlin | ✅ | ||
Latex | ✅ | ✅ | ✅ |
Lua | ✅ | ✅ | |
Markdown | ✅ | ✅ | |
OCaml | ✅ | ✅ | |
Perl | ✅ | ✅ | |
PHP | ✅ | ✅ | ✅ |
Pkl | ✅ | ✅ | |
Python | ✅ | ✅ | |
Ruby | ✅ | ✅ | ✅ |
Rust | ✅ | ✅ | |
Scala | ✅ | ✅ | |
SQL | ✅ | ✅ | |
SSH | ✅ | ✅ | |
Swift | ✅ | ✅ | ✅ |
TOML | ✅ | ||
Tree-sitter 查询语言 | ✅ | ✅ | |
Typescript | ✅ | ✅ | |
Verilog | ✅ | ✅ | |
YAML | ✅ | ||
Zig | ✅ | ✅ | ✅ |
我很乐意收到您的来信!问题或拉取请求都很好。Discord 服务器也提供实时帮助,但我强烈倾向于以文档的形式回答。
我更喜欢协作,如果您有类似的项目,我很乐意找到合作的方式。
我更喜欢使用制表符进行缩进,以提高可访问性。但是,我宁愿您使用您想要的系统并提交 PR,也不愿您因为空格而犹豫。
通过参与本项目,您同意遵守贡献者行为准则。