use regex::Regex;
use std::sync::OnceLock;
const MARKDOWN_BLOCK_STARTERS: &[char] = &['-', '*', '+', '#', '>', '|'];
fn ordered_list_regex() -> &'static Regex {
static REGEX: OnceLock<Regex> = OnceLock::new();
REGEX.get_or_init(|| Regex::new(r"^\d+\.$").expect("Invalid regex"))
}
fn horizontal_rule_regex() -> &'static Regex {
static REGEX: OnceLock<Regex> = OnceLock::new();
REGEX.get_or_init(|| Regex::new(r"^(-{3,}|\*{3,}|_{3,})$").expect("Invalid regex"))
}
fn code_fence_regex() -> &'static Regex {
static REGEX: OnceLock<Regex> = OnceLock::new();
REGEX.get_or_init(|| Regex::new(r"^(`{3,}|~{4,})").expect("Invalid regex"))
}
pub fn is_markdown_syntax_at_line_start(word: &str) -> bool {
if word.is_empty() {
return false;
}
if word.len() == 1 {
let first_char = word.chars().next().unwrap();
if word == "\t" {
return true; }
return MARKDOWN_BLOCK_STARTERS.contains(&first_char);
}
if word.starts_with('#') && word.chars().all(|c| c == '#') {
return true;
}
if ordered_list_regex().is_match(word) {
return true;
}
if horizontal_rule_regex().is_match(word) {
return true;
}
if code_fence_regex().is_match(word) {
return true;
}
if word.starts_with(" ") || word.starts_with('\t') {
return true;
}
false
}
pub fn is_safe_line_break_before(next_word: &str, _context_words: &[&str]) -> bool {
!is_markdown_syntax_at_line_start(next_word)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unordered_list_markers() {
assert!(is_markdown_syntax_at_line_start("-"));
assert!(is_markdown_syntax_at_line_start("*"));
assert!(is_markdown_syntax_at_line_start("+"));
}
#[test]
fn test_ordered_list_markers() {
assert!(is_markdown_syntax_at_line_start("1."));
assert!(is_markdown_syntax_at_line_start("42."));
assert!(is_markdown_syntax_at_line_start("999."));
assert!(!is_markdown_syntax_at_line_start("1")); assert!(!is_markdown_syntax_at_line_start("1.5")); assert!(!is_markdown_syntax_at_line_start("a.")); }
#[test]
fn test_heading_markers() {
assert!(is_markdown_syntax_at_line_start("#"));
assert!(is_markdown_syntax_at_line_start("##"));
assert!(is_markdown_syntax_at_line_start("###"));
assert!(is_markdown_syntax_at_line_start("####"));
assert!(is_markdown_syntax_at_line_start("#####"));
assert!(is_markdown_syntax_at_line_start("######"));
assert!(!is_markdown_syntax_at_line_start("#text")); assert!(!is_markdown_syntax_at_line_start("##text")); }
#[test]
fn test_blockquote_markers() {
assert!(is_markdown_syntax_at_line_start(">"));
}
#[test]
fn test_table_markers() {
assert!(is_markdown_syntax_at_line_start("|"));
}
#[test]
fn test_horizontal_rule_markers() {
assert!(is_markdown_syntax_at_line_start("---"));
assert!(is_markdown_syntax_at_line_start("----"));
assert!(is_markdown_syntax_at_line_start("***"));
assert!(is_markdown_syntax_at_line_start("****"));
assert!(is_markdown_syntax_at_line_start("___"));
assert!(is_markdown_syntax_at_line_start("____"));
assert!(!is_markdown_syntax_at_line_start("--")); assert!(!is_markdown_syntax_at_line_start("**")); assert!(!is_markdown_syntax_at_line_start("__")); }
#[test]
fn test_code_fence_markers() {
assert!(is_markdown_syntax_at_line_start("```"));
assert!(is_markdown_syntax_at_line_start("````"));
assert!(is_markdown_syntax_at_line_start("~~~~"));
assert!(is_markdown_syntax_at_line_start("~~~~~"));
assert!(!is_markdown_syntax_at_line_start("``")); assert!(!is_markdown_syntax_at_line_start("~~~")); }
#[test]
fn test_indented_code_markers() {
assert!(is_markdown_syntax_at_line_start(" ")); assert!(is_markdown_syntax_at_line_start(" code"));
assert!(is_markdown_syntax_at_line_start("\t")); assert!(is_markdown_syntax_at_line_start("\tcode"));
assert!(!is_markdown_syntax_at_line_start(" ")); assert!(!is_markdown_syntax_at_line_start(" code")); }
#[test]
fn test_regular_words() {
assert!(!is_markdown_syntax_at_line_start("word"));
assert!(!is_markdown_syntax_at_line_start("hello"));
assert!(!is_markdown_syntax_at_line_start("123"));
assert!(!is_markdown_syntax_at_line_start("test-word"));
assert!(!is_markdown_syntax_at_line_start("markdown"));
}
#[test]
fn test_safe_line_break() {
assert!(is_safe_line_break_before("word", &[]));
assert!(!is_safe_line_break_before("-", &[]));
assert!(!is_safe_line_break_before("*", &[]));
assert!(!is_safe_line_break_before("#", &[]));
assert!(!is_safe_line_break_before("1.", &[]));
}
}