use std::any::{Any, TypeId};
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::{mem, ptr};
use comemo::Tracked;
use ecow::{EcoString, EcoVec, eco_vec};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use typst_syntax::Span;
use typst_utils::LazyHash;
use crate::diag::{SourceResult, Trace, Tracepoint};
use crate::engine::Engine;
use crate::foundations::{
Content, Context, Element, Field, Func, NativeElement, OneOrMultiple, Packed,
RefableProperty, Repr, Selector, SettableProperty, Target, cast, ty,
};
use crate::introspection::TagElem;
#[ty(cast)]
#[derive(Default, Clone, PartialEq, Hash)]
pub struct Styles(EcoVec<LazyHash<Style>>);
impl Styles {
pub const fn new() -> Self {
Self(EcoVec::new())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &Style> {
self.0.iter().map(|style| &**style)
}
pub fn as_slice(&self) -> &[LazyHash<Style>] {
self.0.as_slice()
}
pub fn set<E, const I: u8>(&mut self, field: Field<E, I>, value: E::Type)
where
E: SettableProperty<I>,
E::Type: Debug + Clone + Hash + Send + Sync + 'static,
{
self.push(Property::new(field, value));
}
pub fn push(&mut self, style: impl Into<Style>) {
self.0.push(LazyHash::new(style.into()));
}
pub fn unset(&mut self) {
self.0.pop();
}
pub fn apply(&mut self, mut outer: Self) {
outer.0.extend(mem::take(self).0);
*self = outer;
}
pub fn apply_one(&mut self, outer: Style) {
self.0.insert(0, LazyHash::new(outer));
}
pub fn spanned(mut self, span: Span) -> Self {
for entry in self.0.make_mut() {
if let Style::Property(property) = &mut **entry {
property.span = span;
}
}
self
}
pub fn outside(mut self) -> Self {
for entry in self.0.make_mut() {
match &mut **entry {
Style::Property(property) => property.outside = true,
Style::Recipe(recipe) => recipe.outside = true,
_ => {}
}
}
self
}
pub fn liftable(mut self) -> Self {
for entry in self.0.make_mut() {
if let Style::Property(property) = &mut **entry {
property.liftable = true;
}
}
self
}
pub fn has<E: NativeElement, const I: u8>(&self, _: Field<E, I>) -> bool {
let elem = E::ELEM;
self.0
.iter()
.filter_map(|style| style.property())
.any(|property| property.is_of(elem) && property.id == I)
}
pub fn root(children: &[(&Content, StyleChain)], initial: StyleChain) -> Styles {
let base = StyleChain::trunk_from_pairs(children).unwrap_or(initial).to_map();
let trunk_len = initial
.to_map()
.as_slice()
.iter()
.zip(base.as_slice())
.take_while(|&(a, b)| a == b)
.count();
base.into_iter()
.enumerate()
.filter(|(i, style)| {
let initial = *i < trunk_len;
style.outside() && (initial || style.liftable())
})
.map(|(_, style)| style)
.collect()
}
}
impl From<LazyHash<Style>> for Styles {
fn from(style: LazyHash<Style>) -> Self {
Self(eco_vec![style])
}
}
impl From<Style> for Styles {
fn from(style: Style) -> Self {
Self(eco_vec![LazyHash::new(style)])
}
}
impl IntoIterator for Styles {
type Item = LazyHash<Style>;
type IntoIter = ecow::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl FromIterator<LazyHash<Style>> for Styles {
fn from_iter<T: IntoIterator<Item = LazyHash<Style>>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
impl Debug for Styles {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Styles ")?;
f.debug_list().entries(&self.0).finish()
}
}
impl Repr for Styles {
fn repr(&self) -> EcoString {
"..".into()
}
}
#[derive(Clone, Hash)]
pub enum Style {
Property(Property),
Recipe(Recipe),
Revocation(RecipeIndex),
}
impl Style {
pub fn property(&self) -> Option<&Property> {
match self {
Self::Property(property) => Some(property),
_ => None,
}
}
pub fn recipe(&self) -> Option<&Recipe> {
match self {
Self::Recipe(recipe) => Some(recipe),
_ => None,
}
}
pub fn span(&self) -> Span {
match self {
Self::Property(property) => property.span,
Self::Recipe(recipe) => recipe.span,
Self::Revocation(_) => Span::detached(),
}
}
pub fn element(&self) -> Option<Element> {
match self {
Style::Property(property) => Some(property.elem),
Style::Recipe(recipe) => match recipe.selector {
Some(Selector::Elem(elem, _)) => Some(elem),
_ => None,
},
Style::Revocation(_) => None,
}
}
pub fn liftable(&self) -> bool {
match self {
Self::Property(property) => property.liftable,
Self::Recipe(_) => true,
Self::Revocation(_) => false,
}
}
pub fn outside(&self) -> bool {
match self {
Self::Property(property) => property.outside,
Self::Recipe(recipe) => recipe.outside,
Self::Revocation(_) => false,
}
}
pub fn wrap(self) -> LazyHash<Style> {
LazyHash::new(self)
}
}
impl Debug for Style {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Property(property) => property.fmt(f),
Self::Recipe(recipe) => recipe.fmt(f),
Self::Revocation(guard) => guard.fmt(f),
}
}
}
impl From<Property> for Style {
fn from(property: Property) -> Self {
Self::Property(property)
}
}
impl From<Recipe> for Style {
fn from(recipe: Recipe) -> Self {
Self::Recipe(recipe)
}
}
#[derive(Clone, Hash)]
pub struct Property {
elem: Element,
id: u8,
value: Block,
span: Span,
liftable: bool,
outside: bool,
}
impl Property {
pub fn new<E, const I: u8>(_: Field<E, I>, value: E::Type) -> Self
where
E: SettableProperty<I>,
E::Type: Debug + Clone + Hash + Send + Sync + 'static,
{
Self {
elem: E::ELEM,
id: I,
value: Block::new(value),
span: Span::detached(),
liftable: false,
outside: false,
}
}
pub fn is(&self, elem: Element, id: u8) -> bool {
self.elem == elem && self.id == id
}
pub fn is_of(&self, elem: Element) -> bool {
self.elem == elem
}
pub fn wrap(self) -> LazyHash<Style> {
Style::Property(self).wrap()
}
}
impl Debug for Property {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"Set({}.{}: ",
self.elem.name(),
self.elem.field_name(self.id).unwrap_or("internal")
)?;
self.value.fmt(f)?;
write!(f, ")")
}
}
#[derive(Hash)]
struct Block(Box<dyn Blockable>);
impl Block {
fn new<T: Blockable>(value: T) -> Self {
Self(Box::new(value))
}
fn downcast<T: 'static>(&self, func: Element, id: u8) -> &T {
self.0
.as_any()
.downcast_ref()
.unwrap_or_else(|| block_wrong_type(func, id, self))
}
}
impl Debug for Block {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl Clone for Block {
fn clone(&self) -> Self {
self.0.dyn_clone()
}
}
trait Blockable: Debug + Send + Sync + 'static {
fn as_any(&self) -> &dyn Any;
fn dyn_hash(&self, state: &mut dyn Hasher);
fn dyn_clone(&self) -> Block;
}
impl<T: Debug + Clone + Hash + Send + Sync + 'static> Blockable for T {
fn as_any(&self) -> &dyn Any {
self
}
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
TypeId::of::<Self>().hash(&mut state);
self.hash(&mut state);
}
fn dyn_clone(&self) -> Block {
Block(Box::new(self.clone()))
}
}
impl Hash for dyn Blockable {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
}
}
#[derive(Clone, PartialEq, Hash)]
pub struct Recipe {
selector: Option<Selector>,
transform: Transformation,
span: Span,
outside: bool,
}
impl Recipe {
pub fn new(
selector: Option<Selector>,
transform: Transformation,
span: Span,
) -> Self {
Self { selector, transform, span, outside: false }
}
pub fn selector(&self) -> Option<&Selector> {
self.selector.as_ref()
}
pub fn transform(&self) -> &Transformation {
&self.transform
}
pub fn span(&self) -> Span {
self.span
}
pub fn apply(
&self,
engine: &mut Engine,
context: Tracked<Context>,
content: Content,
) -> SourceResult<Content> {
let mut content = match &self.transform {
Transformation::Content(content) => content.clone(),
Transformation::Func(func) => {
let mut result = func.call(engine, context, [content.clone()]);
if self.selector.is_some() {
let point = || Tracepoint::Show(content.func().name().into());
result = result.trace(engine.world, point, content.span());
}
result?.display()
}
Transformation::Style(styles) => content.styled_with_map(styles.clone()),
};
if content.span().is_detached() {
content = content.spanned(self.span);
}
Ok(content)
}
}
impl Debug for Recipe {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Show(")?;
if let Some(selector) = &self.selector {
selector.fmt(f)?;
f.write_str(", ")?;
}
self.transform.fmt(f)?;
f.write_str(")")
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct RecipeIndex(pub usize);
#[derive(Clone, PartialEq, Hash)]
pub enum Transformation {
Content(Content),
Func(Func),
Style(Styles),
}
impl Debug for Transformation {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Content(content) => content.fmt(f),
Self::Func(func) => func.fmt(f),
Self::Style(styles) => styles.fmt(f),
}
}
}
cast! {
Transformation,
content: Content => Self::Content(content),
func: Func => Self::Func(func),
}
#[derive(Default, Copy, Clone, Hash)]
pub struct StyleChain<'a> {
head: &'a [LazyHash<Style>],
tail: Option<&'a Self>,
}
impl<'a> StyleChain<'a> {
pub fn new(root: &'a Styles) -> Self {
Self { head: &root.0, tail: None }
}
pub fn get<E, const I: u8>(self, field: Field<E, I>) -> E::Type
where
E: SettableProperty<I>,
E::Type: Copy,
{
self.get_cloned(field)
}
pub fn get_cloned<E, const I: u8>(self, _: Field<E, I>) -> E::Type
where
E: SettableProperty<I>,
{
if let Some(fold) = E::FOLD {
self.get_folded::<E::Type>(E::ELEM, I, fold, E::default())
} else {
self.get_unfolded::<E::Type>(E::ELEM, I)
.cloned()
.unwrap_or_else(E::default)
}
}
pub fn get_ref<E, const I: u8>(self, _: Field<E, I>) -> &'a E::Type
where
E: RefableProperty<I>,
{
self.get_unfolded(E::ELEM, I).unwrap_or_else(|| E::default_ref())
}
pub fn resolve<E, const I: u8>(
self,
field: Field<E, I>,
) -> <E::Type as Resolve>::Output
where
E: SettableProperty<I>,
E::Type: Resolve,
{
self.get_cloned(field).resolve(self)
}
fn get_unfolded<T: 'static>(self, func: Element, id: u8) -> Option<&'a T> {
self.find(func, id).map(|block| block.downcast(func, id))
}
fn get_folded<T: 'static + Clone>(
self,
func: Element,
id: u8,
fold: fn(T, T) -> T,
default: T,
) -> T {
let iter = self
.properties(func, id)
.map(|block| block.downcast::<T>(func, id).clone());
if let Some(folded) = iter.reduce(fold) { fold(folded, default) } else { default }
}
fn find(self, func: Element, id: u8) -> Option<&'a Block> {
self.properties(func, id).next()
}
fn properties(self, func: Element, id: u8) -> impl Iterator<Item = &'a Block> {
self.entries()
.filter_map(|style| style.property())
.filter(move |property| property.is(func, id))
.map(|property| &property.value)
}
pub fn chain<'b, C>(&'b self, local: &'b C) -> StyleChain<'b>
where
C: Chainable + ?Sized,
{
Chainable::chain(local, self)
}
pub fn entries(self) -> Entries<'a> {
Entries { inner: [].as_slice().iter(), links: self.links() }
}
pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> {
self.entries().filter_map(|style| style.recipe())
}
pub fn links(self) -> Links<'a> {
Links(Some(self))
}
pub fn to_map(self) -> Styles {
let mut styles: EcoVec<_> = self.entries().cloned().collect();
styles.make_mut().reverse();
Styles(styles)
}
pub fn suffix(self, len: usize) -> Styles {
let mut styles = EcoVec::new();
let take = self.links().count().saturating_sub(len);
for link in self.links().take(take) {
styles.extend(link.iter().cloned().rev());
}
styles.make_mut().reverse();
Styles(styles)
}
pub fn pop(&mut self) {
*self = self.tail.copied().unwrap_or_default();
}
pub fn trunk(iter: impl IntoIterator<Item = Self>) -> Option<Self> {
let mut iter = iter.into_iter();
let mut trunk = iter.next()?;
let mut depth = trunk.links().count();
for mut chain in iter {
let len = chain.links().count();
if len < depth {
for _ in 0..depth - len {
trunk.pop();
}
depth = len;
} else if len > depth {
for _ in 0..len - depth {
chain.pop();
}
}
while depth > 0 && chain != trunk {
trunk.pop();
chain.pop();
depth -= 1;
}
}
Some(trunk)
}
pub fn trunk_from_pairs(iter: &[(&Content, Self)]) -> Option<Self> {
Self::trunk(iter.iter().filter(|(c, _)| !c.is::<TagElem>()).map(|&(_, s)| s))
}
}
impl Debug for StyleChain<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("StyleChain ")?;
f.debug_list()
.entries(self.entries().collect::<Vec<_>>().into_iter().rev())
.finish()
}
}
impl PartialEq for StyleChain<'_> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.head, other.head)
&& match (self.tail, other.tail) {
(Some(a), Some(b)) => ptr::eq(a, b),
(None, None) => true,
_ => false,
}
}
}
pub trait Chainable {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a>;
}
impl Chainable for LazyHash<Style> {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
StyleChain {
head: std::slice::from_ref(self),
tail: Some(outer),
}
}
}
impl Chainable for [LazyHash<Style>] {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
if self.is_empty() {
*outer
} else {
StyleChain { head: self, tail: Some(outer) }
}
}
}
impl<const N: usize> Chainable for [LazyHash<Style>; N] {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
Chainable::chain(self.as_slice(), outer)
}
}
impl Chainable for Styles {
fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
Chainable::chain(self.0.as_slice(), outer)
}
}
pub struct Entries<'a> {
inner: std::slice::Iter<'a, LazyHash<Style>>,
links: Links<'a>,
}
impl<'a> Iterator for Entries<'a> {
type Item = &'a LazyHash<Style>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(entry) = self.inner.next_back() {
return Some(entry);
}
match self.links.next() {
Some(next) => self.inner = next.iter(),
None => return None,
}
}
}
}
pub struct Links<'a>(Option<StyleChain<'a>>);
impl<'a> Iterator for Links<'a> {
type Item = &'a [LazyHash<Style>];
fn next(&mut self) -> Option<Self::Item> {
let StyleChain { head, tail } = self.0?;
self.0 = tail.copied();
Some(head)
}
}
pub trait Resolve {
type Output;
fn resolve(self, styles: StyleChain) -> Self::Output;
}
impl<T: Resolve> Resolve for Option<T> {
type Output = Option<T::Output>;
fn resolve(self, styles: StyleChain) -> Self::Output {
self.map(|v| v.resolve(styles))
}
}
pub trait Fold {
fn fold(self, outer: Self) -> Self;
}
impl Fold for bool {
fn fold(self, _: Self) -> Self {
self
}
}
impl<T: Fold> Fold for Option<T> {
fn fold(self, outer: Self) -> Self {
match (self, outer) {
(Some(inner), Some(outer)) => Some(inner.fold(outer)),
(inner, _) => inner,
}
}
}
impl<T> Fold for Vec<T> {
fn fold(self, mut outer: Self) -> Self {
outer.extend(self);
outer
}
}
impl<T, const N: usize> Fold for SmallVec<[T; N]> {
fn fold(self, mut outer: Self) -> Self {
outer.extend(self);
outer
}
}
impl<T> Fold for OneOrMultiple<T> {
fn fold(self, mut outer: Self) -> Self {
outer.0.extend(self.0);
outer
}
}
pub type FoldFn<T> = fn(T, T) -> T;
pub trait AlternativeFold {
fn fold_or(self, outer: Self) -> Self;
}
impl<T: Fold> AlternativeFold for Option<T> {
fn fold_or(self, outer: Self) -> Self {
match (self, outer) {
(Some(inner), Some(outer)) => Some(inner.fold(outer)),
(inner, outer) => inner.or(outer),
}
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Hash)]
pub struct Depth(pub usize);
impl Fold for Depth {
fn fold(self, outer: Self) -> Self {
Self(outer.0 + self.0)
}
}
#[cold]
fn block_wrong_type(func: Element, id: u8, value: &Block) -> ! {
panic!(
"attempted to read a value of a different type than was written {}.{}: {:?}",
func.name(),
func.field_name(id).unwrap(),
value
)
}
pub struct NativeRuleMap {
rules: FxHashMap<(Element, Target), NativeShowRule>,
}
pub type ShowFn<T> = fn(
elem: &Packed<T>,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<Content>;
impl NativeRuleMap {
pub fn new() -> Self {
let mut rules = Self { rules: FxHashMap::default() };
rules.register_builtin(crate::foundations::CONTEXT_RULE);
rules.register_builtin(crate::introspection::COUNTER_DISPLAY_RULE);
rules.register_empty::<crate::introspection::CounterUpdateElem>();
rules.register_empty::<crate::introspection::StateUpdateElem>();
rules.register_empty::<crate::introspection::MetadataElem>();
rules.register_empty::<crate::model::PrefixInfo>();
rules
}
fn register_empty<T: NativeElement>(&mut self) {
self.register_builtin::<T>(|_, _, _| Ok(Content::empty()));
}
fn register_builtin<T: NativeElement>(&mut self, f: ShowFn<T>) {
self.register(Target::Paged, f);
self.register(Target::Html, f);
}
pub fn register<T: NativeElement>(&mut self, target: Target, f: ShowFn<T>) {
let res = self.rules.insert((T::ELEM, target), NativeShowRule::new(f));
if res.is_some() {
panic!(
"duplicate native show rule for `{}` on {target:?} target",
T::ELEM.name()
)
}
}
pub fn get(&self, target: Target, content: &Content) -> Option<NativeShowRule> {
self.rules.get(&(content.func(), target)).copied()
}
}
impl Default for NativeRuleMap {
fn default() -> Self {
Self::new()
}
}
pub use rule::NativeShowRule;
mod rule {
use super::*;
#[derive(Copy, Clone)]
pub struct NativeShowRule {
elem: Element,
f: unsafe fn(
elem: &Content,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<Content>,
}
impl NativeShowRule {
pub fn new<T: NativeElement>(f: ShowFn<T>) -> Self {
Self {
elem: T::ELEM,
#[allow(clippy::missing_transmute_annotations)]
f: unsafe { std::mem::transmute(f) },
}
}
pub fn apply(
&self,
content: &Content,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<Content> {
assert_eq!(content.elem(), self.elem);
unsafe { (self.f)(content, engine, styles) }
}
}
}