首页
/ Understand-Anything 的 Swift 支持:语言提示片段如何与 tree-sitter 结构抽取协作,把 Swift 代码变成知识图谱

Understand-Anything 的 Swift 支持:语言提示片段如何与 tree-sitter 结构抽取协作,把 Swift 代码变成知识图谱

2026-09-06 17:16:23作者:秋泉律Samson

在 Understand-Anything 项目中,understand-anything-plugin/skills/understand/languages/swift.md/understand 技能内置的 Swift 语言提示片段(Language Prompt Snippet):它汇总了 Swift 的关键语言概念、导入模式、文件模式、常见框架与示例语言笔记,在项目被识别为包含 Swift 代码时,整段注入架构分析子代理的提示词中。读完本文,你能理解这份片段在整个分析流水线中的注入时机与作用,并通过 Swift 语言配置SwiftExtractor 结构抽取器 的源码,看清"提示词侧"与"确定性解析侧"是如何对同一套 Swift 知识达成一致的。

一、这份文档是什么:/understand 技能的 Swift 语言上下文

这份片段与 SKILL.md 位于同一技能目录的 languages/ 子目录下,是 /understand 技能按需加载的提示词素材。它的加载契约在 SKILL.md 的 Phase 4(ARCHITECTURE)中写明(见 Phase 4 语言上下文注入步骤):

对 Phase 1 检测到的每一种语言,读取 ./languages/<language-id>.md(例如 ./languages/python.md),将其内容附加到基础模板之后,位于 ## Language Context 标题之下;如果该语言对应的文件不存在,则静默跳过并继续。包括非代码语言片段 —— 它们为这些非代码文件提供边模式和摘要风格。

也就是说:Phase 1 的扫描(SCAN)阶段先确定项目的语言集合;一旦检测出 Swift,架构分析子代理(定义见 architecture-analyzer.md)的提示词就会带上这份 swift.md 全文,用来指导 LLM 在划分架构层、撰写摘要与标签时遵循 Swift 的惯用结构(例如把 Package.swift 视为配置、App.swift 视为入口、Tests/ 视为测试目录)。下面完整继承原文档的全部技术要素。

1.1 Key Concepts(关键概念清单)

原文档列出 10 个核心 Swift 概念,它们同时也是 LLM 判读 Swift 文件时的"概念词汇表":

概念 原文档表述
Optionals 与可选链 Type? 包裹可能为 nil 的值;?. 安全链式调用
Protocols 与协议扩展 用扩展为协议提供默认实现,形成契约
值类型 vs 引用类型 struct 与 enum 是值类型;class 是引用类型
Closures 闭包 自包含的功能块,可捕获周围上下文
Property Wrappers 属性包装器 @State@Binding@Published 封装属性存储逻辑
Result Builders 结果构建器 @ViewBuilder@resultBuilder 支持声明式 DSL 语法
Actors 与结构化并发 actor 类型实现数据隔离;async letTaskGroup
Generics 泛型 where 子句的泛型参数与关联类型约束
带关联值的枚举 每个 case 可携带类型各异的有效载荷
Extensions 扩展 为已有类型追加方法、计算属性与协议一致性

值得注意:这 10 条并不是孤立的提示词素材。与它们一一对应的是 swiftConfig 中的 concepts 字段(optionalsprotocolsextensionsgenericsclosuresproperty wrappersresult buildersactorsstructured concurrencyvalue types vs reference types)。从源码结构看,这份 concepts 列表会被 language-lesson 模块用于基于节点的 tags、summary 与 languageNotes 检测节点是否命中某个语言概念,为图谱节点生成"学习"内容——提示词侧的概念清单与确定性侧的概念表是刻意保持同源的。

1.2 Import Patterns(导入模式)

原文档归纳了 4 种 Swift 导入形态:

  • import Foundation —— 核心库,提供数据类型、集合与网络能力;
  • import UIKit —— 传统 view controller 架构的 iOS UI 框架;
  • import SwiftUI —— 带响应式状态管理的声明式 UI 框架;
  • @testable import ModuleName —— 以 internal 访问级别导入,用于单元测试。

这四种模式与提取器的测试用例逐条吻合:swift-extractor.test.ts 中有专门用例验证 @testable import MyApp 能被正确解析为 source: "MyApp"(见 测试用例),并且 #if canImport(UIKit) 条件导入块内的 import UIKit 也能被带正确行号地提取出来(见 测试用例)。

1.3 File Patterns(文件模式)

原文档列出 5 类 Swift 项目的标志性文件:

