Files
mnote/bugs/07-ai/process/7-16-markdown-edit-normalized-search-byte-index-v1.md
T
lix-2026 3311bd0366 chore: document architecture gaps and add dev hot reload
- add npm dev:hot wrapper using cargo-watch and page reload polling

- add mnote-web dev hot reload endpoint and coverage

- record current architecture review and tracked bug findings across realtime, tree, editor, and AI runtimes

Verification:

- node scripts/task-dev-hot-plan-test.js

- node --check scripts/dev-hot.js

- cargo test -p mnote-web dev_hot -- --nocapture
2026-05-17 23:09:56 +08:00

62 lines
2.3 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.
# 7-16 [process][bug] mnote.doc.markdown_edit 中文归一化替换 byte index 误用 v1
> 发现时间:2026-05-17
>
> 状态:`[process]`
>
> 关联主线:`07-ai`
>
> 关联代码:
> - `rust/crates/mnote-web/src/hermes_tools/doc.rs:895-956` — `search_replace`
> - `rust/crates/mnote-web/src/hermes_tools/doc.rs:920-936` — 归一化匹配后的切片计算
## 1. 问题定义
`mnote.doc.markdown_edit``search_replace` 在 Level 2 “忽略首尾空白和全角 / 半角差异”分支中,先对 `norm_line` 调用 `find(&norm_search)`,得到的是 UTF-8 byte offset
```rust
let start = norm_line.find(&norm_search).unwrap();
let end = start + norm_search.len();
```
随后代码把 `start / end` 当成字符序号传给 `line.char_indices().nth(...)`
```rust
&line[..line.char_indices().nth(start).map(|(i, _)| i).unwrap_or(0)]
&line[line.char_indices().nth(end).map(|(i, _)| i).unwrap_or(line.len())..]
```
这在 ASCII 文本里不明显,但中文、中文标点、全角字符都是多字节。byte offset 不等于字符序号,最终替换范围会偏移。
## 2. 影响
- 页面 AI 对中文正文执行 `mnote.doc.markdown_edit` 时,归一化匹配可能替换错位置。
- 本地 `.md` 文件和在线 Convex 文档共用该工具,因此两条 AI 编辑路径都会受影响。
- 如果替换结果继续写回,用户看到的正文可能被局部破坏,而不是简单失败。
## 3. 复现思路
构造一行中文正文,让精确匹配失败但归一化匹配命中,例如带首尾空白或全角 / 半角差异的 search:
```text
原文:第一段内容
search 一段
replace:二段
```
`find()` 得到的是 byte offset;当前代码按字符序号切片后,替换边界会落到错误字符位置。
## 4. 根因
`str::find` 返回 byte index`char_indices().nth(n)``n` 是第几个字符。当前实现把两种索引体系混用。
## 5. 建议修复
- 在归一化时保留原文字符到归一化字符的 offset map。
- 或者只在同一字符串上使用 byte index,并确保切片边界来自同一个原始字符串的 `char_indices` 映射。
- 增加中文、多字节标点、全角英文 / 数字混排的 `search_replace` 单测。
## 6. 状态
已确认源码层缺陷,尚未修复。进入 `process` 等待实现和验证。