diff --git a/src/wrap.rs b/src/wrap.rs index 0eb8e45a..a2a520b5 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -4,7 +4,7 @@ //! `docs/architecture.md` and uses the `unicode-width` crate for accurate //! display calculations. -use regex::Regex; +use regex::{Captures, Regex}; static FENCE_RE: std::sync::LazyLock = std::sync::LazyLock::new(|| Regex::new(r"^\s*(```|~~~).*").unwrap()); @@ -18,6 +18,42 @@ static FOOTNOTE_RE: std::sync::LazyLock = static BLOCKQUOTE_RE: std::sync::LazyLock = std::sync::LazyLock::new(|| Regex::new(r"^(\s*(?:>\s*)+)(.*)$").unwrap()); +struct PrefixHandler { + re: &'static std::sync::LazyLock, + is_bq: bool, + build_prefix: fn(&Captures) -> String, + rest_group: usize, +} + +impl PrefixHandler { + fn build_bullet_prefix(cap: &Captures) -> String { cap[1].to_string() } + + fn build_footnote_prefix(cap: &Captures) -> String { format!("{}{}", &cap[1], &cap[2]) } + + fn build_blockquote_prefix(cap: &Captures) -> String { cap[1].to_string() } +} + +static HANDLERS: &[PrefixHandler] = &[ + PrefixHandler { + re: &BULLET_RE, + is_bq: false, + build_prefix: PrefixHandler::build_bullet_prefix, + rest_group: 2, + }, + PrefixHandler { + re: &FOOTNOTE_RE, + is_bq: false, + build_prefix: PrefixHandler::build_footnote_prefix, + rest_group: 3, + }, + PrefixHandler { + re: &BLOCKQUOTE_RE, + is_bq: true, + build_prefix: PrefixHandler::build_blockquote_prefix, + rest_group: 2, + }, +]; + /// Markdown token emitted by [`tokenize_markdown`]. #[derive(Debug, PartialEq)] pub enum Token<'a> { @@ -341,7 +377,7 @@ pub fn wrap_text(lines: &[String], width: usize) -> Vec { let mut indent = String::new(); let mut in_code = false; - for line in lines { + 'line_loop: for line in lines { if FENCE_RE.is_match(line) { flush_paragraph(&mut out, &buf, &indent, width); buf.clear(); @@ -380,27 +416,21 @@ pub fn wrap_text(lines: &[String], width: usize) -> Vec { continue; } - if let Some(cap) = BULLET_RE.captures(line) { - let prefix = cap.get(1).unwrap().as_str(); - let rest = cap.get(2).unwrap().as_str(); - handle_prefix_line(&mut out, &mut buf, &mut indent, width, prefix, rest, false); - continue; - } - - if let Some(cap) = FOOTNOTE_RE.captures(line) { - let indent_part = cap.get(1).unwrap().as_str(); - let label_part = cap.get(2).unwrap().as_str(); - let prefix = format!("{indent_part}{label_part}"); - let rest = cap.get(3).unwrap().as_str(); - handle_prefix_line(&mut out, &mut buf, &mut indent, width, &prefix, rest, false); - continue; - } - - if let Some(cap) = BLOCKQUOTE_RE.captures(line) { - let prefix = cap.get(1).unwrap().as_str(); - let rest = cap.get(2).unwrap().as_str(); - handle_prefix_line(&mut out, &mut buf, &mut indent, width, prefix, rest, true); - continue; + for handler in HANDLERS { + if let Some(cap) = handler.re.captures(line) { + let prefix = (handler.build_prefix)(&cap); + let rest = cap.get(handler.rest_group).unwrap().as_str(); + handle_prefix_line( + &mut out, + &mut buf, + &mut indent, + width, + &prefix, + rest, + handler.is_bq, + ); + continue 'line_loop; + } } if buf.is_empty() {