use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Content, Depth, Label, NativeElement, Packed, Show, ShowSet, Smart,
StyleChain, Styles,
};
use crate::introspection::Locatable;
use crate::layout::{
Alignment, BlockBody, BlockElem, Em, HElem, PadElem, Spacing, VElem,
};
use crate::model::{CitationForm, CiteElem};
use crate::text::{SmartQuoteElem, SmartQuotes, SpaceElem, TextElem};
#[elem(Locatable, ShowSet, Show)]
pub struct QuoteElem {
block: bool,
quotes: Smart<bool>,
#[borrowed]
attribution: Option<Attribution>,
#[required]
body: Content,
#[internal]
#[fold]
#[ghost]
depth: Depth,
}
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum Attribution {
Content(Content),
Label(Label),
}
cast! {
Attribution,
self => match self {
Self::Content(content) => content.into_value(),
Self::Label(label) => label.into_value(),
},
content: Content => Self::Content(content),
label: Label => Self::Label(label),
}
impl Show for Packed<QuoteElem> {
#[typst_macros::time(name = "quote", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body().clone();
let block = self.block(styles);
if self.quotes(styles) == Smart::Custom(true) || !block {
let quotes = SmartQuotes::get(
SmartQuoteElem::quotes_in(styles),
TextElem::lang_in(styles),
TextElem::region_in(styles),
SmartQuoteElem::alternative_in(styles),
);
let Depth(depth) = QuoteElem::depth_in(styles);
let double = depth % 2 == 0;
let hole = HElem::hole().pack();
realized = Content::sequence([
TextElem::packed(quotes.open(double)),
hole.clone(),
realized,
hole,
TextElem::packed(quotes.close(double)),
])
.styled(QuoteElem::set_depth(Depth(1)));
}
if block {
realized = BlockElem::new()
.with_body(Some(BlockBody::Content(realized)))
.pack()
.spanned(self.span());
if let Some(attribution) = self.attribution(styles).as_ref() {
let mut seq = vec![TextElem::packed('—'), SpaceElem::shared().clone()];
match attribution {
Attribution::Content(content) => {
seq.push(content.clone());
}
Attribution::Label(label) => {
seq.push(
CiteElem::new(*label)
.with_form(Some(CitationForm::Prose))
.pack()
.spanned(self.span()),
);
}
}
let gap = Spacing::Rel(Em::new(0.9).into());
let v = VElem::new(gap).with_weak(true).pack();
realized += v + Content::sequence(seq).aligned(Alignment::END);
}
realized = PadElem::new(realized).pack();
} else if let Some(Attribution::Label(label)) = self.attribution(styles) {
realized += SpaceElem::shared().clone()
+ CiteElem::new(*label).pack().spanned(self.span());
}
Ok(realized)
}
}
impl ShowSet for Packed<QuoteElem> {
fn show_set(&self, styles: StyleChain) -> Styles {
let mut out = Styles::new();
if self.block(styles) {
out.set(PadElem::set_left(Em::new(1.0).into()));
out.set(PadElem::set_right(Em::new(1.0).into()));
out.set(BlockElem::set_above(Smart::Custom(Em::new(2.4).into())));
out.set(BlockElem::set_below(Smart::Custom(Em::new(1.8).into())));
}
out
}
}