选区表示下一次编辑操作的目标。它可以是一个光标、一段文字,也可以是一个完整节点。选区属于 Editor 的规范状态;屏幕上的高亮由 View 根据它绘制。
读取当前选区
const { doc, selection } = editor.state.pm
const { from, to, empty } = selection
const text = doc.textBetween(from, to, '\n')
empty 表示范围是否折叠;普通文本光标的 from 与 to 相同。from / to 是按文档顺序排列的边界;需要保留向前或向后拖选的方向时,读取 anchor / head。
位置是 ProseMirror 文档位置,不是纯文本字符下标,也不是像素。以只含一个段落 Hello 的文档为例,段落前是 0,文字从 1 开始;选择 Hello 使用 { from: 1, to: 6 }。容器边界也占位置,嵌套内容不能靠统计字符串长度定位。需要坐标转换时,使用 mounted View 的几何 API。
设置文本或节点选区
editor.chain().setTextSelection({ from: 1, to: 6 }).focus().run()
传一个数字给 setTextSelection(position) 会放置光标;传 { from, to } 则选择范围。选完整图片或其他可选择节点时,用 setNodeSelection(position),位置指向节点之前;selectAll() 选择整个文档。节点选区的 empty 通常为 false,但它不等于一段连续文字。
不要把前一份文档或更早事务中的数字位置直接套到当前内容。同步命令链共享事务;复杂命令中先改动结构、再定位后续位置时,需要用该事务的 tr.mapping 映射。异步操作还要校验文档、目标和任务是否仍有效。
工具栏如何保留目标
普通加粗按钮可以调用 editor.chain().focus().toggleBold().run():不传位置的 focus() 恢复当前选区,不会把光标移到末尾。不要在每次点击按钮时硬编码 focus('end')。
用 editor.isActive('bold') 判断粗体状态,用 editor.getAttributes('heading').level 读取标题属性;二者都只查询当前状态。Word 的有效格式还可能来自样式继承,应使用对应 Word 格式查询,见连接工具栏。
订阅变化
selectionUpdate 适合更新选区统计;工具栏还需要响应正文和光标处待输入格式的变化,可以订阅 transaction,只读取所需的少量状态。光标移动时不应序列化全文或保存文件。如何清理订阅见生命周期与事件。