# 选区与文档位置

读取光标范围、选中文字或节点，并让工具栏保留当前编辑目标。

选区表示下一次编辑操作的目标。它可以是一个光标、一段文字，也可以是一个完整节点。选区属于 Editor 的规范状态；屏幕上的高亮由 View 根据它绘制。

## 读取当前选区

```ts [读取并提取选中的文字]
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](/api/view)。

## 设置文本或节点选区

```ts [在上面的 Hello 文档中选择文字]
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 格式查询，见[连接工具栏](/build/commands)。

## 订阅变化

`selectionUpdate` 适合更新选区统计；工具栏还需要响应正文和光标处待输入格式的变化，可以订阅 `transaction`，只读取所需的少量状态。光标移动时不应序列化全文或保存文件。如何清理订阅见[生命周期与事件](/concepts/lifecycle)。
