# 修复有序列表中缩进代码块导致编号重置的问题
## 问题描述
当有序列表项之间包含缩进的代码块时,列表编号会错误地重新从 1 开始,而不是继续递增。
**示例:**
```markdown
1. aaa
```c
int a = 0;
```
2. bbb
3. ccc
```
当前会被格式化为:
```markdown
1. aaa
```c
int a = 0;
```
1. bbb ← 错误:应该是 2
2. ccc ← 错误:应该是 3
```
## 根本原因
在 `format_lists` 函数(src/main.rs:318)中,当遇到非列表的非空行时会清空 `list_stack`。缩进的内容(如代码块内的代码行)被误认为是"非列表内容",导致列表上下文被清除。
## 解决方案
修改 `format_lists` 函数的逻辑,使其能够识别并正确处理缩进的内容:
- **空行**:保持 list_stack(已实现)
- **缩进内容**(以空格开头的行):保持 list_stack,视为列表项的续行内容
- **非缩进的非空行**:清空 list_stack,表示列表结束
## 待办任务
- [x] 设计并添加测试用例
- [x] 测试有序列表中间包含缩进代码块
- [x] 测试有序列表中间包含缩进的普通文本
- [x] 测试有序列表中间包含多个缩进元素
- [x] 测试嵌套列表中的缩进代码块
- [x] 修复 `format_lists` 函数的逻辑
- [x] 运行所有测试,确保没有破坏现有功能
- [x] 验证修复效果
## 实现细节
### 测试用例设计
1. **基本场景**:有序列表 + 缩进代码块 + 有序列表
2. **复杂场景**:多层嵌套列表 + 缩进代码块
3. **混合场景**:有序列表 + 缩进文本 + 缩进代码块
### 代码修改
修改 `format_lists` 函数中的非列表行处理逻辑(约318行):
```rust
} else {
// Non-list line
if line.is_empty() {
// Empty line: might be a separator within the same list, keep list_stack
result.push(line.clone());
} else if line.starts_with(' ') || line.starts_with('\t') {
// Indented content: part of the list item (code blocks, continued text, etc.)
// Keep list_stack intact
result.push(line.clone());
} else {
// Real non-list content (text, heading, code, etc.): end the list
list_stack.clear();
result.push(line.clone());
}
}
```
## 验证计划
1. 运行新增的测试用例
2. 运行所有现有测试:`cargo test`
3. 手动测试用户提供的示例
4. 测试边界情况
## 评审
### 修复总结
本次修复成功解决了有序列表中缩进代码块导致编号重置的问题。修改涉及两个核心函数:
#### 1. `format_lists` 函数(src/main.rs:312-326)
**修改内容:**
在处理非列表行时,增加了对缩进内容的判断:
```rust
} else {
// Non-list line
if line.is_empty() {
// Empty line: might be a separator within the same list, keep list_stack
result.push(line.clone());
} else if line.starts_with(' ') || line.starts_with('\t') {
// Indented content: part of the list item (code blocks, continued text, etc.)
// Keep list_stack intact
result.push(line.clone());
} else {
// Real non-list content (text, heading, code, etc.): end the list
list_stack.clear();
result.push(line.clone());
}
}
```
**修复逻辑:**
- 空行:保持列表栈不变(原有逻辑)
- 缩进内容(以空格或制表符开头):保持列表栈不变,视为列表项的续行内容(**新增**)
- 非缩进非空行:清空列表栈,表示列表结束(原有逻辑)
#### 2. `format_lines` 函数(src/main.rs:139-225)
**修改内容:**
- 添加了 `prev_line` 变量来跟踪前一行的内容
- 修改了 `LineState::List` 的处理逻辑:
```rust
LineState::List => {
if prev_line_state != LineState::List && prev_line_state != LineState::Empty {
// Don't add blank line if previous line is indented content (part of list)
if !(prev_line_state == LineState::Normal && prev_line.starts_with(' ')) {
ret.push(String::new());
}
}
ret.push(format_line(line));
}
```
**修复逻辑:**
在列表项之前添加空行时,检查前一行是否是缩进的 Normal 行。如果是,说明前一行是列表项的续行内容(如缩进的代码块),不应该插入空行。
### 测试覆盖
新增了 `test_ordered_list_with_indented_code_block` 测试函数,包含 5 个测试用例:
1. **基本场景**:有序列表 + 缩进代码块
2. **缩进文本**:有序列表 + 缩进的普通文本
3. **多个缩进元素**:有序列表 + 多种缩进内容
4. **嵌套列表**:嵌套列表 + 缩进代码块
5. **列表分隔**:有序列表 + 缩进代码块 + 非缩进文本(应重置编号)
### 测试结果
- ✅ 所有 10 个单元测试通过
- ✅ 所有 6 个集成测试通过
- ✅ 用户提供的示例验证成功
### 影响范围
- 修改了 2 个核心函数
- 新增了 1 个测试函数(5 个测试用例)
- 没有破坏任何现有功能
- 完全向后兼容