[控场AI]
· 7 分钟阅读· 3,762 字

TUI还是GUI?TJ用Neovim打造的原生图形界面实验

TUI还是GUI?TJ用Neovim打造的原生图形界面实验

TJ DeVries为Neovim构建原生GPU渲染GUI,演示混合架构范式与TUI的边界

Neovim核心开发者TJ DeVries在一期开发者节目中展示了一个实验性项目:基于Zed的GPUI框架为Neovim构建原生GPU渲染的图形界面,形成C+Rust+TypeScript+Lua的四语言技术栈。该项目利用Neovim本身将编辑逻辑与界面绘制解耦的架构设计,实现了纯TUI无法完成的功能:跨文件引用箭头、Markdown实时双向同步预览、拖拽式配色编辑器,以及通过Cursor SDK daemon实现的多实例AI对话同步。节目中,主持人围绕"实用性"与"GUI感"展开争论,最终将项目评定为6-7分——已越过GUI门槛,但仍有深化空间。这个仅用不到10天完成的实验,指向了一种值得关注的混合架构方向:成熟编辑内核+高性能渲染框架+可热重载脚本层。

在一期以"TUI or GUI"为主题的开发者节目中,Neovim核心开发者TJ DeVries展示了一个颇具实验性的项目:给Neovim套上一层原生GPU渲染的图形界面。这个项目由Prime、Casey、Trash等人现场"评级",衡量它到底算终端界面(TUI)还是真正的图形界面(GUI)。抛开节目里大量的玩笑和"脑腐模拟器",这个项目背后其实揭示了Neovim架构设计的一个核心思路,以及现代编辑器混合技术栈的一种可能形态。

Neovim为什么天生适合被"嵌入"

TJ在节目中反复强调,让Neovim拥有一个GUI在"现在这个年头"应该是很容易的事。原因在于Neovim的设计初衷之一,就是把"绘制界面"和"编辑逻辑"解耦。

用他的话说,Neovim可以运行在一种模式下——它不再自己负责往终端里画字符,而是告诉外部宿主:"我知道我被嵌入到别的东西里了,我不画东西,我只把Neovim的状态告诉你,你把发生的事件发给我,我处理完再把更新后的状态返回给你。"这是一种典型的状态与事件驱动的外部UI协议。

他还提到了一个历史项目Firenvim,它能让你在网页输入框里直接打开Neovim。比如你要在GitHub上写一段带代码格式的长回复,Firenvim会把Neovim嵌进那个文本框里,编辑完再写回去。Prime当场调侃:"所以你可能会被困在一个网页文本框里出不来。"这个玩笑恰好点出了Neovim嵌入能力的边界——它把绘制职责完全交给外部GUI来处理,自己只管编辑状态。

这种解耦机制在Neovim中通过msgpack-RPC协议实现。Neovim启动时可以以--embed模式运行,此时它不会尝试控制终端,而是通过标准输入输出与外部宿主进程通信。宿主通过RPC调用Neovim的API(如nvim_input发送按键、nvim_command执行命令),Neovim则通过UI事件通知宿主界面变化(如grid_linegrid_scroll等)。这套协议的设计意味着任何语言只要能做进程间通信,都能成为Neovim的前端——这也是Neovide(Rust GUI)、FVim(.NET GUI)、Firenvim(浏览器扩展)能够存在的根本原因。与Vim依赖终端控制序列的架构相比,这是一次根本性的设计转变:Neovim的"界面"在架构层面从一开始就是可替换的插件,而非硬编码的实现。

技术栈:C + Rust + TypeScript + Lua的四语言混搭

这套界面看起来挺酷

这个GUI项目建立在GPUI之上——也就是Zed编辑器开源的那套Rust即时模式(immediate mode)图形框架。节目里梳理出了一个相当"缝合"的技术栈:

  • C:Neovim本体
  • Rust:基于GPUI的GUI渲染层
  • TypeScript/JavaScript:通过Bun运行的插件子系统
  • Lua:Neovim的原生配置语言

Casey在节目里逐一确认这套组合时,现场笑作一团。但这个混搭并非随意为之。TJ解释,Bun子系统的价值在于热重载:他可以在不重新编译整个编辑器的情况下,修改GUI插件(widget)的行为。换句话说,底层系统用Rust构建,而具体的覆盖层UI、对话框、交互组件可以用JavaScript快速迭代。

他的取舍逻辑很务实:"如果某个东西我们要做上千次、而且不再变化了,那就把JavaScript代码删掉,用Rust重写让它更快。"这是一种典型的"先用脚本语言探索、稳定后下沉到系统语言"的开发路径。

