# 选区与异步目标

使用逻辑源码坐标，防止弹窗、上传和异步操作写到过期位置。

## 坐标属于源码

MSFE 使用 CodeMirror 逻辑文本的 UTF-16 偏移和左闭右开范围 `[from, to)`。视觉预览没有独立坐标；一张图或一个任务按钮仍对应同一段源码。Emoji 可能占两个 UTF-16 单元，不能用字符数或 DOM 文本偏移替代。

`getMarkdown()` 可能恢复文件换行策略；计算编辑偏移时使用 `editor.state.doc` 的逻辑文本，不要对带 CRLF 的保存结果按字节计数。`selection.main.from/to` 是排序后的范围，`anchor/head` 保留选择方向。

~~~ts
import { EditorSelection } from '@quirekit/cm-state'

editor.setSelection(EditorSelection.single(10, 18))
editor.scrollTo(10)
editor.focus()
~~~

`EditorSelection.cursor()` 返回单个范围；`setSelection` 接受完整 `EditorSelection`，用 `single()` 或 `create()` 包装。多选区命令应明确支持全部范围还是拒绝；不能悄悄只改第一处。

## 短暂菜单：捕获精确目标

~~~ts
const target = editor.captureTarget()
// The host displays a menu; source may change before the user confirms.
const applied = editor.chain(target).insertText('approved').run()
if (!applied) console.warn(editor.lastCommandResult)
~~~

目标包含实例、文档、版本和有方向选区。任何源码版本变化都可能使它过期；失败时不能移除 target 后盲目重试，否则会修改新的选区。

## 等待对话框：映射原始范围

`captureAnchor()` 可跨无关编辑映射位置，同时验证原范围的文本仍未改变。以下完整函数适合资源选择、异步文本选择等流程；宿主对话框必须尊重 `AbortSignal`。

### recipes/intent.ts

```ts
import type { MsfeEditor } from "@quirekit/msfe-core";
/** @param editor - Current document. @param choose - Host dialog, observing cancellation. @returns Whether a still-current range was updated. */
export async function replaceSelectionFromDialog(editor: MsfeEditor, choose: (signal: AbortSignal) => Promise<string | null>): Promise<boolean> {
  const range = editor.state.selection.main;
  const scope = editor.documentScope.fork();
  const anchor = editor.captureAnchor({ from: range.from, to: range.to });
  scope.own(() => anchor.release());
  try {
    const value = await choose(scope.signal);
    if (scope.disposed || value === null) return false;
    const target = anchor.target();
    // Unrelated edits may move the interval; changes to its own text invalidate it.
    return target ? editor.chain(target).insertText(value).run() : false;
  } finally { scope.dispose(); }
}

```

捕获范围文本已变化、被删除、换文档或已释放时，不再生成有效目标。即使业务复用同一个 `documentId`，换文档也会结束旧锚点。返回 `false` 后可以提示用户重新选择，不能强制使用旧偏移。

## 范围映射策略

默认 `inclusiveStart` / `inclusiveEnd` 为 `false`：恰好插在边界的文本不并入原范围。`onDelete: 'drop'` 会丢弃完整删除的范围；`keep` 可将元数据位置保留为点。选择取决于批注、装饰或插入意图的语义，不能套用同一种策略。

`SourceRangeSet` 适合较多的范围元数据；单个异步用户意图优先用 `captureAnchor()`，无需自行维护整套位置映射。

## 与源码显露、搜索和只读协作

原生选区进入普通替换投影时会显露源码；图表的“编辑源码”动作只需设置正确的源码选区。需要完全由控件消费的原子折叠使用专门状态和 `interaction: 'atomic'`，不能借此永久遮挡可搜索内容。

只读仍可选中和复制。最终写入仍由命令检查权限；捕获锚点并不授予写权限。相关签名见 [命令 API](/api/commands) 与 [渲染 API](/api/rendering)。
