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,也不愿您因为空格而犹豫。
通过参与本项目,您同意遵守贡献者行为准则。