Slate v2 Op-family 第十二切片实战:将精确删除从 Path 扩展到 Point
【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate
导读
本文聚焦 plate 仓库中 Slate v2 重写工程的关键一环——Op-family(操作族)第十二切片(Twelfth Slice):在slate包中把"第一个诚实的delete家族实现"从精确的{ at: Path }场景,扩展到下一个精确场景{ at: Point }。你将了解这套"窄切片、诚实认领、测试先行"的执行方法论如何被落实到删除操作上,理解精确删除(exact point deletion)的语义边界、范围控制原则与五阶段实施流程,并看到仓库中delete家族源码与 Operation 类型定义的相互印证。
背景:Slate v2 的 Op-family 切片策略
本计划文档(docs/plans/2026-04-07-slate-v2-op-family-twelfth-slice.md)是 Slate v2 重写工程的配套执行计划(Supporting plan),其队列与路线图的权威依据统一收口在 docs/slate-v2/master-roadmap.md。原文档中的/Users/zbeyens/git/plate-2/docs/slate-v2/master-roadmap.md是作者本机绝对路径,在仓库中对应的相对路径即为docs/slate-v2/master-roadmap.md。
从 master-roadmap 的"Execution Doctrine"(执行纲领)可以看出这套切片策略的底层原则:
- 每个包构建一份合并契约语料库(merged contract corpus),来源包括:legacy 精确测试/文档、draft 契约测试/文档、当前 proof owner;
- legacy 行是默认事实,draft 行仅在代表 v2 预期行为且不与保留的 legacy 行冲突时保留;
- 核心引擎文件的测试支撑契约覆盖率优先于当前源码形态;
- 程序有两个不可谈判的闸门:**parity gate(对等闸门)**与v2 north-star gate(北极星闸门)。
第十二切片正是这一纲领在delete操作家族上的具体落地。在此之前,同系列的切片已经依序铺开(first-slice 落地insert_node/remove_node操作类型与Transforms.insertNodes/removeNodes;second-slice 落地 path 基set_node与Transforms.setNodes并修复自定义 props 在 draft/snapshot 模型中被剥离的深层 bug;third-slice 在此基础上落地Transforms.unsetNodes)。第十二切片则是把视线转向删除方向的第一步。
切片目标:在slate中扩展第一个诚实的delete家族实现
第十二切片的目标非常聚焦:
将
slate中第一个诚实的delete家族切片从精确的{ at: Path }场景,拓宽到下一个精确场景:Point。
这意味着在此之前,仓库中已经存在一个仅支持精确 Path 定位的delete基础实现(即"第一个诚实的删除切片"),它刻意没有声称支持更宽的语义。本切片要做的是在不破坏该实现诚实性的前提下,让删除操作再多支持一种精确的定位输入——Point(文档树中的一个精确文本点位置)。
值得强调的是"诚实(honest)"这个词贯穿整个 op-family 系列:每一片只认领自己真正实现并经过测试验证的能力,绝不为了 API 表面完整性而写出"假 wrapper"——second-slice 中曾明确指出"draft/snapshot 模型会剥离自定义 props,因此写一个假的setNodeswrapper 就等于撒谎",这正是该系列对实现诚实性的极致要求。
范围控制:窄切片原则
与系列其他切片一致,本切片明确划定了禁止进入的范围,防止一次改动扩散成不可控的兼容性黑洞:
| 允许(In scope) | 禁止(Out of scope) |
|---|---|
| 仅支持精确 Point 定位的删除(exact point deletion) | 区间删除(range deletion) |
| 保持切片窄小、可独立验证 | unit/distance/reverse/hanging 等删除方向与边界语义对等 |
广泛认领 legacydelete(...)API |
这套"先精确、后宽泛"的顺序是有意为之的:Point是比Path更贴近用户光标语义的定位,但又不涉及Range带来的选区、跨越多个块、合并相邻文本等复杂归一化问题。把{ at: Point }作为下一个精确场景,是删除家族中风险递增曲线上的最小可验证台阶。
五阶段实施流程
原计划将本切片拆分为五个可独立校验的阶段,这也是 op-family 系列的通用执行模板:
确认精确 Point 删除语义与当前 helper 接缝(seam)先摸清现有
delete家族代码中哪些辅助函数/内部接缝能承载 Point 语义,确认"精确删除一个 Point"在事务引擎中的正确行为(例如删除后是否需要合并相邻文本、如何维护 selection),再动手。编写聚焦的失败测试(focused failing tests)先写出针对精确 Point 删除的失败测试,用测试契约锁定预期行为。这与 master-roadmap 中"测试支撑的契约覆盖率优先于当前源码形态"的纲领完全一致。
实现最小的诚实
delete扩展在既有{ at: Path }精确删除之上,增加{ at: Point }分支,保持切片窄小:不改区间删除、不引入方向/距离语义、不声称覆盖 legacydelete(...)全量行为。同步包内与公开文档将新增能力同步到
packages/slate的包文档与公开 API 文档,避免"文档已描述、包未实现"或"包已实现、文档未覆盖"的漂移——这正是本系列反复出现的问题类型。验证受影响的包与文档跑通契约测试、类型检查、lint 与格式化检查(详见下文"验证方式")。
从计划文档中阶段前的[ ]未勾选状态可以判断:本切片属于已设计、待执行的切片,其价值在于把删除家族的扩展路径提前敲定。
仓库源码印证:delete家族与 Operation 类型
虽然本切片尚未执行,但仓库中已存在的delete家族源码与 Operation 类型定义,正好回答了第一阶段"当前 helper 接缝在哪里"这个问题。
delete家族工具(辅助函数接缝):在 packages/slate/src/internal/editor/ 与 packages/slate/src/internal/transforms/、packages/slate/src/utils/ 下可以找到:
- deleteBackward.ts / deleteForward.ts:分别封装
slate的deleteBackward/deleteForward基础实现,接受TextUnit(默认'character')作为单位参数,是"向后/向前删除一个字符"的入口; - deleteFragment.ts:封装
deleteFragment,处理选区(Range)对应片段的删除; - deleteText.ts:文本删除的核心 transform;
- deleteMerge.ts:删除后相邻文本/节点合并的归一化辅助。
从这些文件的职责划分可以推断:精确 Point 删除的"接缝"最可能落在deleteText(单点文本删除)与deleteMerge(删除后合并)这一层——将{ at: Point }解析为具体文本偏移后,复用现有的删除与合并管线即可完成"最小的诚实扩展",而不需要触碰方向语义与区间删除。
Operation 类型定义:packages/slate/src/interfaces/operation.ts 中定义了完整的基础操作类型族,其中与删除相关的能力已经就位:
InsertNodeOperation/RemoveNodeOperation:分别携带node与path字段、type: 'insert_node'/'remove_node',是 first-slice 落地的节点级插入/删除操作;RemoveTextOperation:携带path、offset、text三个字段、type: 'remove_text',是删除文本的底层操作——精确 Point 删除最终大概率落到remove_text操作上;- 同文件中的
SetNodeOperation(properties/newProperties/path)则印证了 second-slice 的 set_node 接缝。
Operation类型定义还自带操作 API 的检查工具:OperationApi.isNodeOperation、isOperation、isOperationList、isSelectionOperation、isTextOperation以及inverse(操作取逆,用于实现撤销/历史)。文档注释明确说明:将一切变更表示为操作,正是 Slate 编辑器实现历史、协作等功能的基础——这也是整个 op-family 系列持续补齐操作类型的根本原因。
测试契约佐证:对应的契约测试已存在并保持绿色,例如 deleteBackward.spec.tsx、deleteForward.spec.tsx、deleteText.spec.tsx、deleteMerge.spec.tsx。这些现有测试既是"第一个诚实删除切片"的守护者,也为第十二切片的"聚焦失败测试"提供了同构范本:先描述{ at: Point }的预期行为,再让实现去满足它。
验证方式:与系列切片一致的多层检查
虽然第十二切片本身未列出独立验证命令,但同系列切片(first/second/third)给出了标准的验证清单,可直接沿用:
yarn mocha --require ./config/babel/register.cjs ./packages/slate/test/snapshot-contract.ts——快照契约测试(注意:master-roadmap 明确提示 snapshot-contract.ts 是宽口径的 oracle owner,不默认属于包级 closeout 证明,需要单独直跑验证);yarn test:mocha——Mocha 全量测试;yarn workspace slate-react run test——slate-react 侧回归;- 对改动文件的定向
tsc诊断:0 错误; - 对改动文件的定向
yarn exec eslint; yarn prettier --check(slate-v2 文件)与pnpm exec prettier --check(plate-2 文档)。
系列切片还特别注明了两条诚实的"环境事实":仓库级yarn lint:typescript仍会命中切片范围外的陈旧*-v2tsconfig 引用;仓库级yarn lint:eslint仍会报告大量与本次改动无关的历史问题。因此定向检查(仅对改动文件)才是判断本切片健康度的真实信号——这套"只认领自己改动范围内绿"的做法,与本切片的"诚实"精神一脉相承。
结语:删除家族的下一台阶
第十二切片完成后,delete家族将同时支持精确{ at: Path }与精确{ at: Point }两种定位,为后续更宽的删除语义(区间删除、unit/distance/reverse/hanging 对等)铺平道路。而按照 op-family 系列的节奏,每个切片收敛后都会在 docs/slate-v2/master-roadmap.md 中同步队列状态,确保"文档 — 源码 — 测试"三者始终咬合。
对于想深入这套重写方法的读者,建议按顺序阅读 first-slice、second-slice、third-slice 三份姊妹计划,再对照 master-roadmap.md 的 tranche 顺序与执行纲领,即可完整理解"窄切片、诚实认领、契约先行"这套工程方法如何在 Slate v2 中被系统化执行。
【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考