# 命令与目标 API

命令草稿、失败结果、外部修改与异步范围协议。

本页签名从公共包导出生成，中文说明与当前 JSDoc 同步校验。代码标识保持原名。

## 命令上下文

命令必须纯粹、同步；嵌套操作使用 props.commands，见 [命令教程](/guide/commands)。

### CommandProps

纯命令执行上下文；所有嵌套操作共用同一个事务草稿。

```ts
export interface CommandProps {
    readonly state: EditorState;
    readonly tr: SourceTransaction;
    readonly editor: MsfeEditor;
    readonly commands: SingleCommands;
    readonly dispatch: (() => void) | undefined;
    chain(): ChainedCommands;
    can(): SingleCommands;
}
```

**state** — 链中前序步骤执行后的当前草稿，不能用挂载状态代替。

**tr** — 源码事务构建器。

**editor** — 规范编辑器实例；嵌套修改使用 props.commands。

**commands** — 在当前未发布草稿上执行的嵌套命令。

**dispatch** — can() 期间为 undefined；实际提交由命令管理器负责。

**chain** — 返回基于同一草稿的嵌套原子命令链。

**can** — 返回从当前草稿开始、不会修改它的适用性查询。

## 源码事务

每一步坐标属于当前草稿，不能使用原始文档的过期偏移。

### SourceTransaction

MSFE 源码事务构建器，使用 CodeMirror 状态和源码偏移。

```ts
export interface SourceTransaction {
    readonly state: EditorState;
    insertText(text: string): boolean;
    replace(from: number, to: number, insert: string): boolean;
    setSelection(anchor: number, head?: number): boolean;
    effect<T>(effect: Effect<T>, value: T): boolean;
    setMeta<T>(metadata: Metadata<T>, value: T): boolean;
    apply(spec: TransactionSpec): boolean;
}
```

**state** — 所有前序步骤执行后的不可变草稿。

**insertText** — 参数 text：插入各选区的精确文本。返回是否可加入草稿。

**replace** — 参数 from、to：左闭右开的源码范围；insert：替换文本。返回范围和操作是否有效。

**setSelection** — 参数 anchor、head：有方向的选区；head 默认等于 anchor。返回是否有效。

**effect** — 参数 effect：类型化效果；value：当前草稿坐标下的载荷。返回是否有效。

**setMeta** — 参数 metadata：类型化标记；value：关联数据。返回是否有效。

**apply** — 底层接入入口。参数 spec：CodeMirror 草稿步骤；返回是否有效。

## 适配规划函数

把现有 CodeMirror 纯规划函数接入相同的命令链。

### sourceCommand

将纯 CodeMirror 规划函数适配到源码命令事务。参数 planner：底层规划函数。返回可用于 addCommands 或 chain.command 的命令。

```ts
function sourceCommand(planner: MsfeCommand): Command
```

## 语义格式命令

`toggleSourceMark({ node: 'StrongEmphasis', marker: '**', markerNode: 'EmphasisMark' })`
返回可交给 `editor.run()` 或 `sourceCommand()` 的纯规划函数。功能包声明语法，Core 统一处理
成对标记、方向选区和完整外壳；一次执行同时确认新增格式和原有成对语义仍能解析。
完整正文选区可以扩展到隐藏外壳；没有可证明无损的方案就返回 `null`，不改源码或历史。
局部移除格式和交错范围暂不自动拆分。空选区保留插入成对标记的输入方式，后续输入仍是自由源码。

`querySourceInline(state, range)` 只读端点祖先，不启动解析、遍历全文或执行格式命令，适合界面可用性。
`reason === null` 仅表示结构允许，不能预先保证具体样式组合可行；空白正文可没有节点。
显式规划限制在一个不超过 20,000 字符的行内容器，解析验证预算为 12ms；无法及时验证也拒绝。
所有多选区先规划，再原子提交。低层 `toggleDelimiter` 仍只是字面源码工具。

### SourceMarkFormat

语义格式功能声明的语法节点与源码标记。

```ts
export interface SourceMarkFormat {
    readonly node: string;
    readonly marker: string;
    readonly markerNode: string;
}
```

**node** — 目标格式对应的解析节点名。

**marker** — 添加格式时使用的源码标记。

**markerNode** — 直接子节点中的首尾标记名称。
### SourceInlineQuery

共同的行内容器，以及拒绝格式操作的结构原因。

