
代码编辑器开发工具【免费下载链接】vimrVimR — Neovim GUI for macOS in Swift项目地址https://gitcode.com/gh_mirrors/vi/vimr点击查看免费下载NvimApi 是 VimR 项目macOS 上的 Neovim GUI使用 Swift 编写中独立成包的 Swift 库负责通过 msgpack-rpc 协议与 Neovim 实例通信。本文以 NvimApi/README.md 的生成流程为核心结合仓库内的生成脚本与源码实现完整讲解如何为指定 Neovim 分支ref重新生成 Swift API 绑定文件、生成流程的每一步做了什么以及生成的异步 / 同步 API 背后是何种消息层机制。读完本文你将掌握 NvimApi 的源码再生成方法并理解从nvim --api-info元数据到类型安全 Swift 方法调用的完整链路。一、为什么 NvimApi 需要生成而不是手写Neovim 的 API 面nvim_*/vim_*函数会随版本演进不断变化。如果为每个 Neovim 版本手工维护一份 Swift 绑定既容易遗漏又难以同步。NvimApi 的做法是用脚本从 Neovim 自身导出的 API 元数据自动生成 Swift 源码异步 API 生成到Sources/NvimApi/NvimApi.generated.swift同步 API 生成到Sources/NvimApi/NvimApiSync.generated.swift。仓库里两份生成文件的行首都注明了来源版本// Auto generated for nvim version 0.12.5. // See bin/generate_api_methods.py也就是说生成文件与当时构建的 Neovim 版本严格对应。因此 README 开宗明义先切换到正确的 refdevelop 或 update-neovim再执行生成脚本以保证绑定与目标 Neovim 源码一致。二、核心操作checkout ref 后执行生成脚本NvimApi/README.md 给出的完整流程只有两步# 1. 检出正确的 ref例如 develop 或 update-neovim git checkout develop # 或 git checkout update-neovim # 2. 生成 API Swift 文件 ./bin/generate_sources.sh这里的./bin/generate_sources.sh指的是仓库中 NvimApi/bin/generate_sources.sh。脚本核心逻辑如下#!/bin/bash set -Eeuo pipefail readonly clean${clean:?true or false} readonly NVIM_PATH../Neovim/build/bin/nvim build_nvimserver_bin() { ./bin/neovim/bin/build_neovim_for_dev.sh } main() { pushd $(dirname ${BASH_SOURCE[0]})/../.. /dev/null echo Make sure you have the correct ref checked out; develop or update-neovim. build_nvimserver_bin pushd NvimApi /dev/null ./bin/generate_async_api_methods.py ./bin/generate_sync_api_methods.py swiftformat ./Sources/NvimApi/NvimApi.generated.swift \ ./Sources/NvimApi/NvimApiSync.generated.swift popd /dev/null popd /dev/null } main要点拆解脚本首先调用./bin/neovim/bin/build_neovim_for_dev.sh构建开发版 Neovim server目标二进制为../Neovim/build/bin/nvim保证随后读取的--api-info元数据来自当前检出的 ref然后依次运行两个 Python 生成器产出异步与同步两份绑定文件最后用swiftformat统一格式化生成代码。此外仓库根目录还有一个更高层的入口 bin/generate_sources.sh它除了 NvimApi 之外还会基于 Neovim 的CMakeLists.txt中的版本号NVIM_VERSION_MAJOR/MINOR/PATCH/PRERELEASE生成 NvimView 的 autocmd 与光标形状文件cleanfalse ./NvimApi/bin/generate_sources.sh它接受clean与use_committed_nvim两个环境变量use_committed_nvimtrue时先git submodule update使用仓库记录的 Neovim 版本否则直接使用 workspace 中的 Neovim 源码。三、生成器内部nvim --api-info元数据如何变成 Swift 方法两份 Python 生成器generate_async_api_methods.py 与 generate_sync_api_methods.py的数据来源一致调用当前 Neovim 可执行文件的--api-info输出 msgpack 二进制元数据nvim_path os.environ[NVIM_PATH] if NVIM_PATH in os.environ else nvim nvim_output subprocess.run([nvim_path, --api-info], stdoutsubprocess.PIPE) api msgpack.unpackb(nvim_output.stdout, rawFalse)解析出的api字典包含三块关键信息字段用途version写入生成文件头部的 Neovim 版本号functions逐一生成 Swift 方法的 API 函数清单error_types/types生成NvimApi.Error枚举与 Buffer / Window / Tabpage 的 EXT 类型 id方法名的转换规则在 api_generator_common.py 的snake_to_camel中实现蛇形命名转驼峰如nvim_get_current_buf→nvimGetCurrentBuf。Neovim 类型到 Swift 类型的映射nvim_type_to_swift是理解绑定代码的关键Neovim 类型Swift 类型BooleanBoolIntegerIntFloatFloatStringStringArray[NvimApi.Value]DictDictionaryString, NvimApi.ValueBuffer/Window/TabpageNvimApi.Buffer/NvimApi.Window/NvimApi.TabpageObjectNvimApi.ValueArrayOf(T, ...)[T]递归映射voidVoid值得一提的细节nvim --api-info对 list 类函数如nvim_list_bufs只标注普通Array返回类型但实际 wire 上是 msgpack EXT 值Buffer / Window / Tabpage。生成器通过RETURN_TYPE_OVERRIDES修正这一信息差例如RETURN_TYPE_OVERRIDES { nvim_list_bufs: [NvimApi.Buffer], nvim_list_wins: [NvimApi.Window], nvim_list_tabpages: [NvimApi.Tabpage], nvim_tabpage_list_wins: [NvimApi.Window], }未被认领的新 list 函数会触发warn_if_missing_override警告提醒维护者补充 override 或加入KNOWN_NON_EXT_LIST_FUNCS。此外deprecated_since字段存在时生成的方法会自动加上available(*, deprecated)注解。四、异步 APINvimApi与 Result 风格调用生成的异步 API 以extension NvimApi的方式挂载到 NvimApi.swift 的 actor 上方法统一返回ResultT, NvimApi.Error。以 NvimApi.generated.swift 中的nvimBufLineCount为例func nvimBufLineCount( buf: NvimApi.Buffer, errWhenBlocked: Bool true ) async - ResultInt, NvimApi.Error { let params: [NvimApi.Value] [ .int(Int64(buf.handle)), ] func transform(_ value: NvimApi.Value) throws(NvimApi.Error) - Int { guard let result (value.intValue) else { throw NvimApi.Error.conversion(type: Int.self) } return result } if errWhenBlocked, let error await self.blockedError() { return .failure(error) } let reqResult await self.sendRequest(method: nvim_buf_line_count, params: params) switch reqResult { case let .success(value): return Result { () throws(NvimApi.Error) - Int in try transform(value) } case let .failure(error): return .failure(.other(cause: error)) } }可见每个生成方法遵循统一模板将 Swift 参数编码为[NvimApi.Value]如 Buffer 编码为.int(Int64(handle))调用底层sendRequest发送method名与参数用transform把返回的NvimApi.Value解码为目标类型解码失败返回.conversion(type:)错误无返回值的方法void类型则生成expectsReturnValue: Bool false参数仅在需要返回值时才做blockedError()检查。异常与校验错误由生成器从api[error_types]映射为NvimApi.Error枚举public enum Error: Swift.Error, Sendable { public static let exceptionRawValue UInt64(0) public static let validationRawValue UInt64(1) case exception(message: String) case validation(message: String) case blocked case conversion(type: Any.Type) case other(cause: Swift.Error) case other(description: String) case unknown }五、同步 APINvimApiSync与阻塞式请求NvimApiSync.swift 是只支持 request 的同步封装适用于不需要事件流、希望调用简单阻塞的场景。其sendRequest的实现展示了最朴素的 msgpack-rpc 请求-响应循环public func sendRequest( method: String, params: [MsgpackRpc.Value] ) - ResultMsgpackRpc.Value, NvimApi.Error { self.lock.lock() defer { lock.unlock() } self.msgId 1 let request: [MessagePackValue] [ .uint(MsgpackRpc.MessageType.request.rawValue), .uint(UInt64(self.msgId)), .string(method), .array(params), ] let data MessagePack.pack(.array(request)) do { guard let socket self.socket else { return .failure(.exception(message: NvimApiSync: not connected)) } try socket.write(from: data) var response Data() _ try socket.read(into: response) let decoded try MessagePack.unpack(response) guard case let .array(unpacked) decoded.value, unpacked.count 4, case .uint(MsgpackRpc.MessageType.response.rawValue) unpacked[0] else { return .failure(.exception(message: Invalid response)) } guard unpacked[2].isNil else { return .failure(.other(cause: NvimApi.Error(unpacked[2]))) } return .success(unpacked[3]) } catch { return .failure(.other(cause: error)) } }同步连接与异步一样支持三种方式run(socketPath:)、run(host:port:)以及自动识别 Unix socket 或 TCP 地址的run(address:)。同步版本用OSAllocatedUnfairLock保护 socket 与 msgId 的自增保证线程安全。六、底层消息层MsgpackRpc的协议实现NvimApi/Sources/NvimApi/MsgpackRpc.swift 是异步 API 的通信内核以 Swift actor 实现。它对 msgpack-rpc 三种消息类型的定义如下public enum MessageType: UInt64 { case request 0 case response 1 case notification 2 }请求帧为 4 元组[type, msgid, method, params]响应帧为 4 元组[type, msgid, error, result]通知帧为 3 元组[type, method, params]。MsgpackRpc.request发送请求后若expectsReturnValue true会通过withCheckedContinuation将 msgid 挂起到pendingRequests字典等待对应响应if !expectsReturnValue { return .nilResponse(msgid) } return await withCheckedContinuation { continuation in self.pendingRequests[msgid] continuation }processMessage则按类型分发响应帧唤醒挂起的 continuation通知帧如nvim_ui_attach后的 redraw 事件进入公开的messagesStreamAsyncStreamMessagerequest 帧Neovim 反向调用同样进入流中交由上层处理。Response结构还提供了便捷判断public var isSuccess: Bool { self.error.isNil } public var isError: Bool { !self.isSuccess }传输层支持两种通道管道piperun(inPipe:outPipe:errorPipe:)用于--embed模式Neovim 作为子进程被嵌入宿主利用readabilityHandler将管道数据转成AsyncStreamDatasocketrun(socketPath:)/run(host:port:)/run(address:)连接已运行实例address形如/tmp/nvim.sockUnix socket或localhost:6666TCP端口范围 1...65535。无论哪种通道unpackAllWithRemainder都会把累积的字节流按 msgpack 边界拆解成多条完整消息MessagePackError.insufficientData表示半包数据留待下一轮补齐——这正是流式协议粘包/拆包的标准处理方式。七、Buffer / Window / Tabpage 的 EXT 解码NvimApi.Buffer、NvimApi.Window、NvimApi.Tabpage在 wire 上都是 msgpack EXT 类型。生成器在NvimApi.generated.swift中为三者各写了一段 failable 初始化器例如 Bufferpublic init?(_ value: NvimApi.Value) { guard let (type, data) value.extendedValue else { return nil } guard type buffer 的 EXT type id else { return nil } guard let handle (try? unpack(data))?.value.int64Value else { return nil } self.handle Int(handle) }EXT 的 type id 直接取自api[types][Buffer][id]与目标 Neovim 版本严格绑定。生成代码中对应的用法如nvimListBufsfunc nvimListBufs( errWhenBlocked: Bool true ) async - Result[NvimApi.Buffer], NvimApi.Error { let params: [NvimApi.Value] [] func transform(_ value: NvimApi.Value) throws(NvimApi.Error) - [NvimApi.Buffer] { guard let result (value.arrayValue?.compactMap({ v in NvimApi.Buffer(v) })) else { throw NvimApi.Error.conversion(type: [NvimApi.Buffer].self) } return result } // ... sendRequest(method: nvim_list_bufs, params: params) }而在发请求时这些类型对象会被编码回.int(Int64(handle))与 Neovim 侧句柄语义一致。八、调用示例与测试验证仓库测试目录提供了两段可直接对照学习的示例代码NvimApiExample.swift异步 API 示例。先以--embed启动 nvimnvimUiAttach(width:height:options:)挂接 UI循环nvimExec2执行命令、nvimBufLineCount统计行数、nvimInput发送翻页键最后nvimUiDetach退出let stream await self.api.msgpackRawStream try await self.api.run(inPipe: inPipe, outPipe: outPipe, errorPipe: errorPipe) Task.detached { for await msg in stream { switch msg { case let .notification(method, params): print(NOTIFICATION: \(method): array of \(params.count) elements) case let .error(value, msg): print(ERROR: \(msg) with \(value)) case let .response(msgid, error, result): print(RESPONSE: \(msgid), \(error), \(result)) default: break } } }NvimApiSyncTest.swift同步 API 示例。以--listen /tmp/nvim-api-sync-test.socket启动独立 nvim 进程再通过 socket 连接并调用nvimCommand、nvimGetVvar、nvimBufLineCount。MsgpackRpcNeovimExample.swift直接使用底层MsgpackRpc的示例展示不经过生成 API、手工拼接 msgpack-rpc 请求如nvim_ui_attach、nvim_exec2、nvim_input的写法适合理解协议最原始的形态。九、生成后的验证与再生成注意事项重新生成后应检查以下几点确认版本头NvimApi.generated.swift与NvimApiSync.generated.swift头部Auto generated for nvim version ...应匹配你检出的 Neovim 版本确认 EXT 类型 idBuffer / Window / Tabpage 的 EXT id 来自同一份--api-info切换版本后必须重新生成否则句柄解码会失败处理新 API 的告警generate_async_api_methods.py运行时会向 stderr 打印新出现 list 函数缺少 override 的警告需要按提示补充RETURN_TYPE_OVERRIDES或KNOWN_NON_EXT_LIST_FUNCS条目依赖与平台NvimApi 通过 NvimApi/Package.swift 依赖MessagePack.swift 5.0.0、BlueSocket 2.1.0与本地Commons包平台要求 macOS 14Swift 工具链 6.0生成与构建脚本面向 macOS 开发环境脚本中使用了gsed等工具。以当前仓库为例生成文件对应 Neovim 0.12.5。当你需要跟进 Neovim 新特性例如update-neovim分支时只需切换 ref 后重新执行生成脚本即可获得与目标版本完全同步的 Swift 绑定——这正是 NvimApi 保持与 Neovim API 同步演进的核心机制。赞分享代码编辑器开发工具【免费下载链接】vimrVimR — Neovim GUI for macOS in Swift项目地址https://gitcode.com/gh_mirrors/vi/vimr点击查看免费下载相关推荐Slang 绑定生成测试全解析如何为无显式绑定的着色器自动生成 register 语义Slang 绑定生成测试全解析如何为无显式绑定的着色器自动生成 register 语义 Shader 参数的绑定位置如 HLSL 的 register t0编译器图形学编程语言告别手动绑定windows-rs代码生成器如何自动创建Windows API绑定告别手动绑定windows rs代码生成器如何自动创建Windows API绑定 windows rs是一个为Rust开发者提供Windows API绑定的强开发工具3分钟上手B站视频批量下载BilibiliDown完全指南3分钟上手B站视频批量下载BilibiliDown完全指南 你是否曾因为网络不稳定而无法流畅观看B站视频是否想将收藏夹里的优质内容一次性保存到本地或者想要音视频桌面应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考