diff --git a/src/app.rs b/src/app.rs index bc19067..58eedc8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -382,6 +382,7 @@ BgType::Custom(ref path) if !path.is_empty() => { if let Some(section) = action.jump_to_section { self.state.current_section = section; self.state.current_page = 0; + self.state.pending_anchor = None; if let Some(ref anchor) = action.jump_to_anchor { if let Some(ref book) = self.state.book { if section < book.sections.len() { diff --git a/src/reader.rs b/src/reader.rs index d7e04fd..371274d 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -26,11 +26,14 @@ fn parse_blocks(raw_text: &str) -> Vec { if let Some(anchor_end) = after_level.find('\x04') { let anchor_str = &after_level[anchor_start + 1..anchor_end]; anchor = Some(anchor_str.to_string()); + // Remove \x03\x04 from text to prevent anchor ID contamination + let rm_start = pos + 2 + anchor_start; + let rm_end = pos + 2 + anchor_end + 1; + text.replace_range(rm_start..rm_end, ""); } } text.drain(pos..pos + 2); } - text = text.replace(&['\x03', '\x04'][..], ""); text = text.replace('\x02', ""); } let text = text.trim().to_string(); @@ -682,4 +685,26 @@ mod tests { assert_eq!(blocks[0].text, "段一"); assert_eq!(blocks[1].text, "段二"); } + + #[test] + fn test_parse_blocks_heading_with_anchor() { + // \x011\x03toc1\x04Chapter 1\x02 → heading_level=1, anchor="toc1", text="Chapter 1" + let blocks = parse_blocks("\x011\x03toc1\x04Chapter 1\x02\n\nbody text"); + assert_eq!(blocks.len(), 2); + assert_eq!(blocks[0].heading_level, 1); + assert_eq!(blocks[0].anchor.as_deref(), Some("toc1")); + assert_eq!(blocks[0].text, "Chapter 1"); + assert_eq!(blocks[1].heading_level, 0); + assert_eq!(blocks[1].text, "body text"); + } + + #[test] + fn test_parse_blocks_heading_without_anchor() { + // \x011Title\x02 → heading_level=1, anchor=None, text="Title" + let blocks = parse_blocks("\x011Title\x02"); + assert_eq!(blocks.len(), 1); + assert_eq!(blocks[0].heading_level, 1); + assert_eq!(blocks[0].anchor, None); + assert_eq!(blocks[0].text, "Title"); + } }