/// Strip inline code (backtick) and fenced code blocks, replacing with spaces. #[cfg_attr(not(any(feature = "mentions", test)), allow(dead_code))] pub fn strip_code_spans(input: &str) -> String { let mut out = String::with_capacity(input.len()); let mut chars = input.chars().peekable(); while let Some(ch) = chars.next() { if ch == '`' { let mut tick_count = 1; while chars.peek() == Some(&'`') { tick_count += 1; chars.next(); } let mut skipped = 0; while let Some(c) = chars.next() { skipped += 1; if c == '`' { let mut close_count = 1; while chars.peek() == Some(&'`') { close_count += 1; chars.next(); } if close_count == tick_count { break; } } } let total = tick_count * 2 + skipped; for _ in 0..total { out.push(' '); } } else { out.push(ch); } } out } /// Return byte ranges of inline code spans and fenced code blocks. pub fn code_span_ranges(input: &str) -> Vec<(usize, usize)> { let mut ranges = Vec::new(); let bytes = input.as_bytes(); let len = bytes.len(); let mut i = 0; while i < len { if bytes[i] == b'`' { let start = i; let mut tick_count = 0; while i < len && bytes[i] == b'`' { tick_count += 1; i += 1; } let mut found = false; while i < len { if bytes[i] == b'`' { let mut close_count = 0; while i < len && bytes[i] == b'`' { close_count += 1; i += 1; } if close_count == tick_count { ranges.push((start, i)); found = true; break; } } else { i += 1; } } if !found { ranges.push((start, len)); } } else { i += 1; } } ranges } #[cfg(test)] mod tests { use super::*; #[test] fn strip_inline_code() { let result = strip_code_spans("hello `code` world"); assert!(!result.contains("code")); assert!(result.contains("hello")); assert!(result.contains("world")); } #[test] fn strip_fenced_code() { let result = strip_code_spans("text\n```\ncode block\n```\nmore"); assert!(!result.contains("code block")); assert!(result.contains("text")); assert!(result.contains("more")); } #[test] fn ranges_inline_code() { let input = "hello `code` world"; let ranges = code_span_ranges(input); assert_eq!(ranges.len(), 1); let (start, end) = ranges[0]; assert_eq!(&input[start..end], "`code`"); } #[test] fn ranges_fenced_code() { let input = "text\n```\ncode\n```\nmore"; let ranges = code_span_ranges(input); assert_eq!(ranges.len(), 1); let (start, end) = ranges[0]; assert!(input[start..end].starts_with("```")); assert!(input[start..end].ends_with("```")); } #[test] fn ranges_unclosed_backtick() { let input = "hello `unclosed"; let ranges = code_span_ranges(input); assert_eq!(ranges.len(), 1); assert_eq!(ranges[0], (6, input.len())); } #[test] fn no_code_spans() { assert!(code_span_ranges("no code here").is_empty()); assert_eq!(strip_code_spans("no code here"), "no code here"); } #[test] fn strip_triple_backtick_exact_space_count() { // For ```ab```: tick_count=3, skipped=3 (a,b,`), total = 3*2 + 3 = 9. // Distinguishes `*` from `+` (3+2=5) and pins `+ skipped` vs `- skipped`. let result = strip_code_spans("```ab```"); let spaces = result.chars().filter(|c| *c == ' ').count(); assert_eq!(spaces, 9, "expected 3*2 + 3 = 9 spaces, got {:?}", result); } #[test] fn strip_single_backtick_exact_space_count() { // For `a`: tick_count=1, skipped=2, total = 1*2 + 2 = 4. // Distinguishes `tick_count * 2` from `tick_count + 2` (3 vs 4). let result = strip_code_spans("`a`"); let spaces = result.chars().filter(|c| *c == ' ').count(); assert_eq!(spaces, 4, "expected 1*2 + 2 = 4 spaces, got {:?}", result); } #[test] fn double_backticks_require_double_close() { // ``a`b`` — single ` inside must NOT close the double-tick span. let input = "``a`b``"; let ranges = code_span_ranges(input); assert_eq!(ranges.len(), 1, "the inner single ` must not close"); assert_eq!(&input[ranges[0].0..ranges[0].1], "``a`b``"); } #[test] fn mismatched_tick_counts_dont_close_span() { // Open with 1 tick, close attempt with 3 ticks: close_count=3 != tick_count=1. // Span never closes → runs to EOF. Pins `close_count == tick_count`. let input = "`code```"; let ranges = code_span_ranges(input); assert_eq!(ranges.len(), 1); assert_eq!(ranges[0], (0, input.len())); } #[test] fn multiple_disjoint_spans_get_separate_ranges() { let input = "a `one` b `two` c"; let ranges = code_span_ranges(input); assert_eq!(ranges.len(), 2); assert_eq!(&input[ranges[0].0..ranges[0].1], "`one`"); assert_eq!(&input[ranges[1].0..ranges[1].1], "`two`"); } #[test] fn unclosed_span_range_ends_at_input_len() { // Pins the `if !found { ranges.push((start, len)); }` branch. let input = "abc `unclosed"; let ranges = code_span_ranges(input); assert_eq!(ranges.len(), 1); assert_eq!(ranges[0], (4, input.len())); } }