ekp/DEVELOPER_ZH.md
2026-01-25 13:50:46 +08:00

138 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Emacs-KP 开发者文档
本文档详细介绍了 `emacs-kp` 的内部架构、API 和算法原理。旨在帮助贡献者和高级用户理解其工作机制。
## 1. 架构概览
`emacs-kp` 采用分层架构,将文本处理、布局计算和渲染分离。
```
┌─────────────────────────────────────────────────────────────────┐
│ 用户 API 层 (ekp.el) │
│ ekp-pixel-justify ekp-pixel-range-justify ekp-clear-caches │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 缓存层 (ekp-utils.el) │
│ ekp--get-para (段落缓存) ekp-dp-cache (DP 结果缓存) │
└─────────────────────────────────────────────────────────────────┘
┌───────────────┴───────────────┐
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ 纯 Elisp 路径 │ │ C 模块路径 │
│ ekp--dp-cache-elisp │ │ ekp--dp-cache-via-c │
│ (Elisp 实现 O(n²) DP) │ │ (调用 C 进行 DP) │
└─────────────────────────┘ └─────────────────────────┘
┌─────────────────────────┐
│ C 动态模块 │
│ ekp_break_with_prefixes│
│ (8 线程并行计算) │
└─────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 渲染层 │
│ ekp--render-justified (应用断点,插入 display 属性胶水) │
└─────────────────────────────────────────────────────────────────┘
```
## 2. Elisp 核心 (ekp.el)
### 数据结构
#### `ekp-para` 结构体
核心数据结构是 `ekp-para`,代表预处理后的段落。它被缓存以避免重复分词和测量。
```elisp
(cl-defstruct ekp-para
string ; 带有属性的原始文本
latin-font ; 检测到的拉丁字体
cjk-font ; 检测到的 CJK 字体
boxes ; Box 字符串向量
boxes-widths ; Box 像素宽度向量
boxes-types ; 类型向量 (start-type . end-type)
glues-types ; 胶水类型符号向量 (lws, mws, cws, nws)
hyphen-pixel ; 连字符宽度
hyphen-positions ; 可断词 Box 索引向量
ideal-prefixs ; 前缀和:理想宽度 (用于 O(1) 宽度计算)
min-prefixs ; 前缀和:最小宽度
max-prefixs ; 前缀和:最大宽度
dp-cache) ; 哈希表:行宽像素 → DP 结果
```
#### 胶水类型 (Glue Types)
- `lws` (Latin Word Space): 拉丁词间距
- `mws` (Mixed Word Space): 中西文间距
- `cws` (CJK Word Space): CJK 字符间距
- `nws` (No Word Space): 固定间距
### 核心函数
#### `(ekp-pixel-justify STRING LINE-PIXEL)`
`STRING``LINE-PIXEL` 宽度对齐。
1. 检查缓存中是否有对应的 `ekp-para`
2. 若未命中,创建 `ekp-para`(分词、测量、断词处理)。
3. 调用 DP 引擎Elisp 或 C计算断点。
4. 使用 display 属性(特别是 `space` 属性)渲染结果。
#### `(ekp-pixel-range-justify STRING MIN-PIXEL MAX-PIXEL)`
在范围内寻找“最佳”宽度。使用三分搜索 (O(log n)) 最小化 demerits。用于自动寻找最适合该段落的宽度。
#### `(ekp-param-set ...)`
设置 9 个间距参数LWS/MWS/CWS 的 Ideal/Stretch/Shrink
## 3. C 动态模块 (ekp_c)
对于长文本C 模块通过并行化 O(n²) 动态规划阶段提供约 20 倍的加速。
### 源码结构
- `ekp_c/ekp.c`: Emacs 模块入口点。
- `ekp_c/ekp_kp.c`: Knuth-Plass 算法实现。
- `ekp_c/ekp_thread_pool.c`: 工作线程池。
- `ekp_c/ekp_hyphen.c`: Liang 断词算法。
### C API (暴露给 Elisp)
#### `(ekp-c-init)`
初始化模块和线程池。
#### `(ekp-c-break-with-prefixes ...)`
底层 DP 函数。它接收来自 Elisp 的扁平数组(指针):
- 前缀和数组 (ideal, min, max)
- 每个 Box 的胶水参数
- 连字符位置
- 目标行宽
返回断点索引列表和总代价。
### 内存模型
- **零拷贝 (Zero Copy)**: Elisp 直接将向量数据的指针传递给 C。
- **扁平数组**: 数据结构为并行数组,提高缓存效率。
- **线程安全**: 模块使用固定线程池。DP 算法采用波前模式 (Wavefront) 并行化内部循环。
## 4. 算法细节
### Knuth-Plass 算法
基于 1981 年论文 "Breaking Paragraphs into Lines"。
**代价函数 (Demerits):**
`D = (LinePenalty + Badness)² + Penalty²`
**劣度 (Badness):**
`100 * |Adjustment / Flexibility|³`
### CJK 扩展
- **Boxes**: 每个 CJK 字符视为一个独立的 Box。
- **Glues**: 针对 CJK-CJK 和 CJK-Latin 的特定胶水类型允许精细调整间距(例如在汉字和英文之间增加微小的空隙)。
### 断词 (Hyphenation)
使用 Frank Liang 算法TeX 标准)。
- 模式从 `dictionaries/*.dic` 加载。
- `ekp-hyphen.el` 在纯 Elisp 中处理。
- C 模块有自己的实现 (`ekp_hyphen.c`),目前主要由 Elisp 负责分词。