From 368ababb633453b2be6c9f207ed4584341c7768b Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Tue, 21 Jul 2026 22:44:49 +0800 Subject: docs(spec): update scroll margin to 2 and clarify CJK/IME handling --- ANSI-DRAW.md | 86 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/ANSI-DRAW.md b/ANSI-DRAW.md index 8695754..45f049d 100644 --- a/ANSI-DRAW.md +++ b/ANSI-DRAW.md @@ -23,7 +23,11 @@ 2. 为每个模块编写测试(测试核心逻辑:光标移动、滚动检查、命令解析、ANSI 解析和序列化的对称性、各个编辑模式的行为) 3. 执行 `cargo build`、`cargo test`,保证能构建且测试全部通过 4. 执行 `cargo clippy -- -D warnings` 检查代码质量,修复所有警告直到输出为空。不允许添加任何 clippy 抑制属性 -5. 如果你有子 Agent 能力,生成两个子 Agent,分别对代码做 Review。Review 的重点:是否违反本规范中的硬性约束,是否存在遗漏的边界情况 +5. 使用子 Agent 进行 Review(如果可用)。这一步**非常重要**——你写完代码后会陷入「实现者视角」,对自己的决策有路径依赖,看不见盲区。子 Agent 没有写过这些代码,能**换一个视角**审视你的工作,发现你视而不见的问题。具体做法: + - 生成两个子 Agent(不要用同一个子 Agent 做两次,两个独立的 Agent 才可能发现不同的问题) + - 将整个 `src/` 目录的代码发给它们,同时告诉它们本文档中的「硬性约束」和「边缘情况处理规则」 + - 让它们 Review 的重点:是否违反硬性约束、是否有遗漏的边界情况、是否有逻辑矛盾(如加载和保存的格式不对称) + - 子 Agent 返回后,认真对待每一条意见,确认问题属实后立即修复。如果意见是误报,理解原因后忽略 6. 将已完成的部分以简洁的要点形式写入根目录的 `DONE.md` 文件(写入即可,不需要读取) 7. 读取 `DONE.md` 和本规范对比,检查是否有遗漏的功能点或偏差。不要写入任何文件 8. 执行 `git status` 确认 `ANSI-DRAW.md` 没有任何变化(即你的工作没有修改本文件) @@ -374,9 +378,9 @@ cargo clippy -- -D warnings ### 滚动行为 -边距常量:8。 +边距常量:2。 -当光标移动后,检查光标是否位于视口的「边缘区域」。边缘区域定义为视口内距离边界 8 格以内的范围。 +当光标移动后,检查光标是否位于视口的「边缘区域」。边缘区域定义为视口内距离边界 2 格以内的范围。 如果光标移动到了边缘区域之外(即离某一侧边界的距离小于 8),则相机跟随光标移动,使光标回到边缘区域内。具体规则: @@ -389,7 +393,7 @@ cargo clippy -- -D warnings ### 加载文件时的初始位置 -加载文件后,将相机位置设置为:相机 x = 0,相机 y = 0。然后执行一次滚动检查(使第一个字符 `(0, 0)` 位于距离视口右上角 8 格的位置)。 +加载文件后,将相机位置设置为:相机 x = 0,相机 y = 0。然后执行一次滚动检查(使第一个字符 `(0, 0)` 位于距离视口右上角 2 格的位置)。 --- @@ -407,6 +411,14 @@ cargo clippy -- -D warnings - 在画布坐标计算中,所有坐标使用「格子索引」而非「终端列数」。即两个相邻格子坐标的差值是 1(不论格子中的字符宽度是多少)。Unicode 宽度只在渲染和光标移动时用于计算终端列数偏移量 - 选区计算:矩形选区的边界基于格子索引,不受字符宽度影响 +### 中日韩文字与 IME 输入 + +- 「可打印字符」的定义范围是:所有非控制字符(不是 ASCII 控制字符 0x00-0x1F 的 Unicode 字符)。包括但不限于 ASCII 字母数字、标点、空格、中日韩统一表意文字(CJK)、emoji 等 +- 绑定表中「任何可打印 Unicode 字符」作用于替换/输入/插入模式时,对每个输入的字符产生一个独立的 `InputChar` 命令 +- 输入法(IME)的选词弹出窗口由终端/操作系统管理,ANSI-DRAW 不做干预。IME 确认提交(commit)后,每个提交的字符作为一个独立的 `KeyCode::Char` 事件到达程序,逐一处理 +- 终端键盘自动连发(按住一个键持续输入)由终端驱动层处理,每个重复的按键事件独立到达,逐一产生 `InputChar` 命令 +- 宽度为 2 的字符(如大部分 CJK 文字)在光标移动和渲染时占 2 个终端列,但画布坐标仍以一个格子计数 + --- ## 命令输入语法 @@ -595,38 +607,38 @@ Hello,A[B] World 以下表格定义了所有按键绑定。每个条目表示在某模式下按下某按键后应执行的命令序列。 -| 按键 | 模式 | 命令序列 | -| ---------------------- | ----------------------------------------- | ---------------------------------------------------------------------------- | -| 左箭头 KeyLeft | 所有模式 | [MoveLeft](然后执行通用滚动检查) | -| 右箭头 KeyRight | 所有模式 | [MoveRight](然后执行通用滚动检查) | -| 上箭头 KeyUp | 所有模式 | [MoveUp](然后执行通用滚动检查) | -| 下箭头 KeyDown | 所有模式 | [MoveDown](然后执行通用滚动检查) | -| ESC | 插入/输入/替换/命令/前景绘画/背景绘画模式 | [ModeNavigate] | -| ESC | 选择模式 | [ModeNavigate, CancelSelection] | -| R | 导览模式 | [ModeReplace] | -| I | 导览模式 | [ModeInsert] | -| A | 导览模式 | [ModeInput] | -| F | 导览模式 | [ModePaintFg] | -| B | 导览模式 | [ModePaintBg] | -| : | 导览模式 | [ModeCommand] | -| V | 导览模式 | [ModeVisual] | -| Y | 插入/输入/替换模式 | [Yank] | -| Y | 选择模式 | [VisualYank, CancelSelection] | -| D | 选择模式 | [VisualDelete] | -| P | 插入/输入/替换模式 | [Paste](Paste 的行为依当前模式而定) | -| Backspace | 插入模式 | [BackspaceSquash] | -| Backspace | 输入/替换模式 | [Backspace] | -| 可打印字符(含空格) | 替换模式 | [InputChar(该字符)]——InputChar 命令的执行逻辑见替换模式定义 | -| 可打印字符(含空格) | 输入模式 | [InputChar(该字符)]——执行逻辑见输入模式定义 | -| 可打印字符(含空格) | 插入模式 | [InputChar(该字符)]——执行逻辑见插入模式定义 | -| 可打印字母(a-Z、A-Z) | 前景绘画模式 | [SetColor(对应颜色)] | -| 可打印字母(a-Z、A-Z) | 背景绘画模式 | [SetColor(对应颜色)] | -| 回车键 Enter | 命令输入模式 | 解析 cmd_buffer 得到命令列表,顺序执行,然后 [ModeNavigate],清空 cmd_buffer | -| ESC | 命令输入模式 | 清空 cmd_buffer,[ModeNavigate] | -| Backspace | 命令输入模式 | 删除 cmd_buffer 最后一个字符 | -| 可打印字符(含空格) | 命令输入模式 | 追加到 cmd_buffer | - -注意:「命令输入模式」下的可打印字符不产生 InputChar 命令,而是直接修改 cmd_buffer 字符串。这不是通过命令系统完成的,而是对 App 状态的直接操作。 +| 按键 | 模式 | 命令序列 | +| ----------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------- | +| 左箭头 KeyLeft | 所有模式 | [MoveLeft](然后执行通用滚动检查) | +| 右箭头 KeyRight | 所有模式 | [MoveRight](然后执行通用滚动检查) | +| 上箭头 KeyUp | 所有模式 | [MoveUp](然后执行通用滚动检查) | +| 下箭头 KeyDown | 所有模式 | [MoveDown](然后执行通用滚动检查) | +| ESC | 插入/输入/替换/命令/前景绘画/背景绘画模式 | [ModeNavigate] | +| ESC | 选择模式 | [ModeNavigate, CancelSelection] | +| R | 导览模式 | [ModeReplace] | +| I | 导览模式 | [ModeInsert] | +| A | 导览模式 | [ModeInput] | +| F | 导览模式 | [ModePaintFg] | +| B | 导览模式 | [ModePaintBg] | +| : | 导览模式 | [ModeCommand] | +| V | 导览模式 | [ModeVisual] | +| Y | 插入/输入/替换模式 | [Yank] | +| Y | 选择模式 | [VisualYank, CancelSelection] | +| D | 选择模式 | [VisualDelete] | +| P | 插入/输入/替换模式 | [Paste](Paste 的行为依当前模式而定) | +| Backspace | 插入模式 | [BackspaceSquash] | +| Backspace | 输入/替换模式 | [Backspace] | +| 任何可打印 Unicode 字符(含空格、中文等) | 替换模式 | [InputChar(该字符)]——InputChar 命令的执行逻辑见替换模式定义 | +| 任何可打印 Unicode 字符(含空格、中文等) | 输入模式 | [InputChar(该字符)]——执行逻辑见输入模式定义 | +| 任何可打印 Unicode 字符(含空格、中文等) | 插入模式 | [InputChar(该字符)]——执行逻辑见插入模式定义 | +| 可打印字母(a-Z、A-Z) | 前景绘画模式 | [SetColor(对应颜色)] | +| 可打印字母(a-Z、A-Z) | 背景绘画模式 | [SetColor(对应颜色)] | +| 回车键 Enter | 命令输入模式 | 解析 cmd_buffer 得到命令列表,顺序执行,然后 [ModeNavigate],清空 cmd_buffer | +| ESC | 命令输入模式 | 清空 cmd_buffer,[ModeNavigate] | +| Backspace | 命令输入模式 | 删除 cmd_buffer 最后一个字符 | +| 任何可打印 Unicode 字符 | 命令输入模式 | 追加到 cmd_buffer | + +注意:「命令输入模式」下的任何可打印 Unicode 字符不产生 InputChar 命令,而是直接追加到 cmd_buffer 字符串。这不是通过命令系统完成的,而是对 App 状态的直接操作。 --- @@ -636,7 +648,7 @@ Hello,A[B] World 1. 解析命令行参数。如果提供了一个参数,将其作为文件路径存入 `file_path`,读取文件内容并调用 ANSI 解析函数加载到画布。如果文件不存在或读取失败,设置 `msg` 为错误描述,画布保持为空 2. 如果没有提供参数,画布为空,`file_path` 为无值 -3. 相机初始位置为 (0, 0)。如果加载了文件,执行一次滚动检查(使 (0, 0) 位于距右上角 8 格的位置) +3. 相机初始位置为 (0, 0)。如果加载了文件,执行一次滚动检查(使 (0, 0) 位于距右上角 2 格的位置) 4. 模式为导览模式 5. 所有其他字段为初始默认值 -- cgit