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 trimmed = line.trim();
15
16 if trimmed.len() < 3 {
18 return None;
19 }
20
21 let rule_char = trimmed.chars().next()?;
23 if !matches!(rule_char, '*' | '-' | '_') {
24 return None;
25 }
26
27 let mut count = 0;
29 for ch in trimmed.chars() {
30 match ch {
31 c if c == rule_char => count += 1,
32 ' ' | '\t' => continue,
33 _ => return None,
34 }
35 }
36
37 if count >= 3 { Some(rule_char) } else { None }
39}
40
41pub(crate) fn emit_horizontal_rule(builder: &mut GreenNodeBuilder<'static>, line: &str) {
43 builder.start_node(SyntaxKind::HORIZONTAL_RULE.into());
44
45 let (line_without_newline, newline_str) = strip_newline(line);
47 builder.token(SyntaxKind::HORIZONTAL_RULE.into(), line_without_newline);
48
49 if !newline_str.is_empty() {
51 builder.token(SyntaxKind::NEWLINE.into(), newline_str);
52 }
53
54 builder.finish_node();
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn test_asterisk_rule() {
63 assert_eq!(try_parse_horizontal_rule("***"), Some('*'));
64 assert_eq!(try_parse_horizontal_rule("* * *"), Some('*'));
65 assert_eq!(try_parse_horizontal_rule("* * *"), Some('*'));
66 assert_eq!(try_parse_horizontal_rule("****"), Some('*'));
67 }
68
69 #[test]
70 fn test_dash_rule() {
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_underscore_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_with_leading_trailing_spaces() {
85 assert_eq!(try_parse_horizontal_rule(" *** "), Some('*'));
86 assert_eq!(try_parse_horizontal_rule("\t---\t"), Some('-'));
87 }
88
89 #[test]
90 fn test_too_few_characters() {
91 assert_eq!(try_parse_horizontal_rule("**"), None);
92 assert_eq!(try_parse_horizontal_rule("--"), None);
93 assert_eq!(try_parse_horizontal_rule("__"), None);
94 }
95
96 #[test]
97 fn test_mixed_characters() {
98 assert_eq!(try_parse_horizontal_rule("*-*"), None);
99 assert_eq!(try_parse_horizontal_rule("*_*"), None);
100 }
101
102 #[test]
103 fn test_with_other_content() {
104 assert_eq!(try_parse_horizontal_rule("*** hello"), None);
105 assert_eq!(try_parse_horizontal_rule("---a"), None);
106 }
107
108 #[test]
109 fn test_empty_line() {
110 assert_eq!(try_parse_horizontal_rule(""), None);
111 assert_eq!(try_parse_horizontal_rule(" "), None);
112 }
113}