panache_parser/parser/inlines/
strikeout.rs1use super::core::parse_inline_text;
12use crate::options::ParserOptions;
13use crate::syntax::SyntaxKind;
14use rowan::GreenNodeBuilder;
15
16pub fn try_parse_strikeout(text: &str) -> Option<(usize, &str)> {
19 let bytes = text.as_bytes();
20
21 if bytes.len() < 4 || bytes[0] != b'~' || bytes[1] != b'~' {
23 return None;
24 }
25
26 if bytes.get(2) == Some(&b'~') {
28 return None;
29 }
30
31 let mut pos = 2;
33 let mut found_close = false;
34
35 while pos + 1 < bytes.len() {
36 if bytes[pos] == b'~' && bytes[pos + 1] == b'~' {
37 if pos + 2 < bytes.len() && bytes[pos + 2] == b'~' {
39 pos += 1;
40 continue;
41 }
42
43 found_close = true;
44 break;
45 }
46 pos += 1;
47 }
48
49 if !found_close {
50 return None;
51 }
52
53 let content = &text[2..pos];
55
56 if content.trim().is_empty() {
58 return None;
59 }
60
61 if content.starts_with(char::is_whitespace) || content.ends_with(char::is_whitespace) {
63 return None;
64 }
65
66 let total_len = pos + 2; Some((total_len, content))
68}
69
70pub fn emit_strikeout(builder: &mut GreenNodeBuilder, inner_text: &str, config: &ParserOptions) {
72 builder.start_node(SyntaxKind::STRIKEOUT.into());
73
74 builder.start_node(SyntaxKind::STRIKEOUT_MARKER.into());
76 builder.token(SyntaxKind::STRIKEOUT_MARKER.into(), "~~");
77 builder.finish_node();
78
79 parse_inline_text(builder, inner_text, config, false);
81
82 builder.start_node(SyntaxKind::STRIKEOUT_MARKER.into());
84 builder.token(SyntaxKind::STRIKEOUT_MARKER.into(), "~~");
85 builder.finish_node();
86
87 builder.finish_node();
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_simple_strikeout() {
96 assert_eq!(try_parse_strikeout("~~hello~~"), Some((9, "hello")));
97 }
98
99 #[test]
100 fn test_strikeout_with_spaces() {
101 assert_eq!(
102 try_parse_strikeout("~~hello world~~"),
103 Some((15, "hello world"))
104 );
105 }
106
107 #[test]
108 fn test_no_whitespace_inside_delimiters() {
109 assert_eq!(try_parse_strikeout("~~ hello~~"), None);
111
112 assert_eq!(try_parse_strikeout("~~hello ~~"), None);
114 }
115
116 #[test]
117 fn test_empty_content() {
118 assert_eq!(try_parse_strikeout("~~~~"), None);
119 assert_eq!(try_parse_strikeout("~~ ~~"), None);
120 }
121
122 #[test]
123 fn test_not_enough_tildes() {
124 assert_eq!(try_parse_strikeout("~hello~"), None);
125 }
126
127 #[test]
128 fn test_too_many_tildes() {
129 assert_eq!(try_parse_strikeout("~~~hello~~~"), None);
131 }
132
133 #[test]
134 fn test_no_closing() {
135 assert_eq!(try_parse_strikeout("~~hello"), None);
136 assert_eq!(try_parse_strikeout("~~hello world"), None);
137 }
138
139 #[test]
140 fn test_strikeout_with_other_content_after() {
141 assert_eq!(try_parse_strikeout("~~hello~~ world"), Some((9, "hello")));
142 }
143
144 #[test]
145 fn test_strikeout_in_middle() {
146 assert_eq!(try_parse_strikeout("~~text~~"), Some((8, "text")));
147 }
148}