1use std::collections::HashMap;
2
3use once_cell::sync::Lazy;
4use regex::Regex;
5
6use super::{base_modifier::BaseModifier, constants::{build_strict_reserved_line_pattern, IDENTIFIER_PATTERN, MULTI_LINES_CONTENT_PATTERN, MULTI_LINES_CONTENT_EXCLUDING_HEADINGS_PATTERN, NEW_LINE_PATTERN, STYLE_PATTERN}, ModifierIdentifier, ModifierPattern, ModifiersBucket};
7
8
9pub const PARAGRAPH_SEPARATOR_START: &str = r"(?m:^[ \t]*\r?\n)+";
10pub const PARAGRAPH_SEPARATOR_END: &str = r"(?m:[ \t]*\r?\n){2}";
11
12
13static MODIFIER_PATTERNS_REGEX: Lazy<HashMap<ModifierIdentifier, Regex>> = Lazy::new(|| {
14 let mut regex: HashMap<ModifierIdentifier, Regex> = HashMap::new();
15
16 StandardParagraphModifier::ordered().into_iter().for_each(|m| {
17 regex.insert(m.identifier(), Regex::new(&m.modifier_pattern()).unwrap());
18 });
19
20 regex
21});
22
23
24
25#[derive(Debug, PartialEq, Clone)]
26pub enum StandardParagraphModifier {
27 List,
28 ListItem,
29 Table,
30 Image,
31 AbridgedImage,
32 MultiImage,
33 CodeBlock,
34 CommentBlock,
35 ExtendedBlockQuote,
36 ExtendedBlockQuoteLine,
37 FocusBlock,
38 MathBlock,
39 LineBreakDash,
40 LineBreakStar,
41 LineBreakPlus,
42 CommonParagraph,
43 EmbeddedParagraphStyle,
44 ParagraphIdentifier,
45 PageBreak,
46 Todo,
47 AbridgedTodo,
48 MultilineTodo,
49}
50
51impl StandardParagraphModifier {
52 pub fn ordered() -> Vec<Self> {
53
54 vec![
56 Self::CodeBlock,
57 Self::MathBlock,
58 Self::EmbeddedParagraphStyle,
59 Self::ParagraphIdentifier,
60 Self::Table,
61 Self::ExtendedBlockQuote,
62 Self::FocusBlock,
63 Self::List,
64 Self::MultilineTodo,
65 Self::Todo,
66 Self::AbridgedTodo,
67 Self::MultiImage,
68 Self::AbridgedImage,
69 Self::Image,
70 Self::PageBreak,
71 Self::LineBreakDash,
72 Self::LineBreakStar,
73 Self::LineBreakPlus,
74 Self::CommentBlock,
75 ]
76 }
77
78 pub fn identifier(&self) -> ModifierIdentifier {
79 match self {
80 Self::Image => String::from("image"),
81 Self::CommonParagraph => String::from("common-paragraph"),
82 Self::CodeBlock => String::from("code-block"),
83 Self::MathBlock => String::from("math-block"),
84 Self::ListItem => String::from("list-item"),
85 Self::List => String::from("list"),
86 Self::ExtendedBlockQuoteLine => String::from("extended-block-quote-line"),
87 Self::ExtendedBlockQuote => String::from("extended-block-quote"),
88 Self::LineBreakDash => String::from("line-break-dash"),
89 Self::LineBreakStar => String::from("line-break-star"),
90 Self::LineBreakPlus => String::from("line-break-plus"),
91 Self::FocusBlock => String::from("focus-block"),
92 Self::ParagraphIdentifier => String::from("paragraph-identifier"),
93 Self::EmbeddedParagraphStyle => String::from("embedded-paragraph-style"),
94 Self::PageBreak => String::from("page-break"),
95 Self::Todo => String::from("todo"),
96 Self::AbridgedTodo => String::from("abridged-todo"),
97 Self::MultilineTodo => String::from("multiline-todo"),
98 Self::AbridgedImage => String::from(r"abridged-image"),
99 Self::MultiImage => String::from("multi-image"),
100 Self::Table => String::from("table"),
101 Self::CommentBlock => String::from("comment-block"),
102 }
103 }
104
105 pub fn modifier_pattern(&self) -> ModifierPattern {
107 match *self {
108 Self::Image => build_strict_reserved_line_pattern(&format!(r"!\[([^\]]*)\](?:{})?\(([^)]+)\)(?:\{{\{{{}\}}\}})?", IDENTIFIER_PATTERN, STYLE_PATTERN)),
109 Self::AbridgedImage => build_strict_reserved_line_pattern(&format!(r"!\[\((.*)\)\](?:{})?(?:\{{\{{{}\}}\}})?", IDENTIFIER_PATTERN, STYLE_PATTERN)),
110 Self::MultiImage => String::from(r"!!(?::([\w-]+):)?\[\[(?s:(.*?))\]\]"),
111 Self::CommonParagraph => String::from(r"([\s\S]+)"),
112 Self::CommentBlock => format!(r"<!--(?s:(.*?))-->"),
113 Self::CodeBlock => format!(r"{}{}{}{}", build_strict_reserved_line_pattern(r"```[ \t]*(\w+)?"), NEW_LINE_PATTERN, MULTI_LINES_CONTENT_PATTERN, build_strict_reserved_line_pattern("```")),
114 Self::MathBlock => format!(r"{}{}{}", build_strict_reserved_line_pattern(r"\$\$"), MULTI_LINES_CONTENT_PATTERN, build_strict_reserved_line_pattern(r"\$\$")),
115 Self::FocusBlock => format!(r"{}{}{}{}", build_strict_reserved_line_pattern(r":::[ \t]*(\w+)?"), NEW_LINE_PATTERN, MULTI_LINES_CONTENT_EXCLUDING_HEADINGS_PATTERN, build_strict_reserved_line_pattern(":::")),
116 Self::ListItem => format!(r#"(?m:^([\t ]*)(-\[\]|-\[ \]|-\[x\]|-\[X\]|-|->|\||\*|\+|--|\d[\.)]?|[a-zA-Z]{{1,8}}[\.)]|&[^;]+;) (.*){}?)"#, NEW_LINE_PATTERN),
117 Self::List => format!(r#"((?:{}+)+)"#, Self::ListItem.modifier_pattern()),
118 Self::ExtendedBlockQuoteLine => String::from(r"(?m:^> (.*))"),
119 Self::ExtendedBlockQuote => format!(r"(?m)(^[ \t]*>.*(?:\r?\n>.*)*)"),
120 Self::LineBreakDash => build_strict_reserved_line_pattern(r"-{3,}"),
121 Self::LineBreakStar => build_strict_reserved_line_pattern(r"\*{3,}"),
122 Self::LineBreakPlus => build_strict_reserved_line_pattern(r"\+{3,}"),
123 Self::ParagraphIdentifier => format!(r"\[\[(?sx:(.*?))\]\]{}?{}", NEW_LINE_PATTERN, IDENTIFIER_PATTERN),
124 Self::EmbeddedParagraphStyle => format!(r"\[\[(?sx:(.*?))\]\]{}?(?:{})?{}?\{{\{{{}\}}\}}", NEW_LINE_PATTERN, IDENTIFIER_PATTERN, NEW_LINE_PATTERN, STYLE_PATTERN),
125 Self::PageBreak => build_strict_reserved_line_pattern(r"#{3,}"),
126 Self::Todo => build_strict_reserved_line_pattern(r"(?i:TODO):\s(?:(.*?))"),
127 Self::AbridgedTodo => build_strict_reserved_line_pattern(r"(?i:TODO)"),
128 Self::MultilineTodo => format!("{}{}", build_strict_reserved_line_pattern(r"(?i:TODO):"), r"(?s:(.*?)):(?i:TODO)"),
129 Self::Table => format!(r"(\|(.*)\|{}?)+(?:\|(.*)\|)(?:{}?(?:\[(.*)\])?(?:{})?(?:\{{\{{{}\}}\}})?)?", NEW_LINE_PATTERN, NEW_LINE_PATTERN, IDENTIFIER_PATTERN, STYLE_PATTERN),
130 }
131 }
132
133 pub fn incompatible_modifiers(&self) -> ModifiersBucket {
134 match self {
135
136 Self::Image => ModifiersBucket::All,
137 Self::AbridgedImage => ModifiersBucket::All,
138 Self::MultiImage => ModifiersBucket::All,
139 Self::CodeBlock => ModifiersBucket::All,
140 Self::MathBlock => ModifiersBucket::All,
141 Self::CommentBlock => ModifiersBucket::All,
142
143 _ => ModifiersBucket::None
144 }
145 }
146
147 pub fn modifier_pattern_regex(&self) -> &Regex {
148 MODIFIER_PATTERNS_REGEX.get(&self.identifier()).unwrap()
149 }
150}
151
152impl Into<BaseModifier> for StandardParagraphModifier {
153 fn into(self) -> BaseModifier {
154 BaseModifier::new(self.modifier_pattern(), self.modifier_pattern_regex().clone(), self.incompatible_modifiers())
155 }
156}
157
158
159#[cfg(test)]
160mod test {
161 use regex::Regex;
162
163 use super::StandardParagraphModifier;
164
165 #[test]
166 #[cfg(not(windows))]
167 fn match_list() {
168 let regex = Regex::new(StandardParagraphModifier::List.modifier_pattern().as_str()).unwrap();
169
170 let list = concat!(
171 "\n",
172 "\n",
173 "- [Element 1](#element-1)",
174 "- [Element 2](#element-2)",
175 "\n",
176 "\n",
177 );
178
179 assert!(regex.is_match(list));
180
181 }
182
183}