```ts
export interface SourceInlineQuery {
    readonly node: SyntaxNode | null;
    readonly reason: "readonly" | "unparsed" | "structure" | "literal" | "selection" | null;
}
```

**node** — 已确认的段落、标题或单个表格单元格；空白正文可没有节点。

**reason** — 结构性拒绝原因；为 null 时可继续执行显式格式规划。
### querySourceInline

只读取源码端点和祖先，不启动解析或序列化。参数 state：当前源码状态。参数 range：目标范围。返回共同的行内容器或结构性拒绝原因。

```ts
function querySourceInline(state: EditorState, range: { readonly from: number; readonly to: number; }): SourceInlineQuery
```
### toggleSourceMark

基于当前解析器切换格式，保持 Markdown 文本权威。参数 format：功能声明的节点与标记。返回纯规划命令；不能保留语义时不修改源码或历史。

```ts
function toggleSourceMark(format: SourceMarkFormat): MsfeCommand
```

## 结果分支

具名命令返回 boolean；详细结果读取 lastCommandResult。

### MsfeResult

明确返回成功或拒绝原因；不会自动把过期用户意图转移到新位置。

```ts
export type MsfeResult = {
    readonly ok: true;
} | {
    readonly ok: false;
    readonly reason: "destroyed" | "not-mounted" | "readonly" | "stale-target" | "not-applicable" | "invalid-change" | "composing" | "not-installed" | "source-provider-locked";
};
```

## 外部增量

baseRevision 是本地源码版本，不是服务器 ETag。

### MsfeExternalChange

携带基础版本的外部源码修改。

```ts
export interface MsfeExternalChange {
    readonly documentId: string;
    readonly baseRevision: number;
    readonly changes: ChangeSpec;
    readonly origin?: string;
}
```

**documentId** — 期望的文档标识。

**baseRevision** — 本次偏移对应的精确源码版本。

**changes** — CodeMirror 逻辑源码坐标中的修改。

**origin** — 宿主用于识别回声的来源标识。

## 精确目标

适合短暂菜单；源码版本变化后可能失效。

### MsfeTarget

捕获工具栏动作开始时的目标，仅对该实例、文档及精确版本有效。

```ts
export interface MsfeTarget {
    readonly instanceId: string;
    readonly documentId: string;
    readonly revision: number;
    readonly selection: EditorSelection;
}
```

**instanceId** — 避免将其他编辑器捕获的目标应用到当前实例。

**documentId** — 动作开始时的文档标识。

**revision** — 动作开始时的源码版本。

**selection** — 最初有方向的源码选区。

## 映射目标

异步处理完整示例见 [选区与异步目标](/guide/selection)。

### SourceAnchor

限定于当前文档生命周期、可随修改映射位置的操作意图。

```ts
export interface SourceAnchor {
    getRange(): MsfeSourceRange | null;
    target(): MsfeTarget | null;
    release(): void;
}
```

**getRange** — 返回映射后的当前范围；被删除、释放或换文档后返回 null。

**target** — 仅在捕获的文本未改变时返回新的目标；不会静默指向已修改内容。

**release** — 结束资源归属，不改变源码或历史。

## 映射策略

按功能选择边界亲和性和删除策略，不改变视觉结构。

### RangePolicy

定义边界插入和删除时如何映射位置，与视觉样式无关。

```ts
export interface RangePolicy {
    readonly inclusiveStart?: boolean;
    readonly inclusiveEnd?: boolean;
    readonly onDelete?: "drop" | "keep";
}
```

**inclusiveStart** — 是否包含恰好插在起点的文本，默认 false。

**inclusiveEnd** — 是否包含恰好插在终点的文本，默认 false。

**onDelete** — 完整删除或替换后默认丢弃范围；keep 可保留为一个点。

## 源码块命令

定位完整语法块，并通过捕获的 `MsfeTarget` 运行命令。规划器保留源码片段、一次撤销，拒绝跨子流移动。

### SourceBlock

完整源码块及其同级内容范围，不创建第二份文档模型。

```ts
export interface SourceBlock extends MsfeSourceRange {
    readonly name: string;
    readonly scope: MsfeSourceRange;
}
```

**name** — 已接受的解析节点名称。

**scope** — 允许完整同级块重新排序的源码范围。

**from** — 源码起点，包含该位置。

**to** — 源码终点，不包含该位置。