文件/目录 含义
Package.swift Swift Package Manager 清单,定义 target 与依赖
*.xcodeproj / *.xcworkspace Xcode 工程与工作区配置
AppDelegate.swift UIKit 应用生命周期入口
App.swift 使用 @main 的 SwiftUI 应用入口
Tests/ 遵循 SPM 或 Xcode 惯例的测试 target 目录

这些"文档知识"在确定性侧有精确映射,见 swiftConfig.filePatterns

filePatterns: {
  entryPoints: ["Sources/*/main.swift", "App.swift", "AppDelegate.swift"],
  barrels: [],
  tests: ["*Tests.swift", "Tests/**/*.swift"],
  config: ["Package.swift"],
},

即文档中的 App.swift/AppDelegate.swift 被注册为入口点模式,Tests/ 目录被细化为 *Tests.swiftTests/**/*.swift 两个测试模式,Package.swift 被归类为配置文件。文件到语言的判定则由 LanguageRegistry.getForFile 按扩展名(.swift)或文件名完成;swiftConfig 通过 builtinLanguageConfigs 注册进默认注册表。

1.4 Common Frameworks(常见框架)

原文档列出 5 个在 Swift 代码库中最常见的框架:

  • SwiftUI —— 声明式 UI 框架,响应式数据流;
  • UIKit —— 命令式 UI 框架,使用 view controller 与 Auto Layout;
  • Vapor —— 支持 async 的服务器端 Swift Web 框架;
  • Combine —— 处理时间序列值流的响应式框架;
  • Core Data —— 对象图与持久化框架。

这份框架清单的作用是在框架检测命中后,与 frameworks/ 子目录下的框架补充说明一起进入架构分析提示词;Swift 项目若使用 Vapor 等 Web 框架做服务器端开发,层划分时也能据此区分 UI 层与服务器层。

1.5 Example Language Notes(示例语言笔记)

原文档给出两条 languageNotes 写作示例,定义了 LLM 为该技能图谱节点撰写语言笔记时的"语气与深度标准":

Uses @Published property wrapper to automatically notify SwiftUI views of state changes. When the wrapped value mutates, the property wrapper triggers objectWillChange on the enclosing ObservableObject, causing dependent views to re-render.

Protocol extensions provide default implementations, allowing types to conform by simply declaring conformance — no method body needed if defaults suffice.

两条示例的共同点:不只描述"用了什么语法",而是解释状态如何流转、机制如何触发@PublishedobjectWillChange → 视图重渲染;协议扩展默认实现 → 仅需声明一致性)。languageNotes 是图谱节点 schema 中的可选字符串字段(见 types.tsschema.ts),在仪表盘的 NodeInfo 组件 中展示给探索者。

二、确定性侧如何兑现这些 Swift 知识:SwiftExtractor

提示词片段负责"教 LLM 看懂 Swift",而 SwiftExtractor 负责"机器精确地拆解 Swift"。它基于 tree-sitter 语法运行,实现 extractStructure(结构分析)与 extractCallGraph(调用图)两个能力。以下实现细节均可在源码中逐条核对。

2.1 类型容器折叠:class/struct/enum/actor/extension 统一进 classes[]

TYPE_DECLARATION_KINDS 定义了 classstructenumactorextension 五种类型声明。源码注释说明了设计取舍:Swift 拥有比共享 StructuralAnalysis schema 直接表达的更多"类容器",因此沿用 Dart/Kotlin/Rust 的惯例,把 classstructenumactorprotocolextension 全部折叠进 classes[],而其中的可调用成员同时出现在 functions[] 里。

几个与原文档概念直接呼应的处理:

  • 带关联值的枚举enum LoadState { case idle; case failed(Error); case loaded(User) } 会被解析为类条目 LoadState,且 idle/failed/loaded 三个 case 进入 properties(见 测试用例);
  • Actorsactor Cache { ... } 被提取为 Cache 类型条目,属性 store 与方法 get(返回类型 Data?)均被记录(见 测试用例);
  • 扩展:扩展以 extension <Name> 的复合名作为独立条目,避免与原类型撞名,例如 struct User {} + extension User: Codable 产生 ["User", "extension User"] 两个类条目(见 classLikeName 实现测试用例);
  • 协议与关联类型protocol Repository 中的 associatedtype Itemvar id: String { get }func load(id:) async throws -> Iteminit(seed:) 分别落到 properties 与 methods,且 Repository.init 作为独立函数条目携带参数 ["seed"](见 extractProtocol 实现测试用例)。

2.2 成员命名约定:init/deinit/subscript 与外部标签

