use std::borrow::Cow;
pub(crate) fn iced_markdown_options() -> pulldown_cmark::Options {
pulldown_cmark::Options::ENABLE_YAML_STYLE_METADATA_BLOCKS
| pulldown_cmark::Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS
| pulldown_cmark::Options::ENABLE_TABLES
| pulldown_cmark::Options::ENABLE_STRIKETHROUGH
| pulldown_cmark::Options::ENABLE_TASKLISTS
}
pub(crate) fn hard_breaks(text: &str) -> Cow<'_, str> {
if !text.contains('\n') && !text.contains('\r') {
return Cow::Borrowed(text);
}
let normalized: Cow<'_, str> = if text.contains('\r') {
Cow::Owned(text.replace("\r\n", "\n").replace('\r', "\n"))
} else {
Cow::Borrowed(text)
};
let src = normalized.as_ref();
let positions = soft_break_positions(src);
if positions.is_empty() {
return normalized;
}
let mut out = String::with_capacity(src.len() + positions.len() * 2);
let mut last = 0usize;
for pos in positions {
out.push_str(&src[last..pos]);
out.push_str(" ");
last = pos;
}
out.push_str(&src[last..]);
Cow::Owned(out)
}
fn soft_break_positions(src: &str) -> Vec<usize> {
let mut positions = Vec::new();
for (event, range) in
pulldown_cmark::Parser::new_ext(src, iced_markdown_options()).into_offset_iter()
{
if let pulldown_cmark::Event::SoftBreak = event {
positions.push(range.start);
}
}
positions
}
#[cfg(test)]
mod tests {
use super::hard_breaks;
#[test]
fn hard_breaks_converts_prose_soft_breaks() {
assert_eq!(hard_breaks("foo\nbar").as_ref(), "foo \nbar");
assert_eq!(hard_breaks("a\nb\nc").as_ref(), "a \nb \nc");
}
#[test]
fn hard_breaks_preserves_paragraph_gaps() {
assert_eq!(hard_breaks("foo\n\nbar").as_ref(), "foo\n\nbar");
assert_eq!(hard_breaks("a\nb\n\npara2").as_ref(), "a \nb\n\npara2");
}
#[test]
fn hard_breaks_leaves_code_untouched() {
assert_eq!(
hard_breaks("x\n```\ncode\n\nnext\n```\ny").as_ref(),
"x\n```\ncode\n\nnext\n```\ny"
);
assert_eq!(
hard_breaks(" code\n line2\nplain").as_ref(),
" code\n line2\nplain"
);
assert_eq!(hard_breaks("foo `a\nb` bar").as_ref(), "foo `a\nb` bar");
}
#[test]
fn hard_breaks_normalizes_crlf_and_cr() {
assert_eq!(hard_breaks("foo\r\nbar").as_ref(), "foo \nbar");
assert_eq!(hard_breaks("foo\rbar").as_ref(), "foo \nbar");
}
}