|
1 | 1 | # [221_10] 为有序列表、无序列表和描述列表新增结构化向上/向下插入与删除 |
2 | 2 |
|
| 3 | +> 相关文档:[201_31](201_31.md)、[x_y.md](x_y.md)(模板) |
| 4 | +
|
3 | 5 | ## 如何测试 |
4 | 6 |
|
5 | 7 | ### 一、单元测试 |
|
40 | 42 | 1. 通过菜单 **Text -> Enumerate -> Default** 插入有序列表,输入至少两个条目。 |
41 | 43 | 2. 重复无序列表测试中的步骤 3-9。 |
42 | 44 | 3. 确认插入新条目后,编号自动重新排列。 |
| 45 | +4. **结构化向下删除至空文档**:新建一个空白文档,仅插入一个有序列表并输入一个条目。将光标放在该条目内,按 **Alt + Delete** 删除唯一条目。确认: |
| 46 | + - Mogan **不崩溃**(不会触发 "Throwing empty composite box" 错误)。 |
| 47 | + - 列表被自动移除,文档变为一个包含空字符串的普通段落,光标位于该段落内。 |
| 48 | +5. **结构化向下删除后仍有内容**:新建一个文档,先输入一段普通文字,再插入一个有序列表并输入一个条目。将光标放在列表条目内,按 **Alt + Delete** 删除唯一条目。确认: |
| 49 | + - 列表节点被移除,但前面的普通文字保留。 |
| 50 | + - 光标位于列表原来的位置(文字之后)。 |
43 | 51 |
|
44 | 52 | ### 四、描述列表(description) |
45 | 53 |
|
46 | 54 | 1. 通过菜单 **Text -> Description -> Default** 插入描述列表,输入至少两个条目,每个条目包含标签和描述内容。 |
47 | | -2. 将光标放在**第二个条目内部**,按 **Alt + Up**(macOS 为 **Option + Up**),确认在当前条目**上方**插入一个新的空描述条目(`item*`),且光标进入新条目的描述内容区。 |
48 | | -3. 将光标放在**第一个条目内部**,按 **Alt + Down**(macOS 为 **Option + Down**),确认在当前条目**下方**插入一个新的空描述条目(`item*`),且光标进入新条目的描述内容区。 |
| 55 | +2. 将光标放在**第二个条目内部**,按 **Alt + Up**(macOS 为 **Option + Up**),确认在当前条目**上方**插入一个新的空描述条目(`item* ""`),且光标位于 `item*` 子节点 `""` 的开始位置(`tree-go-to new-item 0 :start`)。 |
| 56 | +3. 将光标放在**第一个条目内部**,按 **Alt + Down**(macOS 为 **Option + Down**),确认在当前条目**下方**插入一个新的空描述条目(`item* ""`),且光标位于 `item*` 子节点 `""` 的开始位置(`tree-go-to new-item 0 :start`)。 |
49 | 57 | 4. 将光标放在描述列表条目内部,查看焦点工具栏,确认出现 **Insert above**、**Insert below**、**Remove upwards**、**Remove downwards** 四个结构化按钮,且**不出现**左右方向的插入/删除按钮。 |
50 | 58 | 5. 按 **Alt + Backspace**(macOS 为 **Option + Backspace**),确认删除当前 item 上方的同级逻辑 item(包括标签和描述内容)。 |
51 | 59 | 6. 按 **Alt + Delete**(macOS 为 **Option + Delete**),确认删除当前 item 下方的同级逻辑 item(包括标签和描述内容)。 |
52 | | -7. 确认在描述列表中插入的新条目结构为 `item*`(而非普通列表的 `item`)。 |
| 60 | +7. 确认在描述列表中插入的新条目结构为 `item* ""`(而非普通列表的 `item`,也非无子节点的 `item*`)。 |
| 61 | + |
| 62 | +## 2026/05/08 修复描述列表插入结构与光标定位,修复空文档保护 |
| 63 | + |
| 64 | +### What |
| 65 | + |
| 66 | +1. **描述列表插入结构修复**:`blank-list-item-stree` 对 `description` 类型返回 `(item* "")` 而非 `(item*)`,确保新插入的描述条目包含一个空的标签/描述内容子节点。 |
| 67 | +2. **描述列表光标定位修复**:`structured-insert-vertical` 在插入 `item*` 后,通过 `(tree-go-to new-item 0 :end)` 将光标放入 `item*` 的子节点内部,而不是停留在 `item*` 节点上。 |
| 68 | +3. **空文档保护修复**:`structured-remove-vertical` 在删除列表节点后,若父 `document` 的 arity 变为 0,则自动插入一个空字符串 `""` 作为占位,避免产生空的 document 触发 `TM_FAILED ("empty composite box")`。 |
| 69 | + |
| 70 | +### Why |
| 71 | + |
| 72 | +1. 描述列表的 `item*` 必须包含一个子节点作为标签/描述内容,插入 `(item*)` 会导致结构不完整,后续编辑时行为异常。 |
| 73 | +2. 光标停留在 `item*` 节点上时,用户直接输入文字不会进入标签/描述内容区,需要额外的 Enter 或点击操作才能开始编辑,体验不符合预期。 |
| 74 | +3. 当整个文档仅包含一个有序/无序/描述列表时,结构化删除所有条目会导致列表节点被移除,根 `document` 变为空。空的 `document` 在排版阶段会创建没有任何子盒子的 composite box,触发 `composite_boxes.cpp:67` 的 `TM_FAILED ("empty composite box")`,导致 Mogan 崩溃。 |
| 75 | + |
| 76 | +### How |
| 77 | + |
| 78 | +1. `blank-list-item-stree` 中 `description` 分支返回 `` `(item* "") ``。 |
| 79 | +2. `structured-insert-vertical` 的光标定位逻辑增加对 `item*` 的判断: |
| 80 | + ```scheme |
| 81 | + (if (tree-is? new-item 'item*) |
| 82 | + (tree-go-to new-item 0 :end) |
| 83 | + (tree-go-to new-list insert-pos :end)) |
| 84 | + ``` |
| 85 | +3. `structured-remove-vertical` 在删除列表节点后增加空文档保护: |
| 86 | + ```scheme |
| 87 | + (when (and parent-doc list-index) |
| 88 | + (tree-remove parent-doc list-index 1) |
| 89 | + (if (== (tree-arity parent-doc) 0) |
| 90 | + (begin |
| 91 | + (tree-insert parent-doc 0 (list "")) |
| 92 | + (tree-go-to parent-doc 0 :end)) |
| 93 | + (tree-go-to parent-doc list-index :end))) |
| 94 | + ``` |
| 95 | +4. 单元测试 `221_10.scm` 新增 `blank-list-item-stree` 的 `description` 类型断言、以及 `only-list-doc` 场景的结构化删除断言。 |
53 | 96 |
|
54 | 97 | ## 2026/05/07 新增列表结构化向上/向下插入与删除 |
55 | 98 |
|
|
0 commit comments