# sourcekit-lsp 使用指南 > 通过 Serena MCP 工具调用 sourcekit-lsp 进行 Swift/iOS 代码分析 ## 前置条件 ### 1. 确保 sourcekit-lsp 已安装 sourcekit-lsp 随 Xcode 一起安装,位于 Xcode 工具链中: ```bash # 验证 sourcekit-lsp 是否可用 xcrun sourcekit-lsp --version # 或者直接定位 xcrun --find sourcekit-lsp # 输出示例: # /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/sourcekit-lsp ``` ### 2. 项目结构要求 sourcekit-lsp 需要以下文件来识别 iOS/Swift 项目: - Xcode 项目: `*.xcodeproj` 或 `*.xcworkspace` - Swift Package: `Package.swift` - CocoaPods: `Podfile` + `*.xcworkspace` - Carthage: `Cartfile` **注意**: 对于使用 CocoaPods 的项目,必须先执行 `pod install` 生成 workspace。 --- ## 通过 Serena 使用 sourcekit-lsp ### 激活项目 ```yaml # Step 1: 激活 Swift/iOS 项目 tool: mcp__serena__activate_project params: project: "/path/to/ios/project" # 返回: 项目已激活,sourcekit-lsp 已初始化 ``` ### 验证配置 ```yaml # 检查当前配置 tool: mcp__serena__get_current_config # 确认输出包含: # - active_project: /path/to/ios/project # - language_server: sourcekit-lsp ``` --- ## 符号提取操作 ### 获取文件符号概览 ```yaml # 获取单个文件的符号列表(不含代码体) tool: mcp__serena__get_symbols_overview params: relative_path: "MyApp/ViewModels/HomeViewModel.swift" depth: 1 # 0=仅顶层, 1=含直接成员, 2=含嵌套成员 # 返回示例: # Classes: # - HomeViewModel (class) [12-187] # Structs: # - Input (struct) [15-25] # - Output (struct) [27-40] # Properties: # - disposeBag (property) [14-14] # - userService (property) [13-13] # Methods: # - transform(input:) (method) [42-120] # - fetchUserData() (method) [122-150] ``` ### 查找符号 ```yaml # 按名称模式查找符号 tool: mcp__serena__find_symbol params: name_path_pattern: "HomeViewModel" relative_path: "MyApp/ViewModels/" include_body: false include_info: true depth: 1 # 名称模式规则: # - "HomeViewModel" -> 匹配任何包含此名称的符号 # - "ViewModels/HomeViewModel" -> 匹配此路径后缀 # - "/MyApp.HomeViewModel" -> 精确匹配完整路径 # - "HomeViewModel/transform" -> 匹配类中的方法 ``` ### 获取符号详情(含代码体) ```yaml # 获取完整的符号定义(含代码) tool: mcp__serena__find_symbol params: name_path_pattern: "HomeViewModel/fetchUserData" relative_path: "MyApp/ViewModels/HomeViewModel.swift" include_body: true depth: 0 # 返回包含: # - 方法签名 # - 文档注释 # - 完整方法体 # - 行号范围 ``` --- ## 引用分析操作 ### 查找符号引用 ```yaml # 查找某个符号被哪些地方引用 tool: mcp__serena__find_referencing_symbols params: name_path: "UserService/fetchUser" relative_path: "MyApp/Services/UserService.swift" include_info: true # 返回示例: # References (3 found): # - HomeViewModel.fetchUserData [45:12-45:35] # snippet: "userService.fetchUser()" # - ProfileViewModel.loadProfile [67:8-67:31] # snippet: "userService.fetchUser()" ``` ### 模式搜索(补充 LSP) ```yaml # 当 LSP 无法找到时,使用模式搜索 tool: mcp__serena__search_for_pattern params: substring_pattern: "\.subscribe\(onNext:" paths_include_glob: "**/*.swift" restrict_search_to_code_files: true context_lines_before: 2 context_lines_after: 2 # 适用场景: # - RxSwift 订阅链 # - Protocol extension 方法 # - 动态类型调用 ``` --- ## Swift 特有处理 ### Swift 特有关键字与结构 ```yaml # Swift 特有符号类型 struct: pattern: "struct \\w+" description: "Swift 结构体,值类型" class: pattern: "class \\w+" description: "Swift 类,引用类型" enum: pattern: "enum \\w+" description: "Swift 枚举,支持关联值" protocol: pattern: "protocol \\w+" description: "Swift 协议,类似接口" extension: pattern: "extension \\w+" description: "Swift 扩展,为类型添加功能" typealias: pattern: "typealias \\w+" description: "类型别名" @propertyWrapper: pattern: "@\\w+\\s+(var|let)" description: "属性包装器" computed_property: pattern: "var \\w+: \\w+ \\{" description: "计算属性" lazy_property: pattern: "lazy var \\w+" description: "延迟初始化属性" ``` ### iOS MVVM 架构层识别 ```yaml # 根据命名约定和基类识别架构层 ViewController 层: naming: "*ViewController.swift", "*VC.swift" base_classes: - "UIViewController" - "BaseViewController" - "UITableViewController" - "UICollectionViewController" key_elements: - "viewDidLoad()" - "viewWillAppear(_:)" - "IBOutlet" - "IBAction" ViewModel 层: naming: "*ViewModel.swift", "*VM.swift" protocols: - "ViewModelProtocol" - "ViewModelType" key_elements: - "struct Input" - "struct Output" - "func transform(input:)" - "DisposeBag" Service 层: naming: "*Service.swift", "*Manager.swift" patterns: - "static let shared" - "func fetch" - "func request" key_elements: - "Observable<" - "Single<" - "Completable" Model 层: naming: "*Model.swift", "*Entity.swift", "*Response.swift" markers: - ": Codable" - ": Decodable" - ": Encodable" key_elements: - "enum CodingKeys" - "init(from decoder:)" Network 层: naming: "*API.swift", "*Router.swift", "*Target.swift" protocols: - "TargetType" - "URLRequestConvertible" key_elements: - "var baseURL: URL" - "var path: String" - "var method: Moya.Method" Coordinator 层: naming: "*Coordinator.swift", "*Navigator.swift" protocols: - "Coordinator" - "CoordinatorType" key_elements: - "var childCoordinators" - "func start()" - "weak var parentCoordinator" Extension 层: naming: "*+*.swift" description: "Swift extension 文件 (e.g., String+Extensions.swift)" patterns: - "extension \\w+ \\{" - "extension \\w+: \\w+ \\{" ``` ### Moya API 识别 ```yaml # Moya TargetType 枚举识别 TargetType 协议: description: "Moya 网络请求定义协议" required_properties: - "baseURL: URL" - "path: String" - "method: Moya.Method" - "task: Task" - "headers: [String: String]?" # 提取示例 tool: mcp__serena__search_for_pattern params: substring_pattern: "case\\s+\\w+.*\\n.*var path" paths_include_glob: "**/*API.swift" multiline: true # 解析 Moya Target # enum UserAPI: TargetType { # case login(phone: String, code: String) # case fetchProfile(userId: Int) # # var path: String { # switch self { # case .login: # return "/api/v1/user/login" # case .fetchProfile(let userId): # return "/api/v1/user/\(userId)" # } # } # } # # 提取: # - case: login # path: /api/v1/user/login # method: POST (从 method 属性推断) # params: [phone: String, code: String] # # - case: fetchProfile # path: /api/v1/user/{userId} # method: GET # params: [userId: Int] ``` ### Alamofire 请求识别 ```yaml # Alamofire 请求模式 AF.request: description: "Alamofire 5.x 请求" pattern: "AF\\.request\\(" example: | AF.request("https://api.example.com/users", method: .get, parameters: params, encoding: URLEncoding.default) .responseDecodable(of: UserResponse.self) { response in // handle response } session.request: description: "Session 实例请求" pattern: "session\\.request\\(" URLRequestConvertible: description: "自定义请求构建器" protocol: "URLRequestConvertible" method: "asURLRequest() throws -> URLRequest" ``` ### RxSwift/RxCocoa 模式 ```yaml # RxSwift 核心类型 rx_types: Observable: description: "可观察序列,核心类型" operators: ["map", "flatMap", "filter", "subscribe"] Single: description: "单值序列,成功或失败" operators: ["subscribe", "map", "flatMap"] Completable: description: "无值序列,仅完成或失败" operators: ["subscribe", "andThen"] Maybe: description: "可选单值序列" operators: ["subscribe", "map"] Driver: description: "UI 绑定专用,主线程、无错误、共享" from: "asDriver()" Signal: description: "类似 Driver,但不重放" from: "asSignal()" # RxSwift Subject 类型 rx_subjects: PublishSubject: description: "无初始值,只发送新事件" usage: "事件总线" BehaviorSubject: description: "有初始值,发送最新值" usage: "状态管理" ReplaySubject: description: "缓存指定数量事件" usage: "历史事件回放" PublishRelay: description: "PublishSubject 无 error/complete" usage: "UI 事件转发" BehaviorRelay: description: "BehaviorSubject 无 error/complete" usage: "状态绑定" # 常用操作符 rx_operators: transformation: - "map" - "flatMap" - "flatMapLatest" - "compactMap" - "scan" filtering: - "filter" - "distinctUntilChanged" - "debounce" - "throttle" - "skip" - "take" combining: - "merge" - "combineLatest" - "zip" - "withLatestFrom" - "concat" error_handling: - "catchError" - "catchErrorJustReturn" - "retry" - "retryWhen" utility: - "do(onNext:)" - "delay" - "observeOn" - "subscribeOn" - "share" - "replay" # 提取 RxSwift 模式 tool: mcp__serena__search_for_pattern params: substring_pattern: "\\.(map|flatMap|filter|subscribe)\\s*\\{" paths_include_glob: "**/*.swift" ``` ### Combine 模式(Swift 原生响应式) ```yaml # Combine 核心类型 combine_types: Publisher: description: "发布者协议" operators: ["map", "flatMap", "sink", "assign"] AnyPublisher: description: "类型擦除发布者" usage: "API 返回类型" PassthroughSubject: description: "类似 PublishSubject" usage: "事件发送" CurrentValueSubject: description: "类似 BehaviorSubject" usage: "状态管理" @Published: description: "属性包装器,自动发布变化" usage: "SwiftUI/Combine 状态" AnyCancellable: description: "订阅句柄" usage: "生命周期管理" # 识别 Combine 使用 tool: mcp__serena__search_for_pattern params: substring_pattern: "@Published|PassthroughSubject|CurrentValueSubject|\\.sink\\(" paths_include_glob: "**/*.swift" ``` --- ## LSP SymbolKind 映射 ```yaml # sourcekit-lsp 返回的 SymbolKind 数值对照 1: File 2: Module 3: Namespace # extension 4: Package 5: Class # class 6: Method # func (instance) 7: Property # var/let (instance) 8: Field 9: Constructor # init 10: Enum # enum 11: Interface # protocol 12: Function # func (top-level/static) 13: Variable # var/let (local) 14: Constant # let (constant) 15: String 16: Number 17: Boolean 18: Array 19: Object 20: Key 21: Null 22: EnumMember # enum case 23: Struct # struct 24: Event 25: Operator 26: TypeParameter # associated type / generic ``` --- ## 调用链构建 ### ViewController -> ViewModel 调用链 ```python # 伪代码: 构建 MVVM 调用链 def build_mvvm_chain(viewcontroller_path): # Step 1: 获取 ViewController 符号 vc_symbols = get_symbols_overview(viewcontroller_path, depth=2) # Step 2: 识别 ViewModel 依赖 # 查找类似: private let viewModel = HomeViewModel() # 或: private var viewModel: HomeViewModelType! vm_deps = extract_viewmodel_deps(vc_symbols) # Step 3: 追踪 ViewModel 绑定 for vm in vm_deps: # 获取 ViewModel 定义 vm_symbols = get_symbols_overview(vm.file_path, depth=2) # 查找 Input/Output 模式 if has_input_output_pattern(vm_symbols): input_struct = find_symbol("Input", vm.file_path) output_struct = find_symbol("Output", vm.file_path) transform_method = find_symbol("transform", vm.file_path, include_body=True) # 追踪 Service 调用 service_calls = extract_service_calls(transform_method.body) return chain ``` ### ViewModel -> Service -> API 调用链 ```yaml # 实际操作步骤 # 1. 获取 ViewModel 方法 tool: mcp__serena__find_symbol params: name_path_pattern: "HomeViewModel/fetchUserData" relative_path: "MyApp/ViewModels/" include_body: true # 2. 分析方法体中的 Service 调用 # 从返回的 body 中提取: userService.fetchUser() # 3. 定位 Service 方法 tool: mcp__serena__find_symbol params: name_path_pattern: "UserService/fetchUser" relative_path: "MyApp/Services/" include_body: true # 4. 分析 Service 中的 API 调用 # 从返回的 body 中提取: provider.request(.fetchProfile(userId)) # 5. 定位 Moya Target tool: mcp__serena__search_for_pattern params: substring_pattern: "case fetchProfile" paths_include_glob: "**/*API.swift" context_lines_after: 10 # 6. 提取端点信息 # case fetchProfile(userId: Int) # path: "/api/v1/user/\(userId)" # method: .get ``` --- ## 大规模项目处理 ### 项目规模评估 ```bash # 评估 Swift 项目规模 find /path/to/project -name "*.swift" -not -path "*/Pods/*" -not -path "*/Carthage/*" | wc -l # 输出: 3602 (文件数) find /path/to/project -name "*.swift" -not -path "*/Pods/*" -not -path "*/Carthage/*" -exec wc -l {} + | tail -1 # 输出: 424000 (总行数) ``` ### 分批处理策略 ```yaml # 大规模项目(424K 行)处理策略 batch_processing: description: "按模块分批处理,避免内存溢出" # Step 1: 按目录分组 grouping: - "AppDelegate.swift" # 入口点优先 - "**/Coordinator/**/*.swift" # 导航层 - "**/ViewModel/**/*.swift" # ViewModel 层 - "**/Service/**/*.swift" # Service 层 - "**/Network/**/*.swift" # Network 层 - "**/Model/**/*.swift" # Model 层 - "**/View/**/*.swift" # View 层 - "**/Extension/**/*.swift" # Extension 层 - "**/*.swift" # 其他 # Step 2: 批次配置 batch_config: batch_size: 100 # 每批 100 个文件 parallel_batches: 3 # 最多 3 个并行批次 checkpoint_interval: 50 # 每 50 个文件保存检查点 # Step 3: 内存管理 memory_management: release_after_batch: true # 每批处理后释放内存 stream_output: true # 流式写入输出文件 # 处理流程 procedure: - step: "scan_and_group" description: "扫描文件并按层分组" - step: "process_core_first" description: "优先处理核心文件" files: - "AppDelegate.swift" - "SceneDelegate.swift" - "*Coordinator.swift" - step: "batch_process" description: "分批处理各层" for_each_batch: "grouped_files | batch(100)" actions: - "extract_symbols" - "analyze_layer" - "save_checkpoint" - step: "merge_results" description: "合并所有批次结果" ``` ### Pod 模块边界识别 ```yaml # 分析 Podfile 识别模块边界 # Step 1: 读取 Podfile tool: Read params: file_path: "/path/to/project/Podfile" # Step 2: 提取 Pod 依赖 patterns: - "pod '([^']+)'(?:,\\s*'([^']+)')?" - "pod \"([^\"]+)\"(?:,\\s*\"([^\"]+)\")?" # Step 3: 分类 Pod categories: networking: - "Alamofire" - "Moya" - "AFNetworking" - "Kingfisher" - "SDWebImage" reactive: - "RxSwift" - "RxCocoa" - "RxRelay" - "RxDataSources" - "RxGesture" ui: - "SnapKit" - "Masonry" - "MBProgressHUD" - "SVProgressHUD" - "MJRefresh" database: - "Realm" - "FMDB" - "GRDB" - "CoreStore" analytics: - "Firebase" - "Bugly" - "UMAnalytics" testing: - "Quick" - "Nimble" - "RxTest" - "RxBlocking" # Step 4: 分析模块边界 module_boundaries: description: "识别 Pod 模块边界" analysis: - "哪些模块依赖 RxSwift" - "网络层使用哪个库" - "UI 组件库选型" ``` --- ## 常见问题 ### Q: sourcekit-lsp 启动慢 ``` A: sourcekit-lsp 首次索引项目可能较慢: - 大型项目(3600+ 文件)可能需要几分钟 - 确保 Xcode 和 Command Line Tools 已安装 建议: 先执行 xcodebuild 构建项目预热索引 # 预热命令 xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug build ``` ### Q: 找不到 Swift 符号 ``` A: 可能原因: 1. 项目未正确配置(缺少 xcworkspace) 2. CocoaPods 未安装(先执行 pod install) 3. 符号在 Pods 目录中(被排除) 4. Swift Package 未解析 解决: 1. 确保使用 .xcworkspace 而非 .xcodeproj 2. 执行 pod install 生成 workspace 3. 使用 search_for_pattern 作为备选 4. 对于 SPM 项目,确保 Package.resolved 存在 ``` ### Q: RxSwift 调用追踪不完整 ``` A: RxSwift 的链式调用使得静态分析困难: 1. 操作符链可能跨越多行 2. 闭包中的调用难以追踪 3. 协议扩展方法无法直接定位 解决: 使用 search_for_pattern 搜索特定操作符模式 结合 Input/Output 模式分析 ViewModel 追踪 DisposeBag 的使用位置 ``` ### Q: Extension 方法找不到 ``` A: Swift extension 方法分散在多个文件中: 1. extension 文件通常命名为 Type+Category.swift 2. 同一类型可能有多个 extension 文件 3. Protocol extension 更难追踪 解决: 1. 先搜索 extension TypeName 识别所有扩展 2. 使用 Glob 匹配 *+*.swift 文件 3. 分析 Protocol extension 时搜索协议名 ``` ### Q: Moya Target 解析 ``` A: Moya TargetType 是 enum,需要特殊处理: 1. case 定义了 API 端点 2. path/method/task 等属性需要配合分析 3. switch self 模式匹配需要解析 解决: 1. 先提取所有 case 定义 2. 分析 path 属性的 switch 语句 3. 匹配 case 与 path 的对应关系 # 示例搜索 mcp__serena__search_for_pattern( substring_pattern="case \\w+.*\\n.*path:", paths_include_glob="**/*API.swift", multiline=True, context_lines_after=5 ) ``` --- ## 最佳实践 1. **按架构层分析** - 先识别 MVVM 各层文件,再逐层深入 2. **ViewModel 是关键** - ViewModel 层连接 ViewController 和 Service,是分析重点 3. **Input/Output 模式** - 识别 RxSwift MVVM 的标准模式 4. **Pod 依赖优先** - 通过 Podfile 快速了解项目技术栈 5. **Extension 归类** - 将 extension 文件与原类型关联 6. **分批处理大项目** - 424K 行项目必须分批处理 --- ## 项目特定模式(示例:chefu_driver_app_ios) 该项目使用的技术栈和模式: ```yaml 架构模式: MVVM + Coordinator ViewController: *ViewController.swift ViewModel: *ViewModel.swift (Input/Output 模式) Service: *Service.swift (单例模式) Coordinator: *Coordinator.swift 网络层: Moya + RxSwift API 定义: *API.swift (TargetType enum) Provider: MoyaProvider 响应类型: Observable 响应式框架: RxSwift + RxCocoa ViewModel: Observable/Driver ViewController: DisposeBag, bind/drive UI 框架: 布局: SnapKit 图片: Kingfisher 刷新: MJRefresh 项目规模: 文件数: 3,602 Swift 文件 代码行数: 约 424,000 行 Pod 依赖: 50+ 个 Pod 目录结构: MyApp/ AppDelegate.swift Coordinator/ # 导航协调器 Modules/ # 按功能模块划分 Home/ ViewController/ ViewModel/ View/ Model/ Order/ ... Services/ # 业务服务 Network/ # 网络层 API/ Model/ Common/ # 公共组件 Base/ Extension/ Utils/ ```