use typst_html::HtmlElem;
use typst_library::foundations::{Content, StyleChain};
use typst_library::introspection::TagElem;
use typst_library::layout::HElem;
use typst_library::routines::Pair;
use typst_library::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum SpaceState {
Invisible,
Destructive,
Supportive,
Space,
}
pub(crate) fn collapse_spaces(buf: &mut Vec<Pair>, start: usize) {
let mut cursor = start;
let mut prev_space = cursor;
let mut state = SpaceState::Destructive;
for i in start..buf.len() {
let (content, styles) = buf[i];
state = match collapse_state(content, styles) {
SpaceState::Invisible => state,
SpaceState::Destructive => {
if state == SpaceState::Space {
buf.copy_within(prev_space + 1..cursor, prev_space);
cursor -= 1;
}
SpaceState::Destructive
}
SpaceState::Supportive => SpaceState::Supportive,
SpaceState::Space => {
if state != SpaceState::Supportive {
continue;
}
prev_space = cursor;
SpaceState::Space
}
};
if cursor < i {
buf[cursor] = buf[i];
}
cursor += 1;
}
if state == SpaceState::Space {
buf.copy_within(prev_space + 1..cursor, prev_space);
cursor -= 1;
}
buf.truncate(cursor);
}
pub(crate) fn collapse_state(content: &Content, styles: StyleChain) -> SpaceState {
if content.is::<TagElem>() {
SpaceState::Invisible
} else if let Some(elem) = content.to_packed::<HElem>() {
if elem.amount.is_fractional() || elem.weak.get(styles) {
SpaceState::Destructive
} else {
SpaceState::Invisible
}
} else if content.is::<LinebreakElem>()
|| content.to_packed::<HtmlElem>().is_some_and(|elem| {
typst_html::tag::is_whitespace_collapsing(elem.tag)
})
{
SpaceState::Destructive
} else if content.is::<SpaceElem>() {
SpaceState::Space
} else {
SpaceState::Supportive
}
}
pub(crate) fn collapse_state_textual<'a>(
content: &'a Content,
styles: StyleChain<'_>,
) -> (SpaceState, &'a str) {
if content.is::<TagElem>() {
(SpaceState::Invisible, "")
} else if content.is::<LinebreakElem>() {
(SpaceState::Destructive, "\n")
} else if content.is::<SpaceElem>() {
(SpaceState::Space, " ")
} else if let Some(elem) = content.to_packed::<TextElem>() {
(SpaceState::Supportive, &elem.text)
} else if let Some(elem) = content.to_packed::<SmartQuoteElem>() {
let text = if elem.double.get(styles) { "\"" } else { "'" };
(SpaceState::Supportive, text)
} else {
let name = content.elem().name();
panic!("tried to find regex match in a non-textual element: {name}");
}
}