如果只需要分析 Markdown 内容,或给自己的渲染器提供语法树,可以直接使用 @quirekit/incremark-core。这个入口不导入 Vue/UI,也不创建可编辑视图;目前仍通过 MSFE 依赖 CodeMirror 模块。
1. 解析一份完整文本
文本已经接收完毕时,调用 parseMarkdown():
import { parseMarkdown } from '@quirekit/incremark-core'
const state = parseMarkdown('# Hello')
const text = state.doc.toString()
state 是 CodeMirror 的只读 EditorState,里面包含文本、语法树及扩展维护的语义信息;它不是 HTML,也不是 mdast/hast。上面的 text 仍是 # Hello。保存时保留原始 Markdown,不要序列化 EditorState。
2. 接收陆续到来的文本
持续收到新片段时,保留同一个 MarkdownStream,把每个新片段交给 append():
import { MarkdownStream } from '@quirekit/incremark-core'
const stream = new MarkdownStream()
stream.append('# Hello')
const complete = stream.advance(4)
const snapshot = stream.state
append() 接收新增加的部分;如果应用已经累计了完整文本,改用 stream.update(全文)。advance(4) 给解析器约 4 毫秒的额外预算,返回 true 表示当前文本的语法与引用分组已完成,返回 false 时由应用安排下一次调用。
类本身没有定时器。不要用同步循环一直等待 true,否则会阻塞界面。预算是协作式提示,不保证所有处理严格在 4 毫秒内结束;后续引用检查和界面渲染也需要时间。snapshot 可以先读取,但尚未完成时不应把它当作最终解析结果。
3. 读取语法树
从完成的状态中取得 Lezer 语法树:
import { syntaxTree } from '@quirekit/cm-language'
const tree = syntaxTree(state)
const description = tree.toString()
对于 # Hello,紧凑描述是 Document(ATXHeading1(HeaderMark)):整个文档里有一个一级标题及其 # 标记。自己的遍历逻辑可以通过树的 cursor、firstChild、nextSibling 读取节点和 UTF-16 范围。
state.doc 使用规范化换行。链接和脚注等跨块关系由扩展维护,不能仅凭某个源码前缀推断最终链接目标。树描述适合调试,不是存档格式。
完整函数参考
recipes/snapshot.ts 将第一步和第三步封装成 inspectMarkdown(source),返回状态、文本和树描述。需要在自己的分析工具中复用时,可查阅这个单文件函数:
import { parseMarkdown } from "@quirekit/incremark-core";
import { syntaxTree } from "@quirekit/cm-language";
/**
* Parse once without mounting an editor or creating Vue components.
* @param source - Exact Markdown supplied by the host.
* @returns The immutable snapshot, canonical text and a compact syntax-tree description.
*/
export function inspectMarkdown(source: string) {
const state = parseMarkdown(source);
return { state, text: state.doc.toString(), syntax: syntaxTree(state).toString() };
}