### SourceBlockSide

相对于完整块的插入方向：上方或下方。

```ts
export type SourceBlockSide = "before" | "after";
```

### sourceBlockAt

只查询指针或光标所在的祖先链，不扫描兄弟内容。参数 state：已接受源码及解析快照。参数 position：块内逻辑位置。参数 scope：可选的同级范围，用于把嵌套落点提升到同一层。返回最近的完整可操作块。

```ts
function sourceBlockAt(state: EditorState, position: number, scope?: SourceBlock["scope"]): SourceBlock | null
```

### sourceBlockSibling

参数 state：当前解析快照。参数 block：完整块。参数 direction：上一个（-1）或下一个（1）。返回相邻结构块，不枚举整篇文档。

```ts
function sourceBlockSibling(state: EditorState, block: SourceBlock, direction: -1 | 1): SourceBlock | null
```

### sameSourceBlockScope

参数 a、b：两个源码块。返回两者是否属于完全相同的同级内容范围。

```ts
function sameSourceBlockScope(a: SourceBlock, b: SourceBlock): boolean
```

### insertSourceBlock

参数 block：明确的邻接块。参数 side：插入方向。参数 markdown：完整 Markdown。返回原子插入规划器，保留内容中的原始分隔标记。

```ts
function insertSourceBlock(block: SourceBlock, side: SourceBlockSide, markdown: string): MsfeCommand
```

`replaceSourceBlock(block, markdown)` 用新 Markdown 一次替换完整块，保留必要的相邻分隔符，
并校验外层容器边界。通过 `editor.run(command, capturedTarget)` 执行，以拒绝过时菜单意图。

### replaceSourceBlock

参数 block：捕获的完整源码块。参数 markdown：规范的替换内容。返回原子替换规划器，重新校验块归属并保留同级分隔与容器边界。

```ts
function replaceSourceBlock(block: SourceBlock, markdown: string): MsfeCommand
```

### SourceBlankLine

可插入内容流中的空白物理行，不创建虚假的语法节点。

```ts
export interface SourceBlankLine extends MsfeSourceRange {
    readonly scope: MsfeSourceRange;
}
```

**scope** — 可编辑的同级内容范围，不包含外层分隔符。

**from** — 源码起点，包含该位置。

**to** — 源码终点，不包含该位置。

### sourceBlankLineAt

参数 state：当前源码与语法。参数 position：指针或光标位置。返回可插入内容流中的空行；代码或列表语法内部空白不作为插入位置。

```ts
function sourceBlankLineAt(state: EditorState, position: number): SourceBlankLine | null
```

### insertAtSourceBlankLine

参数 line：已捕获的可编辑空行。参数 markdown：完整的标准源码。返回受保护的替换命令，保留同级块分隔与外层容器。

```ts
function insertAtSourceBlankLine(line: SourceBlankLine, markdown: string): MsfeCommand
```

### removeSourceBlock

参数 block：捕获的完整块。返回在命令草稿中重新校验的源码删除规划器。

```ts
function removeSourceBlock(block: SourceBlock): MsfeCommand
```

### moveSourceBlock

通过 editor.run 移动精确源码，只提交一次事务、产生一次撤销事件。参数 block：手势开始时捕获的块。参数 target：最终指针位置的完整同级块。参数 side：目标前后方向。返回拒绝自身、后代、跨范围或过期目标的规划器。

```ts
function moveSourceBlock(block: SourceBlock, target: SourceBlock, side: SourceBlockSide): MsfeCommand
```

## 编辑边界

