panache_parser/parser/inlines/
raw_inline.rs1use super::sink::InlineSink;
13use crate::parser::utils::attributes::{AttributeBlock, emit_attribute_node};
14use crate::syntax::SyntaxKind;
15
16pub fn is_raw_inline(attributes: &AttributeBlock) -> Option<&str> {
19 if attributes.identifier.is_some() || !attributes.key_values.is_empty() {
22 return None;
23 }
24
25 if attributes.classes.len() == 1 {
26 let class = &attributes.classes[0];
27 if let Some(format) = class.strip_prefix('=')
28 && !format.is_empty()
29 {
30 return Some(format);
31 }
32 }
33
34 None
35}
36
37pub fn emit_raw_inline(
44 builder: &mut impl InlineSink,
45 content: &str,
46 backtick_count: usize,
47 attr_raw: &str,
48) {
49 builder.start_node(SyntaxKind::RAW_INLINE.into());
50
51 builder.token(
53 SyntaxKind::RAW_INLINE_MARKER.into(),
54 &"`".repeat(backtick_count),
55 );
56
57 builder.token(SyntaxKind::RAW_INLINE_CONTENT.into(), content);
59
60 builder.token(
62 SyntaxKind::RAW_INLINE_MARKER.into(),
63 &"`".repeat(backtick_count),
64 );
65
66 emit_attribute_node(builder, attr_raw);
68
69 builder.finish_node();
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use crate::parser::utils::attributes::AttributeBlock;
76
77 #[test]
78 fn test_is_raw_inline_html() {
79 let attrs = AttributeBlock {
80 identifier: None,
81 classes: vec!["=html".to_string()],
82 key_values: vec![],
83 };
84 assert_eq!(is_raw_inline(&attrs), Some("html"));
85 }
86
87 #[test]
88 fn test_is_raw_inline_latex() {
89 let attrs = AttributeBlock {
90 identifier: None,
91 classes: vec!["=latex".to_string()],
92 key_values: vec![],
93 };
94 assert_eq!(is_raw_inline(&attrs), Some("latex"));
95 }
96
97 #[test]
98 fn test_is_raw_inline_openxml() {
99 let attrs = AttributeBlock {
100 identifier: None,
101 classes: vec!["=openxml".to_string()],
102 key_values: vec![],
103 };
104 assert_eq!(is_raw_inline(&attrs), Some("openxml"));
105 }
106
107 #[test]
108 fn test_not_raw_inline_regular_class() {
109 let attrs = AttributeBlock {
110 identifier: None,
111 classes: vec!["python".to_string()],
112 key_values: vec![],
113 };
114 assert_eq!(is_raw_inline(&attrs), None);
115 }
116
117 #[test]
118 fn test_not_raw_inline_with_id() {
119 let attrs = AttributeBlock {
120 identifier: Some("myid".to_string()),
121 classes: vec!["=html".to_string()],
122 key_values: vec![],
123 };
124 assert_eq!(is_raw_inline(&attrs), None);
125 }
126
127 #[test]
128 fn test_not_raw_inline_with_key_value() {
129 let attrs = AttributeBlock {
130 identifier: None,
131 classes: vec!["=html".to_string()],
132 key_values: vec![("key".to_string(), "value".to_string())],
133 };
134 assert_eq!(is_raw_inline(&attrs), None);
135 }
136
137 #[test]
138 fn test_not_raw_inline_multiple_classes() {
139 let attrs = AttributeBlock {
140 identifier: None,
141 classes: vec!["=html".to_string(), "other".to_string()],
142 key_values: vec![],
143 };
144 assert_eq!(is_raw_inline(&attrs), None);
145 }
146
147 #[test]
148 fn test_not_raw_inline_empty_format() {
149 let attrs = AttributeBlock {
150 identifier: None,
151 classes: vec!["=".to_string()],
152 key_values: vec![],
153 };
154 assert_eq!(is_raw_inline(&attrs), None);
155 }
156
157 #[test]
161 fn raw_inline_attribute_is_structured_and_lossless() {
162 let input = "`<a>`{=html}\n";
163 let tree = crate::parse(input, None);
164 assert_eq!(tree.text().to_string(), input);
165
166 let attr = tree
167 .descendants()
168 .find(|n| n.kind() == SyntaxKind::ATTRIBUTE)
169 .expect("ATTRIBUTE node under RAW_INLINE");
170 assert_eq!(attr.text().to_string(), "{=html}");
171 let class = attr
172 .children_with_tokens()
173 .find(|el| el.kind() == SyntaxKind::ATTR_CLASS)
174 .and_then(|el| el.into_token())
175 .expect("ATTR_CLASS token");
176 assert_eq!(class.text(), "=html");
177 }
178
179 #[test]
182 fn raw_inline_attribute_preserves_interior_whitespace() {
183 let input = "`<a>`{ =html }\n";
184 let tree = crate::parse(input, None);
185 assert_eq!(tree.text().to_string(), input);
186 }
187}