use std::any::TypeId;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::iter::{self, Sum};
use std::marker::PhantomData;
use std::ops::{Add, AddAssign, Deref, DerefMut};
use std::sync::Arc;
use comemo::Tracked;
use ecow::{eco_format, EcoString};
use serde::{Serialize, Serializer};
use typst_syntax::Span;
use typst_utils::{fat, singleton, LazyHash, SmallBitSet};
use crate::diag::{SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
elem, func, scope, ty, Context, Dict, Element, Fields, IntoValue, Label,
NativeElement, Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles,
Value,
};
use crate::introspection::Location;
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
use crate::model::{Destination, EmphElem, LinkElem, StrongElem};
use crate::text::UnderlineElem;
#[ty(scope, cast)]
#[derive(Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Content {
inner: Arc<Inner<dyn Bounds>>,
span: Span,
}
#[derive(Hash)]
struct Inner<T: ?Sized + 'static> {
label: Option<Label>,
location: Option<Location>,
lifecycle: SmallBitSet,
elem: LazyHash<T>,
}
impl Content {
pub fn new<T: NativeElement>(elem: T) -> Self {
Self {
inner: Arc::new(Inner {
label: None,
location: None,
lifecycle: SmallBitSet::new(),
elem: elem.into(),
}),
span: Span::detached(),
}
}
pub fn empty() -> Self {
singleton!(Content, SequenceElem::default().pack()).clone()
}
pub fn elem(&self) -> Element {
self.inner.elem.dyn_elem()
}
pub fn span(&self) -> Span {
self.span
}
pub fn spanned(mut self, span: Span) -> Self {
if self.span.is_detached() {
self.span = span;
}
self
}
pub fn label(&self) -> Option<Label> {
self.inner.label
}
pub fn labelled(mut self, label: Label) -> Self {
self.set_label(label);
self
}
pub fn set_label(&mut self, label: Label) {
self.make_mut().label = Some(label);
}
pub fn located(mut self, loc: Location) -> Self {
self.set_location(loc);
self
}
pub fn set_location(&mut self, location: Location) {
self.make_mut().location = Some(location);
}
pub fn is_guarded(&self, index: RecipeIndex) -> bool {
self.inner.lifecycle.contains(index.0)
}
pub fn guarded(mut self, index: RecipeIndex) -> Self {
self.make_mut().lifecycle.insert(index.0);
self
}
pub fn is_prepared(&self) -> bool {
self.inner.lifecycle.contains(0)
}
pub fn mark_prepared(&mut self) {
self.make_mut().lifecycle.insert(0);
}
pub fn get(
&self,
id: u8,
styles: Option<StyleChain>,
) -> Result<Value, FieldAccessError> {
if id == 255 {
if let Some(label) = self.label() {
return Ok(label.into_value());
}
}
match styles {
Some(styles) => self.inner.elem.field_with_styles(id, styles),
None => self.inner.elem.field(id),
}
}
pub fn get_by_name(&self, name: &str) -> Result<Value, FieldAccessError> {
if name == "label" {
return self
.label()
.map(|label| label.into_value())
.ok_or(FieldAccessError::Unknown);
}
let id = self.elem().field_id(name).ok_or(FieldAccessError::Unknown)?;
self.get(id, None)
}
pub fn field(&self, id: u8) -> StrResult<Value> {
self.get(id, None)
.map_err(|e| e.message(self, self.elem().field_name(id).unwrap()))
}
pub fn field_by_name(&self, name: &str) -> StrResult<Value> {
self.get_by_name(name).map_err(|e| e.message(self, name))
}
pub fn materialize(&mut self, styles: StyleChain) {
self.make_mut().elem.materialize(styles);
}
pub fn sequence(iter: impl IntoIterator<Item = Self>) -> Self {
let vec: Vec<_> = iter.into_iter().collect();
if vec.is_empty() {
Self::empty()
} else if vec.len() == 1 {
vec.into_iter().next().unwrap()
} else {
SequenceElem::new(vec).into()
}
}
pub fn is<T: NativeElement>(&self) -> bool {
self.inner.elem.dyn_type_id() == TypeId::of::<T>()
}
pub fn to_packed<T: NativeElement>(&self) -> Option<&Packed<T>> {
Packed::from_ref(self)
}
pub fn to_packed_mut<T: NativeElement>(&mut self) -> Option<&mut Packed<T>> {
Packed::from_mut(self)
}
pub fn into_packed<T: NativeElement>(self) -> Result<Packed<T>, Self> {
Packed::from_owned(self)
}
pub fn unpack<T: NativeElement>(self) -> Result<T, Self> {
self.into_packed::<T>().map(Packed::unpack)
}
fn make_mut(&mut self) -> &mut Inner<dyn Bounds> {
let arc = &mut self.inner;
if Arc::strong_count(arc) > 1 || Arc::weak_count(arc) > 0 {
*self = arc.elem.dyn_clone(arc, self.span);
}
Arc::get_mut(&mut self.inner).unwrap()
}
pub fn can<C>(&self) -> bool
where
C: ?Sized + 'static,
{
self.elem().can::<C>()
}
pub fn with<C>(&self) -> Option<&C>
where
C: ?Sized + 'static,
{
let vtable = self.elem().vtable()(TypeId::of::<C>())?;
let data = self as *const Content as *const ();
Some(unsafe { &*fat::from_raw_parts(data, vtable.as_ptr()) })
}
pub fn with_mut<C>(&mut self) -> Option<&mut C>
where
C: ?Sized + 'static,
{
let vtable = self.elem().vtable()(TypeId::of::<C>())?;
let data = self as *mut Content as *mut ();
Some(unsafe { &mut *fat::from_raw_parts_mut(data, vtable.as_ptr()) })
}
pub fn is_empty(&self) -> bool {
let Some(sequence) = self.to_packed::<SequenceElem>() else {
return false;
};
sequence.children.is_empty()
}
pub fn sequence_recursive_for_each<'a>(&'a self, f: &mut impl FnMut(&'a Self)) {
if let Some(sequence) = self.to_packed::<SequenceElem>() {
for child in &sequence.children {
child.sequence_recursive_for_each(f);
}
} else {
f(self);
}
}
pub fn styled_with_recipe(
self,
engine: &mut Engine,
context: Tracked<Context>,
recipe: Recipe,
) -> SourceResult<Self> {
if recipe.selector().is_none() {
recipe.apply(engine, context, self)
} else {
Ok(self.styled(recipe))
}
}
pub fn repeat(&self, count: usize) -> Self {
Self::sequence(std::iter::repeat_with(|| self.clone()).take(count))
}
pub fn styled(mut self, style: impl Into<Style>) -> Self {
if let Some(style_elem) = self.to_packed_mut::<StyledElem>() {
style_elem.styles.apply_one(style.into());
self
} else {
self.styled_with_map(style.into().into())
}
}
pub fn styled_with_map(mut self, styles: Styles) -> Self {
if styles.is_empty() {
return self;
}
if let Some(style_elem) = self.to_packed_mut::<StyledElem>() {
style_elem.styles.apply(styles);
self
} else {
StyledElem::new(self, styles).into()
}
}
pub fn style_in_place(&mut self, styles: Styles) {
if styles.is_empty() {
return;
}
if let Some(style_elem) = self.to_packed_mut::<StyledElem>() {
style_elem.styles.apply(styles);
} else {
*self = StyledElem::new(std::mem::take(self), styles).into();
}
}
pub fn query(&self, selector: Selector) -> Vec<Content> {
let mut results = Vec::new();
self.traverse(&mut |element| {
if selector.matches(&element, None) {
results.push(element);
}
});
results
}
pub fn query_first(&self, selector: &Selector) -> Option<Content> {
let mut result = None;
self.traverse(&mut |element| {
if result.is_none() && selector.matches(&element, None) {
result = Some(element);
}
});
result
}
pub fn plain_text(&self) -> EcoString {
let mut text = EcoString::new();
self.traverse(&mut |element| {
if let Some(textable) = element.with::<dyn PlainText>() {
textable.plain_text(&mut text);
}
});
text
}
fn traverse<F>(&self, f: &mut F)
where
F: FnMut(Content),
{
f(self.clone());
self.inner
.elem
.fields()
.into_iter()
.for_each(|(_, value)| walk_value(value, f));
fn walk_value<F>(value: Value, f: &mut F)
where
F: FnMut(Content),
{
match value {
Value::Content(content) => content.traverse(f),
Value::Array(array) => {
for value in array {
walk_value(value, f);
}
}
_ => {}
}
}
}
}
impl Content {
pub fn strong(self) -> Self {
let span = self.span();
StrongElem::new(self).pack().spanned(span)
}
pub fn emph(self) -> Self {
let span = self.span();
EmphElem::new(self).pack().spanned(span)
}
pub fn underlined(self) -> Self {
let span = self.span();
UnderlineElem::new(self).pack().spanned(span)
}
pub fn linked(self, dest: Destination) -> Self {
self.styled(LinkElem::set_current(Some(dest)))
}
pub fn aligned(self, align: Alignment) -> Self {
self.styled(AlignElem::set_alignment(align))
}
pub fn padded(self, padding: Sides<Rel<Length>>) -> Self {
let span = self.span();
PadElem::new(self)
.with_left(padding.left)
.with_top(padding.top)
.with_right(padding.right)
.with_bottom(padding.bottom)
.pack()
.spanned(span)
}
pub fn moved(self, delta: Axes<Rel<Length>>) -> Self {
let span = self.span();
MoveElem::new(self)
.with_dx(delta.x)
.with_dy(delta.y)
.pack()
.spanned(span)
}
}
#[scope]
impl Content {
#[func]
pub fn func(&self) -> Element {
self.elem()
}
#[func]
pub fn has(
&self,
field: Str,
) -> bool {
if field.as_str() == "label" {
return self.label().is_some();
}
let Some(id) = self.elem().field_id(&field) else {
return false;
};
self.inner.elem.has(id)
}
#[func]
pub fn at(
&self,
field: Str,
#[named]
default: Option<Value>,
) -> StrResult<Value> {
self.get_by_name(&field)
.or_else(|e| default.ok_or(e))
.map_err(|e| e.message_no_default(self, &field))
}
#[func]
pub fn fields(&self) -> Dict {
let mut dict = self.inner.elem.fields();
if let Some(label) = self.label() {
dict.insert("label".into(), label.into_value());
}
dict
}
#[func]
pub fn location(&self) -> Option<Location> {
self.inner.location
}
}
impl Default for Content {
fn default() -> Self {
Self::empty()
}
}
impl Debug for Content {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.inner.elem.fmt(f)
}
}
impl<T: NativeElement> From<T> for Content {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl PartialEq for Content {
fn eq(&self, other: &Self) -> bool {
self.elem() == other.elem() && self.inner.elem.dyn_eq(other)
}
}
impl Repr for Content {
fn repr(&self) -> EcoString {
self.inner.elem.repr()
}
}
impl Add for Content {
type Output = Self;
fn add(self, mut rhs: Self) -> Self::Output {
let mut lhs = self;
match (lhs.to_packed_mut::<SequenceElem>(), rhs.to_packed_mut::<SequenceElem>()) {
(Some(seq_lhs), Some(rhs)) => {
seq_lhs.children.extend(rhs.children.iter().cloned());
lhs
}
(Some(seq_lhs), None) => {
seq_lhs.children.push(rhs);
lhs
}
(None, Some(rhs_seq)) => {
rhs_seq.children.insert(0, lhs);
rhs
}
(None, None) => Self::sequence([lhs, rhs]),
}
}
}
impl<'a> Add<&'a Self> for Content {
type Output = Self;
fn add(self, rhs: &'a Self) -> Self::Output {
let mut lhs = self;
match (lhs.to_packed_mut::<SequenceElem>(), rhs.to_packed::<SequenceElem>()) {
(Some(seq_lhs), Some(rhs)) => {
seq_lhs.children.extend(rhs.children.iter().cloned());
lhs
}
(Some(seq_lhs), None) => {
seq_lhs.children.push(rhs.clone());
lhs
}
(None, Some(_)) => {
let mut rhs = rhs.clone();
rhs.to_packed_mut::<SequenceElem>().unwrap().children.insert(0, lhs);
rhs
}
(None, None) => Self::sequence([lhs, rhs.clone()]),
}
}
}
impl AddAssign for Content {
fn add_assign(&mut self, rhs: Self) {
*self = std::mem::take(self) + rhs;
}
}
impl AddAssign<&Self> for Content {
fn add_assign(&mut self, rhs: &Self) {
*self = std::mem::take(self) + rhs;
}
}
impl Sum for Content {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
Self::sequence(iter)
}
}
impl Serialize for Content {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_map(
iter::once(("func".into(), self.func().name().into_value()))
.chain(self.fields()),
)
}
}
trait Bounds: Debug + Repr + Fields + Send + Sync + 'static {
fn dyn_type_id(&self) -> TypeId;
fn dyn_elem(&self) -> Element;
fn dyn_clone(&self, inner: &Inner<dyn Bounds>, span: Span) -> Content;
fn dyn_hash(&self, hasher: &mut dyn Hasher);
fn dyn_eq(&self, other: &Content) -> bool;
}
impl<T: NativeElement> Bounds for T {
fn dyn_type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
fn dyn_elem(&self) -> Element {
Self::elem()
}
fn dyn_clone(&self, inner: &Inner<dyn Bounds>, span: Span) -> Content {
Content {
inner: Arc::new(Inner {
label: inner.label,
location: inner.location,
lifecycle: inner.lifecycle.clone(),
elem: LazyHash::reuse(self.clone(), &inner.elem),
}),
span,
}
}
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
TypeId::of::<Self>().hash(&mut state);
self.hash(&mut state);
}
fn dyn_eq(&self, other: &Content) -> bool {
let Some(other) = other.to_packed::<Self>() else {
return false;
};
*self == **other
}
}
impl Hash for dyn Bounds {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
}
}
#[derive(Clone, PartialEq, Hash)]
#[repr(transparent)]
pub struct Packed<T: NativeElement>(
Content,
PhantomData<T>,
);
impl<T: NativeElement> Packed<T> {
pub fn new(element: T) -> Self {
Packed(element.pack(), PhantomData)
}
pub fn from_ref(content: &Content) -> Option<&Self> {
if content.is::<T>() {
return Some(unsafe { std::mem::transmute::<&Content, &Packed<T>>(content) });
}
None
}
pub fn from_mut(content: &mut Content) -> Option<&mut Self> {
if content.is::<T>() {
return Some(unsafe {
std::mem::transmute::<&mut Content, &mut Packed<T>>(content)
});
}
None
}
pub fn from_owned(content: Content) -> Result<Self, Content> {
if content.is::<T>() {
return Ok(unsafe { std::mem::transmute::<Content, Packed<T>>(content) });
}
Err(content)
}
pub fn pack(self) -> Content {
self.0
}
pub fn unpack(self) -> T {
(*self).clone()
}
pub fn span(&self) -> Span {
self.0.span()
}
pub fn spanned(self, span: Span) -> Self {
Self(self.0.spanned(span), PhantomData)
}
pub fn label(&self) -> Option<Label> {
self.0.label()
}
pub fn location(&self) -> Option<Location> {
self.0.location()
}
pub fn set_location(&mut self, location: Location) {
self.0.set_location(location);
}
}
impl<T: NativeElement> AsRef<T> for Packed<T> {
fn as_ref(&self) -> &T {
self
}
}
impl<T: NativeElement> AsMut<T> for Packed<T> {
fn as_mut(&mut self) -> &mut T {
self
}
}
impl<T: NativeElement> Deref for Packed<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
let elem = &*self.0.inner.elem;
unsafe { &*(elem as *const dyn Bounds as *const T) }
}
}
impl<T: NativeElement> DerefMut for Packed<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
let elem = &mut *self.0.make_mut().elem;
unsafe { &mut *(elem as *mut dyn Bounds as *mut T) }
}
}
impl<T: NativeElement + Debug> Debug for Packed<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[elem(Debug, Repr, PartialEq)]
pub struct SequenceElem {
#[required]
pub children: Vec<Content>,
}
impl Debug for SequenceElem {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Sequence ")?;
f.debug_list().entries(&self.children).finish()
}
}
#[allow(clippy::derivable_impls)]
impl Default for SequenceElem {
fn default() -> Self {
Self { children: Default::default() }
}
}
impl PartialEq for SequenceElem {
fn eq(&self, other: &Self) -> bool {
self.children.iter().eq(other.children.iter())
}
}
impl Repr for SequenceElem {
fn repr(&self) -> EcoString {
if self.children.is_empty() {
"[]".into()
} else {
let elements = crate::foundations::repr::pretty_array_like(
&self.children.iter().map(|c| c.inner.elem.repr()).collect::<Vec<_>>(),
false,
);
eco_format!("sequence{}", elements)
}
}
}
#[elem(Debug, Repr, PartialEq)]
pub struct StyledElem {
#[required]
pub child: Content,
#[required]
pub styles: Styles,
}
impl Debug for StyledElem {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
for style in self.styles.iter() {
writeln!(f, "#{style:?}")?;
}
self.child.fmt(f)
}
}
impl PartialEq for StyledElem {
fn eq(&self, other: &Self) -> bool {
self.child == other.child
}
}
impl Repr for StyledElem {
fn repr(&self) -> EcoString {
eco_format!("styled(child: {}, ..)", self.child.repr())
}
}
pub trait PlainText {
fn plain_text(&self, text: &mut EcoString);
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum FieldAccessError {
Unknown,
Unset,
Internal,
}
impl FieldAccessError {
#[cold]
pub fn message(self, content: &Content, field: &str) -> EcoString {
let elem_name = content.elem().name();
match self {
FieldAccessError::Unknown => {
eco_format!("{elem_name} does not have field {}", field.repr())
}
FieldAccessError::Unset => {
eco_format!(
"field {} in {elem_name} is not known at this point",
field.repr()
)
}
FieldAccessError::Internal => {
eco_format!(
"internal error when accessing field {} in {elem_name} – this is a bug",
field.repr()
)
}
}
}
#[cold]
pub fn message_no_default(self, content: &Content, field: &str) -> EcoString {
let mut msg = self.message(content, field);
msg.push_str(" and no default was specified");
msg
}
}