GPUI是Zed团队为其编辑器自研的即时模式(immediate mode)UI框架,以Rust编写,直接调用Metal(macOS)或Vulkan/WebGPU等图形API进行GPU加速渲染,完全绕开系统原生UI控件。与传统的保留模式(retained mode)框架(如Qt、GTK)不同,即时模式框架在每一帧都重新描述整个UI状态,由框架自行决定哪些部分需要重绘——这与React的虚拟DOM思路有相似之处,但发生在GPU渲染层。GPUI的优势在于延迟极低、对自定义渲染效果没有框架层面的限制,代价是开发者需要手动管理更多的渲染细节。Zed在2024年开源后,GPUI作为独立crate发布,使其他项目(如本文提及的Neovim GUI实验)能够复用这套渲染基础设施,而无需从零构建GPU管线。

GUI能做而TUI做不到的那些事

这些都是cursor的聊天记录

节目里演示了一系列纯TUI难以实现的功能。最获好评的是引用箭头——当你查看某个符号的引用时,界面会直接画出箭头指向代码中被使用的位置,可以跨越整个大文件。Casey此前专门要求过这个功能,TJ形容"TUI永远做不到(A TUI could never)"。原因很直接:终端只能往字符网格里画一样东西,无法叠加图层,也无法在文字之上叠加图形。

其他演示的GUI特性包括:

  • Markdown实时预览:浮动窗口渲染图片、清单、代码高亮,还能显示序列图和表格,且与原buffer实时双向同步。TJ特意吐槽"在Vim里读表格几乎是不可能的",而LLM又特别爱输出表格和序列图。
  • 原生右键菜单:可以放置那些每月才用一次、不值得记快捷键的操作。
  • 实时配色方案编辑器:拖动即改、无延迟,TJ顺带调侃"VS Code做不到这个"。
  • 可交互的Cursor聊天集成:一个后台daemon运行Cursor SDK,让多个Neovim实例、以及浏览器视图之间同步对话状态。

关于渲染管线,TJ给出了一个清晰的说明:Neovim在字节变化时有回调,触发Tree-sitter重新解析,解析结果发给GUI层,再由GPUI渲染。整套流程强调速度——"如果我要vibe code它,那它至少得快。"

Tree-sitter是一个增量式语法解析库,最初由GitHub开发,后被Neovim深度集成。它的核心优势在于"增量":当文件内容发生变化时,Tree-sitter不会重新解析整个文件,而是只更新受影响的语法树节点,因此即便是数万行的大文件,每次按键后的重新解析也能在毫秒级完成。在上述渲染管线中,Tree-sitter的解析结果(具体语法树)是高亮、折叠、引用跳转等功能的数据基础——GUI层绘制引用箭头时,需要从Tree-sitter获取精确的符号位置信息,而非依赖正则表达式匹配。这也解释了为何该架构能够在字节变化时触发回调并快速刷新渲染:Tree-sitter的增量特性使得"编辑→解析→渲染"这条链路的延迟足够低,不会成为性能瓶颈。

争论焦点:什么才算真正的GUI

当然可以

这期节目最有意思的部分,其实是几位主持人对"GUI标准"的分歧。TJ坚持从功能有用性角度展示,而Casey明确反对用"实用性"来评判:"GUI的重点不在于它有用,而在于它做一些奇怪的事来证明自己是个GUI。"

这个看似玩笑的观点背后藏着一层真实的产品哲学讨论。评审们指出了这个项目的几个短板:

  • 大量功能只是"弹出式模态窗口",浮在编辑器之上,缺乏"真正嵌入"的一体感;
  • 没有动画过渡,几位主持人(半开玩笑地)认为动画是GUI的标志;
  • Cursor聊天窗口"看起来就像把终端直接搬上来",没有利用图形能力做圆角、箭头等TUI做不到的细节。

Prime甚至列出一个反讽的"GUI必备特性":可见的输入延迟。"它太快了,让我感觉在用TUI,你得在按键上加个100毫秒延迟。"这套调侃反过来点明了一个真实观察——很多所谓GUI编辑器(比如VS Code)恰恰以卡顿和延迟为代价换取了图形丰富度。

最终这个项目被评为"6到7分"(0为纯TUI,10为纯GUI),公认它"越过了门槛,确实是个GUI了",但GUI化程度还能走得更远。TJ自己坦言这个项目只做了"不到10天"。

一个未被节目点破的行业信号

当Trash问到"还有别的编辑器有这些能力吗",TJ的回答很坦诚:"Emacs大概每一个功能都有,我做的只是触及了Emacs用户早就做过的东西的表面——只不过他们是每秒0.001帧。"这句玩笑其实指向了一个长期存在的权衡:可扩展性与性能往往难以兼得。

这个实验的真正意义,或许不在于"Neovim应不应该有GUI",而在于它演示了一种混合架构范式:用成熟的编辑内核(Neovim)+ 高性能渲染框架(GPUI/Rust)+ 可热重载脚本层(Bun/TS)组合,在保持编辑逻辑不变的前提下,叠加图形能力。对于关注编辑器技术演进和AI辅助编程(节目里大量集成了Cursor SDK)的开发者来说,这是一个值得留意的方向。

分享:

相关推荐