panache_parser/parser/blocks/
horizontal_rules.rs1use crate::syntax::SyntaxKind;
4use rowan::GreenNodeBuilder;
5
6use crate::parser::utils::helpers::strip_newline;
7
8pub(crate) fn try_parse_horizontal_rule(line: &str) -> Option<char> {
14 let leading = line.bytes().take_while(|b| *b == b' ').count();
18 if leading >= 4 {
19 return None;
20 }
21 let trimmed = line.trim();
22
23 if trimmed.len() < 3 {
25 return None;
26 }
27
28 let rule_char = trimmed.chars().next()?;
30 if !matches!(rule_char, '*' | '-' | '_') {
31 return None;
32 }
33
34 let mut count = 0;
36 for ch in trimmed.chars() {
37 match ch {
38 c if c == rule_char => count += 1,
39 ' ' | '\t' => continue,
40 _ => return None,
41 }
42 }
43
44 if count >= 3 { Some(rule_char) } else { None }
46}
47
48pub(crate) fn emit_horizontal_rule(builder: &mut GreenNodeBuilder<'static>, line: &str) {
50 builder.start_node(SyntaxKind::HORIZONTAL_RULE.into());
51
52 let (line_without_newline, newline_str) = strip_newline(line);
54 builder.token(SyntaxKind::HORIZONTAL_RULE.into(), line_without_newline);
55
56 if !newline_str.is_empty() {
58 builder.token(SyntaxKind::NEWLINE.into(), newline_str);
59 }
60
61 builder.finish_node();
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn test_asterisk_rule() {
70 assert_eq!(try_parse_horizontal_rule("***"), Some('*'));
71 assert_eq!(try_parse_horizontal_rule("* * *"), Some('*'));
72 assert_eq!(try_parse_horizontal_rule("* * *"), Some('*'));
73 assert_eq!(try_parse_horizontal_rule("****"), Some('*'));
74 }
75
76 #[test]
77 fn test_dash_rule() {
78 assert_eq!(try_parse_horizontal_rule("---"), Some('-'));
79 assert_eq!(try_parse_horizontal_rule("- - -"), Some('-'));
80 assert_eq!(try_parse_horizontal_rule("---------------"), Some('-'));
81 }
82
83 #[test]
84 fn test_underscore_rule() {
85 assert_eq!(try_parse_horizontal_rule("___"), Some('_'));
86 assert_eq!(try_parse_horizontal_rule("_ _ _"), Some('_'));
87 assert_eq!(try_parse_horizontal_rule("_____"), Some('_'));
88 }
89
90 #[test]
91 fn test_with_leading_trailing_spaces() {
92 assert_eq!(try_parse_horizontal_rule(" *** "), Some('*'));
93 assert_eq!(try_parse_horizontal_rule("\t---\t"), Some('-'));
94 }
95
96 #[test]
97 fn test_too_few_characters() {
98 assert_eq!(try_parse_horizontal_rule("**"), None);
99 assert_eq!(try_parse_horizontal_rule("--"), None);
100 assert_eq!(try_parse_horizontal_rule("__"), None);
101 }
102
103 #[test]
104 fn test_mixed_characters() {
105 assert_eq!(try_parse_horizontal_rule("*-*"), None);
106 assert_eq!(try_parse_horizontal_rule("*_*"), None);
107 }
108
109 #[test]
110 fn test_with_other_content() {
111 assert_eq!(try_parse_horizontal_rule("*** hello"), None);
112 assert_eq!(try_parse_horizontal_rule("---a"), None);
113 }
114
115 #[test]
116 fn test_empty_line() {
117 assert_eq!(try_parse_horizontal_rule(""), None);
118 assert_eq!(try_parse_horizontal_rule(" "), None);
119 }
120}