博客 / WEEKLY #005

一次 Markdown 编辑器换内核的尝试

渲染层和源码是两套状态,是所见即所得编辑器一类顽固问题的根源。一次从 Milkdown 迁到 CodeMirror 6 的重构,说明修改已有系统的机会在哪里、验收的责任在谁。

所见即所得的编辑器里藏着一个隐蔽的问题:你看到的,和文件里实际存的,可能不是同一份数据。一次把 Markdown 编辑器从 Milkdown 迁移到 CodeMirror 6 内核的重构,就是从这个根源动刀的。

两份状态,数不清的细节

原来的内核里,渲染出来的内容和原始源码是两套状态。改东西的时候,界面上看起来已经改了,源码里可能并没有改。这不是一个孤立的 Bug,而是一类问题的温床:两份表示之间需要同步,同步就有时机、粒度和冲突处理的问题,细节多到数不清,修掉一个又冒出一个。

目标因此很明确:让源码成为唯一的事实,渲染从源码派生。这也是 Typora 等主流编辑器已经验证过的路线。做到这一点最直接的办法,就是把底层内核换掉——在旧的架构上继续修补,等于不断为两份状态之间的缝隙打补丁。

方案是模型提的,工期也由它估

这次重构连方案都来自模型。开发者并不熟悉内核选型,是模型提出换内核、列举了换完能解决的一串问题,并估计工作量“可能需要一周”。开发者反复确认“这个事情我能 Hold 住吗”,得到肯定答复后才动手,使用的模型是当时的 DeepSeek Flash。

一周是按人类的节奏估的。开发者提醒它“你是 AI,一天差不多了”,结果模型一次性写出了迁移的主体,有一些 Bug,但骨架一次成型。预估和执行的落差本身说明了一个变化:过去拦住这类重构的,主要是逐文件改写的工作量;这部分被压缩之后,真正稀缺的变成了判断——要不要迁、迁到哪、怎么算迁完。

“写完了”不等于迁移完成

迁移最容易省略的环节是验收。一次性生成的代码会有 Bug;换完内核后编辑器“巨爽”、解决了一堆旧问题,是使用层面的感受。编辑器质量最终仍要靠长期使用和回归情况说话:旧的编辑行为有没有逐项保留,嵌套结构、粘贴、撤销、表格这些 Markdown 的老大难场景有没有回归。这次尝试没有展示完整的测试覆盖,所以它能支持的结论是“迁移主体完成、使用体验明显改善”,而不是“内核迁移从此很轻松”。

同一晚的讨论里还有一个可以对照的案例:一个游戏项目在更换实现语言之前,先让单元测试把既有逻辑锁住,替换才变得“轻轻松松”。两个案例指向同一件事——行为约束和回归检查,比“重写完了”更能定义迁移是否完成。迁移的产出不只是新代码,还有一套能证明旧行为没有被破坏的依据。

这次经验适用到哪里

同一段讨论里还有另一个迁移:把一整套 Obsidian 插件搬进另一个运行环境,交代方式是让模型自己判断哪些功能与笔记无关并去掉,一次迁完。可见模型能承担的不只是机械改写,还包括迁移中的裁剪判断——前提是把“什么算相关”的范围交代清楚。

讨论中也出现了更激进的感叹,认为不少跨平台技术栈“已经没有市场了”。这句话需要谨慎对待。一个编辑器换内核成功,说明在“双份状态”这个具体病灶上,换掉内核比继续修补更划算;它不能推广成所有框架失去价值。某个框架值不值得用,取决于它解决的问题在当前项目里是否还存在、留在原架构和迁走的成本谁高谁低——这些要按项目逐个算,一次尝试定不了论。

迁移的发起和执行可以交给模型,“什么叫做完”仍然由人定义。这个责任没有因为执行变快而消失,反而因为执行变快,更容易被人忽略。

相关资料