Skip to content

核心概念

理解 Incremark 的工作原理,有助于更好地使用和调试。

增量解析流程

┌──────────────────────────────────────────────────────────────┐
│                        输入流                                  │
│  "# 标题" → "\n\n内" → "容\n" → "\n## 二" → "级标题"           │
└──────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────┐
│                      缓冲区 (Buffer)                          │
│  "# 标题\n\n内容\n\n## 二级标题"                               │
└──────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────┐
│                    边界检测 (Boundary Detection)              │
│  逐行扫描,识别块边界:                                        │
│  - 空行分隔段落                                               │
│  - 标题行独立成块                                             │
│  - 代码围栏 ``` 需要配对                                      │
└──────────────────────────────────────────────────────────────┘

              ┌───────────────┴───────────────┐
              ▼                               ▼
┌─────────────────────────┐     ┌─────────────────────────┐
│    已完成块 (Completed)  │     │    待处理块 (Pending)    │
│  • 不再重新解析          │     │  • 每次 append 重新解析   │
│  • 节点可被复用          │     │  • 可能不完整            │
└─────────────────────────┘     └─────────────────────────┘

块状态

每个解析出的块有三种状态:

状态说明处理方式
pending正在接收,可能不完整每次 append 重新解析
stable可能完整,但后续 chunk 可能改变缓存但不确认
completed确认完成,不会再变永久缓存,不再处理

边界检测规则

Incremark 使用启发式规则检测块边界:

简单块

  • 空行 - 分隔段落
  • 标题 (#) - 独立成块
  • 分隔线 (---) - 独立成块

需要闭合的块

  • 代码块 (```) - 必须等待闭合围栏
  • 容器 (:::) - 必须等待闭合标记

嵌套块

  • 列表 - 跟踪缩进级别
  • 引用 (>) - 跟踪引用深度
  • 表格 - 检测分隔符行

上下文跟踪

为了正确处理嵌套结构,解析器维护上下文状态:

ts
interface BlockContext {
  inFencedCode: boolean     // 是否在代码块中
  fenceChar?: string        // 代码块围栏字符
  fenceLength?: number      // 围栏长度
  listDepth: number         // 列表嵌套深度
  blockquoteDepth: number   // 引用嵌套深度
  inContainer: boolean      // 是否在容器中
  containerDepth: number    // 容器嵌套深度
}

AST 结构

Incremark 生成标准的 MDAST 格式:

ts
interface Root {
  type: 'root'
  children: RootContent[]
}

// 块级节点
type RootContent = 
  | Heading 
  | Paragraph 
  | Code 
  | List 
  | Blockquote 
  | Table 
  | ThematicBreak
  | ...

性能优化

为什么快?

  1. 跳过已完成块 - O(1) 而非 O(n)
  2. 增量行更新 - 只处理新增行
  3. 前缀和优化 - O(1) 计算行偏移

复杂度对比

操作传统方式Incremark
追加 chunkO(n)O(k)
总解析量O(n²)O(n)
内存占用反复创建增量复用

n = 总字符数,k = 新增字符数

下一步

Released under the MIT License.