initdeinit 被记为成员方法,并以 容器名.init / 容器名.deinit 的函数名进入 functions[](如 Box.initBox.deinit);subscript 记为成员 subscript,并提取其参数与返回类型。参数提取刻意使用本地参数名而非外部标签func update(_ value: Int = 0, forKey key: String) 得到 ["value", "key"](见 extractParams 实现测试用例)。

2.3 可见性规则决定 exports

Swift 的可见性是图谱"导出面"的关键。isExported 判定 的逻辑是:继承父级导出状态,且声明不是 private/fileprivate。测试覆盖了完整的可见性矩阵:默认与 internal 导出;public/open/package 导出;private/fileprivate 不导出;public private(set) 属性仍导出;private extension 的成员不导出;@objc public func 这种"属性在前、修饰符在后"的排列也能识别(见 visibility 测试组)。

2.4 import 解析:source 取首段、specifier 取末段

extractImport 把导入标识符按 . 拆分:source 取第一段(模块名),specifier 取最后一段(具体符号)。这正好覆盖了原文档 Import Patterns 的粒度——import struct Foundation.Date 解析为 source: "Foundation"specifiers: ["Date"]import class UIKit.UIViewimport protocol Combine.Publisher 则分别得到 UIViewPublisher(见 测试用例)。带导入种类限定符(class/protocol/struct)的行也能正确处理,因为这些 token 在 tree-sitter 树中不是 identifier 类型的命名子节点。

2.5 调用图:调用者栈与可选链归一化

extractCallGraph 维护一个调用者栈:进入 function_declarationinit(记为 容器名.init)、deinit 时压栈,离开时弹栈;计算属性与 willset/didset 观察块内发生的调用归属到属性名本身(例如 var value: Int { didSet { notify() } } 产生 value → notify)。对 call_expressionconstructor_expression 两类被调表达式分别提取名称,其中导航表达式的归一化规则在 extractNavigationName 中实现:先剥离 ?.!.(对应原文档 Key Concepts 里的可选链),再按接收者判断——super 保留全限定(super.init)、self 只取末段(self.fetchfetch)、大写开头的接收者保留限定名(Logger.info 保持 Logger.info)、小写接收者只取末段(service.fetchfetch)。

测试用例印证了这套规则的边界情况:

  • SwiftUI 结果构建器:struct AppView { var body: some View { VStack { Text("Hi") } } } 产生 body → VStackbody → Text(对应原文档 Key Concepts 的 Result Builders,见 测试用例);
  • 构造器形态的调用:User(name: "A") 记录为 make → User
  • 闭包内的调用归属外层函数:items.map { transform($0) } 中的 map/transform 都归属 build(对应原文档的 Closures);
  • 顶层无外层可调用的语句不产生调用边:let booted = bootstrap() 记录为空。

三、WASM 语法与运行链路

SwiftExtractor 依赖的 Swift 语法是项目自托管的 WASM 包 @understand-anything/tree-sitter-swift-wasm

Vendored tree-sitter-swift WASM grammar built with the modern dylink.0 ABI for use with web-tree-sitter@^0.26.

swiftConfig.treeSitter 声明了 wasmPackagewasmFiletree-sitter-swift.wasm)两个字段;语法包的构建细节记录在 BUILD.md。整个链路是:TreeSitterPlugin 加载 wasm 语法 → 按扩展名把 .swift 文件分派给 SwiftExtractorlanguageIds = ["swift"])→ 产出 functions/classes/imports/exports 与调用图 → 汇入 knowledge-graph.json 的节点与边。插件集成测试 用一个最小 SwiftUI App(@main struct MyApp: App { ... WindowGroup { ContentView() } })验证了全链路:import SwiftUI 被识别、MyAppbody 被提取为类条目、WindowGroupContentView 进入调用图。

四、小结:一份提示词素材的双重角色

回到 swift.md 本身。它只有几十行,但在 Understand-Anything 中承担了两个相互咬合的角色:

  1. 提示词角色:Phase 4 中被整体注入 architecture-analyzer## Language Context,让 LLM 用 Swift 的惯用语言(可选值、协议、入口点、Package.swift 配置、Tests/ 目录)去划分架构层、撰写摘要与 languageNotes
  2. 知识基线角色:其中的关键概念、文件模式与导入模式,在 swiftConfigconcepts/filePatternsSwiftExtractor 的解析规则中有确定性的对应实现,并有 完整测试套件 逐条锁定行为。

这种"LLM 侧语义 + 解析器侧结构"的双轨设计,正是 Understand-Anything 把任意 Swift 代码库变成可探索、可搜索、可提问的知识图谱的底层机制之一。

登录后查看全文
热门项目推荐
相关项目推荐