Skip to main content

max / makenotwork

3.8 KB · 123 lines History Blame Raw
1 //! Locate inline code spans and fenced code blocks so substitution can skip
2 //! them, preserving literal `{{ … }}` template syntax shown as examples.
3
4 /// Return byte ranges of inline code spans and fenced code blocks.
5 ///
6 /// Public so that callers who need to reason about the same regions the
7 /// substituter skips (a corpus lint, for instance) can share this definition
8 /// instead of re-deriving it and drifting.
9 pub fn code_span_ranges(input: &str) -> Vec<(usize, usize)> {
10 let mut ranges = Vec::new();
11 let bytes = input.as_bytes();
12 let len = bytes.len();
13 let mut i = 0;
14
15 while i < len {
16 if bytes[i] == b'`' {
17 let start = i;
18 let mut tick_count = 0;
19 while i < len && bytes[i] == b'`' {
20 tick_count += 1;
21 i += 1;
22 }
23 let mut found = false;
24 while i < len {
25 if bytes[i] == b'`' {
26 let mut close_count = 0;
27 while i < len && bytes[i] == b'`' {
28 close_count += 1;
29 i += 1;
30 }
31 if close_count == tick_count {
32 ranges.push((start, i));
33 found = true;
34 break;
35 }
36 } else {
37 i += 1;
38 }
39 }
40 if !found {
41 ranges.push((start, len));
42 }
43 } else {
44 i += 1;
45 }
46 }
47 ranges
48 }
49
50 #[cfg(test)]
51 mod tests {
52 use super::*;
53
54 #[test]
55 fn ranges_inline_code() {
56 let input = "hello `code` world";
57 let ranges = code_span_ranges(input);
58 assert_eq!(ranges.len(), 1);
59 let (start, end) = ranges[0];
60 assert_eq!(&input[start..end], "`code`");
61 }
62
63 #[test]
64 fn ranges_fenced_code() {
65 let input = "text\n```\ncode\n```\nmore";
66 let ranges = code_span_ranges(input);
67 assert_eq!(ranges.len(), 1);
68 let (start, end) = ranges[0];
69 assert!(input[start..end].starts_with("```"));
70 assert!(input[start..end].ends_with("```"));
71 }
72
73 #[test]
74 fn ranges_unclosed_backtick() {
75 let input = "hello `unclosed";
76 let ranges = code_span_ranges(input);
77 assert_eq!(ranges.len(), 1);
78 assert_eq!(ranges[0], (6, input.len()));
79 }
80
81 #[test]
82 fn no_code_spans() {
83 assert!(code_span_ranges("no code here").is_empty());
84 }
85
86 #[test]
87 fn double_backticks_require_double_close() {
88 // ``a`b`` — single ` inside must NOT close the double-tick span.
89 let input = "``a`b``";
90 let ranges = code_span_ranges(input);
91 assert_eq!(ranges.len(), 1, "the inner single ` must not close");
92 assert_eq!(&input[ranges[0].0..ranges[0].1], "``a`b``");
93 }
94
95 #[test]
96 fn mismatched_tick_counts_dont_close_span() {
97 // Open with 1 tick, close attempt with 3 ticks: close_count=3 != tick_count=1.
98 // Span never closes → runs to EOF. Pins `close_count == tick_count`.
99 let input = "`code```";
100 let ranges = code_span_ranges(input);
101 assert_eq!(ranges.len(), 1);
102 assert_eq!(ranges[0], (0, input.len()));
103 }
104
105 #[test]
106 fn multiple_disjoint_spans_get_separate_ranges() {
107 let input = "a `one` b `two` c";
108 let ranges = code_span_ranges(input);
109 assert_eq!(ranges.len(), 2);
110 assert_eq!(&input[ranges[0].0..ranges[0].1], "`one`");
111 assert_eq!(&input[ranges[1].0..ranges[1].1], "`two`");
112 }
113
114 #[test]
115 fn unclosed_span_range_ends_at_input_len() {
116 // Pins the `if !found { ranges.push((start, len)); }` branch.
117 let input = "abc `unclosed";
118 let ranges = code_span_ranges(input);
119 assert_eq!(ranges.len(), 1);
120 assert_eq!(ranges[0], (4, input.len()));
121 }
122 }
123