插件用 `addEditingBoundaries()`（或 descriptor 的 `editingBoundaries`）声明隐藏源码的
语义范围。宿主 `MdEditor` / `createMdEditor` / `createMdEditorExtensions` 的同名选项可
按节点覆盖默认规则；完整接入示例见 [光标与编辑边界](/guide/md-editor#光标与编辑边界)。
这份声明用于导航、选区和正文输入保护；Enter、Shift-Enter 和 Mod-Enter 的结构操作由
下面的 `addBlockInput()` 合同承担。

### 范围和策略

所有偏移属于当前 `state.doc`。必须满足
`0 ≤ from ≤ contentFrom ≤ contentTo ≤ to ≤ state.doc.length`；可选的
`selectFrom/selectTo` 必须覆盖 `from/to` 且在文档内。非法范围在解析规则时抛出
`invalid-editing-boundary` 错误；无法确认语法时应返回 `null`。

| `kind` | 语义与常见用法 |
| --- | --- |
| `style` | 加粗、斜体、代码、链接等行内格式，正文在成对外壳内部 |
| `block` | 代码、容器等块，外壳保护包含保持结构所需的相邻换行 |
| `prefix` | 标题、列表、引用等结构前缀，光标进入前缀后的正文 |
| `separator` | 压缩的结构间隔，导航跳过；产品允许原生删除以合并正文 |
| `syntax` | 表格竖线或对齐行等结构字符，`selectFrom/selectTo` 可指向完整表格 |
| `atom` | 整体组件或空围栏，光标不进入内部，边缘删除先选中整体 |

`navigation: 'content'` 是默认的行内进入策略，`split` 保留样式内外两侧。
块显式声明 `content` 时，原生行首、行尾端点收敛到块内内容，适用于表格行的外侧竖线；
左右键仍通过公共导航越过重复端点，删除保护不变。未声明时保留块的默认跨外壳导航。
当前 `navigation: 'native'` 使整条语法规则退出公共运行时，包括它的编辑保护。
`deletion: 'unwrap'` 在对应边缘移除完整样式或前缀；`protect` 保留外壳；
`deletion: 'native'` 仅委托编辑处理，仍保留导航规则。建议显式声明删除策略。
完整选中所属结构允许整体删除，部分选区保留其外壳。

### EditingBoundary

隐藏源码的完整结构范围及可编辑正文；不创建第二份内容模型。

```ts
export interface EditingBoundary {
    readonly from: number;
    readonly to: number;
    readonly contentFrom: number;
    readonly contentTo: number;
    readonly kind: EditingBoundaryKind;
    readonly selectFrom?: number;
    readonly selectTo?: number;
    readonly navigation?: "content" | "split" | "native";
    readonly deletion?: "unwrap" | "protect" | "native";
}
```

**from** — 完整语义结构的起点。

**to** — 完整语义结构的终点。

**contentFrom** — 可编辑正文的第一个源码位置。

**contentTo** — 可编辑正文的最后一个源码位置。

**kind** — 结构类型，决定外部导航和整体删除行为。

**selectFrom** — 整体删除要求完整选中的更大结构起点，例如包含该分隔符的表格。

**selectTo** — 同一个整体删除所属结构的终点，默认使用本边界的终点。

**navigation** — content 直接进入样式内容，并将块外壳端点收敛到内容；split 保留样式外侧和内侧两个停顿位置。

**deletion** — unwrap 在光标位于相应边缘时移除完整外壳；protect 保留外壳；native 委托原生编辑。

### EditingBoundaryKind

隐藏源码的语义归属，独立于 DOM 装饰形状。

```ts
export type EditingBoundaryKind = "style" | "block" | "prefix" | "separator" | "syntax" | "atom";
```

### 查询和优先级

`nodes` 使用解析器节点名称。`resolve` 是当前源码上的纯查询，不能 dispatch、保留旧
节点偏移或按显示行数猜测位置。普通导航只查询端点与祖先，编辑查询本次变更覆盖的
语法；自定义查询也应保持对应的工作范围。

同一节点只采用按扩展优先级排序后的第一条规则，宿主覆盖在功能默认规则之前。
规则整体替换，不逐字段合并；返回 `null` 会退出该语法规则，不回退到下一条。
已物化的块组件独立贡献整体对象范围，并覆盖其内部语法规则。

默认 `when: 'hidden'` 仅在整体隐藏语法的呈现中启用。`when: 'prose'` 扩展到 Markdown
编辑呈现；需要保护未显露代码等结构时，先用 `isSourceRevealed(state, node)` 检查
具体范围，显露时返回 `null`。源码模式与组合输入期间不执行边界归一化。

### EditingBoundaryRule

功能提供的边界事实，由公共导航与编辑运行时统一消费。

```ts
export interface EditingBoundaryRule {
    readonly nodes: readonly string[];
    readonly when?: "hidden" | "prose";
    readonly resolve: (context: EditingBoundaryContext) => EditingBoundary | null;
}
```

**nodes** — 声明负责的语法节点名称；按扩展优先级，同名节点的第一条规则生效。

**when** — 默认仅在整体隐藏语法时查询；prose 也覆盖始终压缩的结构分隔位置。

**resolve** — 参数 context：当前源码与语法事实。返回：完整结构范围；语法保持字面呈现时返回 null。

### EditingBoundaryContext

针对一个规范源码端点的纯功能查询上下文。

```ts
export interface EditingBoundaryContext {
    readonly state: EditorState;
    readonly node: SyntaxNode;
    readonly position: number;
}
```

**state** — 已接受且不可变的 Markdown 状态。

**node** — 已确认的 Markdown 节点，不包含嵌入语言覆盖层。

**position** — 正在解析的端点或变更范围位置。

### 安装与生命周期

一般插件使用 `addEditingBoundaries()` 自动接入。高级 CM 集成可在
`addCodeMirrorExtensions()` 中安装 `editingBoundaries(rules)`；重复安装共享同一执行层。
`rules` 为空时返回空扩展，不卸载其他插件的规则。MD Editor 的 `editingBoundaries: []`
也保留默认规则；配置只在创建时读取，Vue prop 更新不自动重装规则。

输入保护在接受事务前生成完整变更，保留 annotations 并映射 effects；移动光标不写源码
或增加历史。显式结构命令、外部文档更新与历史恢复保持自身权限；绕过过滤器的事务
只在需要时恢复合法选区，并检查当前状态身份和实例是否已销毁。只读不接受正文写入。

### editingBoundaries

参数 rules：功能拥有的语法范围规则，高优先级在前。返回：共享导航、输入保护与端点恢复扩展；重复安装复用同一个运行时。

```ts
function editingBoundaries(rules: readonly EditingBoundaryRule[]): Extension
```

## 块级回车

扩展通过 `addBlockInput()` 声明语法块的输入归属；参见 [命令教程](/guide/commands)。`planBlockInput` 是纯规划入口，返回 `null` 时由调用方保留原生处理。

### BlockInputRule

语法节点的唯一输入所有者；省略意图时委托父级，未注册的块节点终止分派。

```ts
export interface BlockInputRule {
    readonly nodes: readonly string[];
    readonly enter?: BlockInputBehavior;
    readonly lineBreak?: BlockInputBehavior;
    readonly exit?: BlockInputBehavior;
}
```

**nodes** — 本声明拥有的确切语法节点名称。

**enter** — Enter 行为；根据当前源码决定，不记录按键次数。

**lineBreak** — Shift-Enter 的显式换行行为。

**exit** — Mod-Enter 的退出块行为。

### BlockInputContext

整笔选区的不可变输入上下文；处理器只规划，不提交或修改 storage。

```ts
export interface BlockInputContext {
    readonly state: EditorState;
    readonly intent: BlockInputIntent;
    readonly targets: readonly BlockInputTarget[];
}
```

**state** — 任何输入修改之前的规范源码状态。

**intent** — 当前请求的语义键盘意图。

**targets** — 共享此处理器的全部端点及其所有者；非空选区提供两端。

### BlockInputTarget

一个选区端点及公共分派器为它解析出的当前语法所有者。

```ts
export interface BlockInputTarget {
    readonly range: SelectionRange;
    readonly position: number;
    readonly node: SyntaxNode;
}
```

**range** — 保留方向的原始完整选区。

**position** — 尚未修改的源码中的端点偏移。

**node** — 该端点最近的参与处理块。

### BlockInputIntent

Enter、Shift-Enter 和 Mod-Enter 对应的语义意图。

```ts
export type BlockInputIntent = "enter" | "lineBreak" | "exit";
```

### BlockInputResult

一笔原子源码编辑、委托父级或回退原生键盘处理。

```ts
export type BlockInputResult = TransactionSpec | "parent" | "native";
```

### BlockInputHandler

参数 context：经过统一校验的完整选区输入。
返回：针对整笔动作的纯规划结果。

```ts
export type BlockInputHandler = (context: BlockInputContext) => BlockInputResult;
```

### BlockInputBehavior

纯规划函数或固定归属决定；固定决定无需逐键调用函数。

```ts
export type BlockInputBehavior = BlockInputHandler | "parent" | "native";
```

### planBlockInput

解析输入归属，不触发解析、提交或扫描无关块。
参数 state：规范的不可变源码状态。
参数 intent：请求的键盘意图。
返回：完整选区的一笔编辑，或不产生部分修改的原生回退。

```ts
function planBlockInput(state: EditorState, intent: BlockInputIntent): TransactionSpec | null
```
