- 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
2.3 KiB
2.3 KiB
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_replacerust/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:
let start = norm_line.find(&norm_search).unwrap();
let end = start + norm_search.len();
随后代码把 start / end 当成字符序号传给 line.char_indices().nth(...):
&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:
原文:第一段内容
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 等待实现和验证。