diff options
Diffstat (limited to 'crates/typst-library/src/model')
| -rw-r--r-- | crates/typst-library/src/model/bibliography.rs | 92 | ||||
| -rw-r--r-- | crates/typst-library/src/model/cite.rs | 10 | ||||
| -rw-r--r-- | crates/typst-library/src/model/emph.rs | 25 | ||||
| -rw-r--r-- | crates/typst-library/src/model/enum.rs | 63 | ||||
| -rw-r--r-- | crates/typst-library/src/model/figure.rs | 140 | ||||
| -rw-r--r-- | crates/typst-library/src/model/footnote.rs | 64 | ||||
| -rw-r--r-- | crates/typst-library/src/model/heading.rs | 109 | ||||
| -rw-r--r-- | crates/typst-library/src/model/link.rs | 41 | ||||
| -rw-r--r-- | crates/typst-library/src/model/list.rs | 49 | ||||
| -rw-r--r-- | crates/typst-library/src/model/mod.rs | 20 | ||||
| -rw-r--r-- | crates/typst-library/src/model/outline.rs | 86 | ||||
| -rw-r--r-- | crates/typst-library/src/model/quote.rs | 155 | ||||
| -rw-r--r-- | crates/typst-library/src/model/reference.rs | 20 | ||||
| -rw-r--r-- | crates/typst-library/src/model/strong.rs | 25 | ||||
| -rw-r--r-- | crates/typst-library/src/model/table.rs | 133 | ||||
| -rw-r--r-- | crates/typst-library/src/model/terms.rs | 104 |
16 files changed, 168 insertions, 968 deletions
diff --git a/crates/typst-library/src/model/bibliography.rs b/crates/typst-library/src/model/bibliography.rs index c44748a9..188af4da 100644 --- a/crates/typst-library/src/model/bibliography.rs +++ b/crates/typst-library/src/model/bibliography.rs @@ -2,7 +2,6 @@ use std::any::TypeId; use std::collections::HashMap; use std::ffi::OsStr; use std::fmt::{self, Debug, Formatter}; -use std::num::NonZeroUsize; use std::path::Path; use std::sync::{Arc, LazyLock}; @@ -17,7 +16,7 @@ use hayagriva::{ use indexmap::IndexMap; use smallvec::{smallvec, SmallVec}; use typst_syntax::{Span, Spanned, SyntaxMode}; -use typst_utils::{Get, ManuallyHash, NonZeroExt, PicoStr}; +use typst_utils::{ManuallyHash, PicoStr}; use crate::diag::{ bail, error, At, HintedStrResult, LoadError, LoadResult, LoadedWithin, ReportPos, @@ -26,18 +25,17 @@ use crate::diag::{ use crate::engine::{Engine, Sink}; use crate::foundations::{ elem, Bytes, CastInfo, Content, Derived, FromValue, IntoValue, Label, NativeElement, - OneOrMultiple, Packed, Reflect, Scope, Show, ShowSet, Smart, StyleChain, Styles, + OneOrMultiple, Packed, Reflect, Scope, ShowSet, Smart, StyleChain, Styles, Synthesize, Value, }; use crate::introspection::{Introspector, Locatable, Location}; use crate::layout::{ BlockBody, BlockElem, Em, GridCell, GridChild, GridElem, GridItem, HElem, PadElem, - Sides, Sizing, TrackSizings, + Sizing, TrackSizings, }; use crate::loading::{format_yaml_error, DataSource, Load, LoadSource, Loaded}; use crate::model::{ - CitationForm, CiteGroup, Destination, FootnoteElem, HeadingElem, LinkElem, ParElem, - Url, + CitationForm, CiteGroup, Destination, FootnoteElem, HeadingElem, LinkElem, Url, }; use crate::routines::Routines; use crate::text::{ @@ -88,7 +86,7 @@ use crate::World; /// /// #bibliography("works.bib") /// ``` -#[elem(Locatable, Synthesize, Show, ShowSet, LocalName)] +#[elem(Locatable, Synthesize, ShowSet, LocalName)] pub struct BibliographyElem { /// One or multiple paths to or raw bytes for Hayagriva `.yaml` and/or /// BibLaTeX `.bib` files. @@ -203,84 +201,6 @@ impl Synthesize for Packed<BibliographyElem> { } } -impl Show for Packed<BibliographyElem> { - #[typst_macros::time(name = "bibliography", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - const COLUMN_GUTTER: Em = Em::new(0.65); - const INDENT: Em = Em::new(1.5); - - let span = self.span(); - - let mut seq = vec![]; - if let Some(title) = self.title.get_ref(styles).clone().unwrap_or_else(|| { - Some(TextElem::packed(Self::local_name_in(styles)).spanned(span)) - }) { - seq.push( - HeadingElem::new(title) - .with_depth(NonZeroUsize::ONE) - .pack() - .spanned(span), - ); - } - - let works = Works::generate(engine).at(span)?; - let references = works - .references - .as_ref() - .ok_or_else(|| match self.style.get_ref(styles).source { - CslSource::Named(style) => eco_format!( - "CSL style \"{}\" is not suitable for bibliographies", - style.display_name() - ), - CslSource::Normal(..) => { - "CSL style is not suitable for bibliographies".into() - } - }) - .at(span)?; - - if references.iter().any(|(prefix, _)| prefix.is_some()) { - let row_gutter = styles.get(ParElem::spacing); - - let mut cells = vec![]; - for (prefix, reference) in references { - cells.push(GridChild::Item(GridItem::Cell( - Packed::new(GridCell::new(prefix.clone().unwrap_or_default())) - .spanned(span), - ))); - cells.push(GridChild::Item(GridItem::Cell( - Packed::new(GridCell::new(reference.clone())).spanned(span), - ))); - } - seq.push( - GridElem::new(cells) - .with_columns(TrackSizings(smallvec![Sizing::Auto; 2])) - .with_column_gutter(TrackSizings(smallvec![COLUMN_GUTTER.into()])) - .with_row_gutter(TrackSizings(smallvec![row_gutter.into()])) - .pack() - .spanned(span), - ); - } else { - for (_, reference) in references { - let realized = reference.clone(); - let block = if works.hanging_indent { - let body = HElem::new((-INDENT).into()).pack() + realized; - let inset = Sides::default() - .with(styles.resolve(TextElem::dir).start(), Some(INDENT.into())); - BlockElem::new() - .with_body(Some(BlockBody::Content(body))) - .with_inset(inset) - } else { - BlockElem::new().with_body(Some(BlockBody::Content(realized))) - }; - - seq.push(block.pack().spanned(span)); - } - } - - Ok(Content::sequence(seq)) - } -} - impl ShowSet for Packed<BibliographyElem> { fn show_set(&self, _: StyleChain) -> Styles { const INDENT: Em = Em::new(1.0); @@ -564,7 +484,7 @@ impl IntoValue for CslSource { /// memoization) for the whole document. This setup is necessary because /// citation formatting is inherently stateful and we need access to all /// citations to do it. -pub(super) struct Works { +pub struct Works { /// Maps from the location of a citation group to its rendered content. pub citations: HashMap<Location, SourceResult<Content>>, /// Lists all references in the bibliography, with optional prefix, or diff --git a/crates/typst-library/src/model/cite.rs b/crates/typst-library/src/model/cite.rs index 19513990..b3ae3e52 100644 --- a/crates/typst-library/src/model/cite.rs +++ b/crates/typst-library/src/model/cite.rs @@ -3,8 +3,7 @@ use typst_syntax::Spanned; use crate::diag::{error, At, HintedString, SourceResult}; use crate::engine::Engine; use crate::foundations::{ - cast, elem, Cast, Content, Derived, Label, Packed, Show, Smart, StyleChain, - Synthesize, + cast, elem, Cast, Content, Derived, Label, Packed, Smart, StyleChain, Synthesize, }; use crate::introspection::Locatable; use crate::model::bibliography::Works; @@ -153,16 +152,15 @@ pub enum CitationForm { /// /// This is automatically created from adjacent citations during show rule /// application. -#[elem(Locatable, Show)] +#[elem(Locatable)] pub struct CiteGroup { /// The citations. #[required] pub children: Vec<Packed<CiteElem>>, } -impl Show for Packed<CiteGroup> { - #[typst_macros::time(name = "cite", span = self.span())] - fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> { +impl Packed<CiteGroup> { + pub fn realize(&self, engine: &mut Engine) -> SourceResult<Content> { let location = self.location().unwrap(); let span = self.span(); Works::generate(engine) diff --git a/crates/typst-library/src/model/emph.rs b/crates/typst-library/src/model/emph.rs index 2d9cbec1..6267736b 100644 --- a/crates/typst-library/src/model/emph.rs +++ b/crates/typst-library/src/model/emph.rs @@ -1,10 +1,4 @@ -use crate::diag::SourceResult; -use crate::engine::Engine; -use crate::foundations::{ - elem, Content, NativeElement, Packed, Show, StyleChain, TargetElem, -}; -use crate::html::{tag, HtmlElem}; -use crate::text::{ItalicToggle, TextElem}; +use crate::foundations::{elem, Content}; /// Emphasizes content by toggling italics. /// @@ -29,24 +23,9 @@ use crate::text::{ItalicToggle, TextElem}; /// This function also has dedicated syntax: To emphasize content, simply /// enclose it in underscores (`_`). Note that this only works at word /// boundaries. To emphasize part of a word, you have to use the function. -#[elem(title = "Emphasis", keywords = ["italic"], Show)] +#[elem(title = "Emphasis", keywords = ["italic"])] pub struct EmphElem { /// The content to emphasize. #[required] pub body: Content, } - -impl Show for Packed<EmphElem> { - #[typst_macros::time(name = "emph", span = self.span())] - fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let body = self.body.clone(); - Ok(if styles.get(TargetElem::target).is_html() { - HtmlElem::new(tag::em) - .with_body(Some(body)) - .pack() - .spanned(self.span()) - } else { - body.set(TextElem::emph, ItalicToggle(true)) - }) - } -} diff --git a/crates/typst-library/src/model/enum.rs b/crates/typst-library/src/model/enum.rs index 8c191658..388fb9ed 100644 --- a/crates/typst-library/src/model/enum.rs +++ b/crates/typst-library/src/model/enum.rs @@ -1,19 +1,11 @@ use std::str::FromStr; -use ecow::eco_format; use smallvec::SmallVec; -use crate::diag::{bail, SourceResult}; -use crate::engine::Engine; -use crate::foundations::{ - cast, elem, scope, Array, Content, NativeElement, Packed, Show, Smart, StyleChain, - Styles, TargetElem, -}; -use crate::html::{attr, tag, HtmlElem}; -use crate::layout::{Alignment, BlockElem, Em, HAlignment, Length, VAlignment, VElem}; -use crate::model::{ - ListItemLike, ListLike, Numbering, NumberingPattern, ParElem, ParbreakElem, -}; +use crate::diag::bail; +use crate::foundations::{cast, elem, scope, Array, Content, Packed, Smart, Styles}; +use crate::layout::{Alignment, Em, HAlignment, Length, VAlignment}; +use crate::model::{ListItemLike, ListLike, Numbering, NumberingPattern}; /// A numbered list. /// @@ -71,7 +63,7 @@ use crate::model::{ /// Enumeration items can contain multiple paragraphs and other block-level /// content. All content that is indented more than an item's marker becomes /// part of that item. -#[elem(scope, title = "Numbered List", Show)] +#[elem(scope, title = "Numbered List")] pub struct EnumElem { /// Defines the default [spacing]($enum.spacing) of the enumeration. If it /// is `{false}`, the items are spaced apart with @@ -223,51 +215,6 @@ impl EnumElem { type EnumItem; } -impl Show for Packed<EnumElem> { - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let tight = self.tight.get(styles); - - if styles.get(TargetElem::target).is_html() { - let mut elem = HtmlElem::new(tag::ol); - if self.reversed.get(styles) { - elem = elem.with_attr(attr::reversed, "reversed"); - } - if let Some(n) = self.start.get(styles).custom() { - elem = elem.with_attr(attr::start, eco_format!("{n}")); - } - let body = Content::sequence(self.children.iter().map(|item| { - let mut li = HtmlElem::new(tag::li); - if let Some(nr) = item.number.get(styles) { - li = li.with_attr(attr::value, eco_format!("{nr}")); - } - // Text in wide enums shall always turn into paragraphs. - let mut body = item.body.clone(); - if !tight { - body += ParbreakElem::shared(); - } - li.with_body(Some(body)).pack().spanned(item.span()) - })); - return Ok(elem.with_body(Some(body)).pack().spanned(self.span())); - } - - let mut realized = - BlockElem::multi_layouter(self.clone(), engine.routines.layout_enum) - .pack() - .spanned(self.span()); - - if tight { - let spacing = self - .spacing - .get(styles) - .unwrap_or_else(|| styles.get(ParElem::leading)); - let v = VElem::new(spacing.into()).with_weak(true).with_attach(true).pack(); - realized = v + realized; - } - - Ok(realized) - } -} - /// An enumeration item. #[elem(name = "item", title = "Numbered List Item")] pub struct EnumItem { diff --git a/crates/typst-library/src/model/figure.rs b/crates/typst-library/src/model/figure.rs index 7ac659a9..ac3676ee 100644 --- a/crates/typst-library/src/model/figure.rs +++ b/crates/typst-library/src/model/figure.rs @@ -9,19 +9,16 @@ use crate::diag::{bail, SourceResult}; use crate::engine::Engine; use crate::foundations::{ cast, elem, scope, select_where, Content, Element, NativeElement, Packed, Selector, - Show, ShowSet, Smart, StyleChain, Styles, Synthesize, TargetElem, + ShowSet, Smart, StyleChain, Styles, Synthesize, }; -use crate::html::{tag, HtmlElem}; use crate::introspection::{ Count, Counter, CounterKey, CounterUpdate, Locatable, Location, }; use crate::layout::{ - AlignElem, Alignment, BlockBody, BlockElem, Em, HAlignment, Length, OuterVAlignment, - PlaceElem, PlacementScope, VAlignment, VElem, -}; -use crate::model::{ - Numbering, NumberingPattern, Outlinable, ParbreakElem, Refable, Supplement, + AlignElem, Alignment, BlockElem, Em, Length, OuterVAlignment, PlacementScope, + VAlignment, }; +use crate::model::{Numbering, NumberingPattern, Outlinable, Refable, Supplement}; use crate::text::{Lang, Region, TextElem}; use crate::visualize::ImageElem; @@ -104,7 +101,7 @@ use crate::visualize::ImageElem; /// caption: [I'm up here], /// ) /// ``` -#[elem(scope, Locatable, Synthesize, Count, Show, ShowSet, Refable, Outlinable)] +#[elem(scope, Locatable, Synthesize, Count, ShowSet, Refable, Outlinable)] pub struct FigureElem { /// The content of the figure. Often, an [image]. #[required] @@ -328,65 +325,6 @@ impl Synthesize for Packed<FigureElem> { } } -impl Show for Packed<FigureElem> { - #[typst_macros::time(name = "figure", span = self.span())] - fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let span = self.span(); - let target = styles.get(TargetElem::target); - let mut realized = self.body.clone(); - - // Build the caption, if any. - if let Some(caption) = self.caption.get_cloned(styles) { - let (first, second) = match caption.position.get(styles) { - OuterVAlignment::Top => (caption.pack(), realized), - OuterVAlignment::Bottom => (realized, caption.pack()), - }; - let mut seq = Vec::with_capacity(3); - seq.push(first); - if !target.is_html() { - let v = VElem::new(self.gap.get(styles).into()).with_weak(true); - seq.push(v.pack().spanned(span)) - } - seq.push(second); - realized = Content::sequence(seq) - } - - // Ensure that the body is considered a paragraph. - realized += ParbreakElem::shared().clone().spanned(span); - - if target.is_html() { - return Ok(HtmlElem::new(tag::figure) - .with_body(Some(realized)) - .pack() - .spanned(span)); - } - - // Wrap the contents in a block. - realized = BlockElem::new() - .with_body(Some(BlockBody::Content(realized))) - .pack() - .spanned(span); - - // Wrap in a float. - if let Some(align) = self.placement.get(styles) { - realized = PlaceElem::new(realized) - .with_alignment(align.map(|align| HAlignment::Center + align)) - .with_scope(self.scope.get(styles)) - .with_float(true) - .pack() - .spanned(span); - } else if self.scope.get(styles) == PlacementScope::Parent { - bail!( - span, - "parent-scoped placement is only available for floating figures"; - hint: "you can enable floating placement with `figure(placement: auto, ..)`" - ); - } - - Ok(realized) - } -} - impl ShowSet for Packed<FigureElem> { fn show_set(&self, _: StyleChain) -> Styles { // Still allows breakable figures with @@ -471,7 +409,7 @@ impl Outlinable for Packed<FigureElem> { /// caption: [A rectangle], /// ) /// ``` -#[elem(name = "caption", Synthesize, Show)] +#[elem(name = "caption", Synthesize)] pub struct FigureCaption { /// The caption's position in the figure. Either `{top}` or `{bottom}`. /// @@ -559,6 +497,35 @@ pub struct FigureCaption { } impl FigureCaption { + /// Realizes the textual caption content. + pub fn realize( + &self, + engine: &mut Engine, + styles: StyleChain, + ) -> SourceResult<Content> { + let mut realized = self.body.clone(); + + if let ( + Some(Some(mut supplement)), + Some(Some(numbering)), + Some(Some(counter)), + Some(Some(location)), + ) = ( + self.supplement.clone(), + &self.numbering, + &self.counter, + &self.figure_location, + ) { + let numbers = counter.display_at_loc(engine, *location, styles, numbering)?; + if !supplement.is_empty() { + supplement += TextElem::packed('\u{a0}'); + } + realized = supplement + numbers + self.get_separator(styles) + realized; + } + + Ok(realized) + } + /// Gets the default separator in the given language and (optionally) /// region. fn local_separator(lang: Lang, _: Option<Region>) -> &'static str { @@ -588,43 +555,6 @@ impl Synthesize for Packed<FigureCaption> { } } -impl Show for Packed<FigureCaption> { - #[typst_macros::time(name = "figure.caption", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let mut realized = self.body.clone(); - - if let ( - Some(Some(mut supplement)), - Some(Some(numbering)), - Some(Some(counter)), - Some(Some(location)), - ) = ( - self.supplement.clone(), - &self.numbering, - &self.counter, - &self.figure_location, - ) { - let numbers = counter.display_at_loc(engine, *location, styles, numbering)?; - if !supplement.is_empty() { - supplement += TextElem::packed('\u{a0}'); - } - realized = supplement + numbers + self.get_separator(styles) + realized; - } - - Ok(if styles.get(TargetElem::target).is_html() { - HtmlElem::new(tag::figcaption) - .with_body(Some(realized)) - .pack() - .spanned(self.span()) - } else { - BlockElem::new() - .with_body(Some(BlockBody::Content(realized))) - .pack() - .spanned(self.span()) - }) - } -} - cast! { FigureCaption, v: Content => v.unpack::<Self>().unwrap_or_else(Self::new), diff --git a/crates/typst-library/src/model/footnote.rs b/crates/typst-library/src/model/footnote.rs index 63a461bd..b920a8ae 100644 --- a/crates/typst-library/src/model/footnote.rs +++ b/crates/typst-library/src/model/footnote.rs @@ -3,16 +3,16 @@ use std::str::FromStr; use typst_utils::NonZeroExt; -use crate::diag::{bail, At, SourceResult, StrResult}; +use crate::diag::{bail, StrResult}; use crate::engine::Engine; use crate::foundations::{ - cast, elem, scope, Content, Label, NativeElement, Packed, Show, ShowSet, Smart, - StyleChain, Styles, + cast, elem, scope, Content, Label, NativeElement, Packed, ShowSet, Smart, StyleChain, + Styles, }; -use crate::introspection::{Count, Counter, CounterUpdate, Locatable, Location}; -use crate::layout::{Abs, Em, HElem, Length, Ratio}; -use crate::model::{Destination, Numbering, NumberingPattern, ParElem}; -use crate::text::{SuperElem, TextElem, TextSize}; +use crate::introspection::{Count, CounterUpdate, Locatable, Location}; +use crate::layout::{Abs, Em, Length, Ratio}; +use crate::model::{Numbering, NumberingPattern, ParElem}; +use crate::text::{TextElem, TextSize}; use crate::visualize::{LineElem, Stroke}; /// A footnote. @@ -51,7 +51,7 @@ use crate::visualize::{LineElem, Stroke}; /// apply to the footnote's content. See [here][issue] for more information. /// /// [issue]: https://github.com/typst/typst/issues/1467#issuecomment-1588799440 -#[elem(scope, Locatable, Show, Count)] +#[elem(scope, Locatable, Count)] pub struct FootnoteElem { /// How to number footnotes. /// @@ -135,21 +135,6 @@ impl Packed<FootnoteElem> { } } -impl Show for Packed<FootnoteElem> { - #[typst_macros::time(name = "footnote", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let span = self.span(); - let loc = self.declaration_location(engine).at(span)?; - let numbering = self.numbering.get_ref(styles); - let counter = Counter::of(FootnoteElem::ELEM); - let num = counter.display_at_loc(engine, loc, styles, numbering)?; - let sup = SuperElem::new(num).pack().spanned(span); - let loc = loc.variant(1); - // Add zero-width weak spacing to make the footnote "sticky". - Ok(HElem::hole().pack() + sup.linked(Destination::Location(loc))) - } -} - impl Count for Packed<FootnoteElem> { fn update(&self) -> Option<CounterUpdate> { (!self.is_ref()).then(|| CounterUpdate::Step(NonZeroUsize::ONE)) @@ -191,7 +176,7 @@ cast! { /// page run is a sequence of pages without an explicit pagebreak in between). /// For this reason, set and show rules for footnote entries should be defined /// before any page content, typically at the very start of the document. -#[elem(name = "entry", title = "Footnote Entry", Show, ShowSet)] +#[elem(name = "entry", title = "Footnote Entry", ShowSet)] pub struct FootnoteEntry { /// The footnote for this entry. Its location can be used to determine /// the footnote counter state. @@ -274,37 +259,6 @@ pub struct FootnoteEntry { pub indent: Length, } -impl Show for Packed<FootnoteEntry> { - #[typst_macros::time(name = "footnote.entry", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let span = self.span(); - let number_gap = Em::new(0.05); - let default = StyleChain::default(); - let numbering = self.note.numbering.get_ref(default); - let counter = Counter::of(FootnoteElem::ELEM); - let Some(loc) = self.note.location() else { - bail!( - span, "footnote entry must have a location"; - hint: "try using a query or a show rule to customize the footnote instead" - ); - }; - - let num = counter.display_at_loc(engine, loc, styles, numbering)?; - let sup = SuperElem::new(num) - .pack() - .spanned(span) - .linked(Destination::Location(loc)) - .located(loc.variant(1)); - - Ok(Content::sequence([ - HElem::new(self.indent.get(styles).into()).pack(), - sup, - HElem::new(number_gap.into()).with_weak(true).pack(), - self.note.body_content().unwrap().clone(), - ])) - } -} - impl ShowSet for Packed<FootnoteEntry> { fn show_set(&self, _: StyleChain) -> Styles { let mut out = Styles::new(); diff --git a/crates/typst-library/src/model/heading.rs b/crates/typst-library/src/model/heading.rs index d6f6d01f..0f2a1d33 100644 --- a/crates/typst-library/src/model/heading.rs +++ b/crates/typst-library/src/model/heading.rs @@ -1,21 +1,16 @@ use std::num::NonZeroUsize; -use ecow::eco_format; -use typst_utils::{Get, NonZeroExt}; +use typst_utils::NonZeroExt; -use crate::diag::{warning, SourceResult}; +use crate::diag::SourceResult; use crate::engine::Engine; use crate::foundations::{ - elem, Content, NativeElement, Packed, Resolve, Show, ShowSet, Smart, StyleChain, - Styles, Synthesize, TargetElem, + elem, Content, NativeElement, Packed, ShowSet, Smart, StyleChain, Styles, Synthesize, }; -use crate::html::{attr, tag, HtmlElem}; -use crate::introspection::{ - Count, Counter, CounterUpdate, Locatable, Locator, LocatorLink, -}; -use crate::layout::{Abs, Axes, BlockBody, BlockElem, Em, HElem, Length, Region, Sides}; +use crate::introspection::{Count, Counter, CounterUpdate, Locatable}; +use crate::layout::{BlockElem, Em, Length}; use crate::model::{Numbering, Outlinable, Refable, Supplement}; -use crate::text::{FontWeight, LocalName, SpaceElem, TextElem, TextSize}; +use crate::text::{FontWeight, LocalName, TextElem, TextSize}; /// A section heading. /// @@ -49,7 +44,7 @@ use crate::text::{FontWeight, LocalName, SpaceElem, TextElem, TextSize}; /// one or multiple equals signs, followed by a space. The number of equals /// signs determines the heading's logical nesting depth. The `{offset}` field /// can be set to configure the starting depth. -#[elem(Locatable, Synthesize, Count, Show, ShowSet, LocalName, Refable, Outlinable)] +#[elem(Locatable, Synthesize, Count, ShowSet, LocalName, Refable, Outlinable)] pub struct HeadingElem { /// The absolute nesting depth of the heading, starting from one. If set /// to `{auto}`, it is computed from `{offset + depth}`. @@ -215,96 +210,6 @@ impl Synthesize for Packed<HeadingElem> { } } -impl Show for Packed<HeadingElem> { - #[typst_macros::time(name = "heading", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let html = styles.get(TargetElem::target).is_html(); - - const SPACING_TO_NUMBERING: Em = Em::new(0.3); - - let span = self.span(); - let mut realized = self.body.clone(); - - let hanging_indent = self.hanging_indent.get(styles); - let mut indent = match hanging_indent { - Smart::Custom(length) => length.resolve(styles), - Smart::Auto => Abs::zero(), - }; - - if let Some(numbering) = self.numbering.get_ref(styles).as_ref() { - let location = self.location().unwrap(); - let numbering = Counter::of(HeadingElem::ELEM) - .display_at_loc(engine, location, styles, numbering)? - .spanned(span); - - if hanging_indent.is_auto() && !html { - let pod = Region::new(Axes::splat(Abs::inf()), Axes::splat(false)); - - // We don't have a locator for the numbering here, so we just - // use the measurement infrastructure for now. - let link = LocatorLink::measure(location); - let size = (engine.routines.layout_frame)( - engine, - &numbering, - Locator::link(&link), - styles, - pod, - )? - .size(); - - indent = size.x + SPACING_TO_NUMBERING.resolve(styles); - } - - let spacing = if html { - SpaceElem::shared().clone() - } else { - HElem::new(SPACING_TO_NUMBERING.into()).with_weak(true).pack() - }; - - realized = numbering + spacing + realized; - } - - Ok(if html { - // HTML's h1 is closer to a title element. There should only be one. - // Meanwhile, a level 1 Typst heading is a section heading. For this - // reason, levels are offset by one: A Typst level 1 heading becomes - // a `<h2>`. - let level = self.resolve_level(styles).get(); - if level >= 6 { - engine.sink.warn(warning!(span, - "heading of level {} was transformed to \ - <div role=\"heading\" aria-level=\"{}\">, which is not \ - supported by all assistive technology", - level, level + 1; - hint: "HTML only supports <h1> to <h6>, not <h{}>", level + 1; - hint: "you may want to restructure your document so that \ - it doesn't contain deep headings")); - HtmlElem::new(tag::div) - .with_body(Some(realized)) - .with_attr(attr::role, "heading") - .with_attr(attr::aria_level, eco_format!("{}", level + 1)) - .pack() - .spanned(span) - } else { - let t = [tag::h2, tag::h3, tag::h4, tag::h5, tag::h6][level - 1]; - HtmlElem::new(t).with_body(Some(realized)).pack().spanned(span) - } - } else { - let block = if indent != Abs::zero() { - let body = HElem::new((-indent).into()).pack() + realized; - let inset = Sides::default() - .with(styles.resolve(TextElem::dir).start(), Some(indent.into())); - BlockElem::new() - .with_body(Some(BlockBody::Content(body))) - .with_inset(inset) - } else { - BlockElem::new().with_body(Some(BlockBody::Content(realized))) - }; - block.pack().spanned(span) - }) - } -} - impl ShowSet for Packed<HeadingElem> { fn show_set(&self, styles: StyleChain) -> Styles { let level = self.resolve_level(styles).get(); diff --git a/crates/typst-library/src/model/link.rs b/crates/typst-library/src/model/link.rs index 1e2c708e..c630835e 100644 --- a/crates/typst-library/src/model/link.rs +++ b/crates/typst-library/src/model/link.rs @@ -2,13 +2,10 @@ use std::ops::Deref; use ecow::{eco_format, EcoString}; -use crate::diag::{bail, warning, At, SourceResult, StrResult}; -use crate::engine::Engine; +use crate::diag::{bail, StrResult}; use crate::foundations::{ - cast, elem, Content, Label, NativeElement, Packed, Repr, Show, ShowSet, Smart, - StyleChain, Styles, TargetElem, + cast, elem, Content, Label, Packed, Repr, ShowSet, Smart, StyleChain, Styles, }; -use crate::html::{attr, tag, HtmlElem}; use crate::introspection::Location; use crate::layout::Position; use crate::text::TextElem; @@ -38,7 +35,7 @@ use crate::text::TextElem; /// # Syntax /// This function also has dedicated syntax: Text that starts with `http://` or /// `https://` is automatically turned into a link. -#[elem(Show)] +#[elem] pub struct LinkElem { /// The destination the link points to. /// @@ -103,38 +100,6 @@ impl LinkElem { } } -impl Show for Packed<LinkElem> { - #[typst_macros::time(name = "link", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let body = self.body.clone(); - - Ok(if styles.get(TargetElem::target).is_html() { - if let LinkTarget::Dest(Destination::Url(url)) = &self.dest { - HtmlElem::new(tag::a) - .with_attr(attr::href, url.clone().into_inner()) - .with_body(Some(body)) - .pack() - .spanned(self.span()) - } else { - engine.sink.warn(warning!( - self.span(), - "non-URL links are not yet supported by HTML export" - )); - body - } - } else { - match &self.dest { - LinkTarget::Dest(dest) => body.linked(dest.clone()), - LinkTarget::Label(label) => { - let elem = engine.introspector.query_label(*label).at(self.span())?; - let dest = Destination::Location(elem.location().unwrap()); - body.clone().linked(dest) - } - } - }) - } -} - impl ShowSet for Packed<LinkElem> { fn show_set(&self, _: StyleChain) -> Styles { let mut out = Styles::new(); diff --git a/crates/typst-library/src/model/list.rs b/crates/typst-library/src/model/list.rs index 5e6db1fa..660716de 100644 --- a/crates/typst-library/src/model/list.rs +++ b/crates/typst-library/src/model/list.rs @@ -3,12 +3,10 @@ use comemo::Track; use crate::diag::{bail, SourceResult}; use crate::engine::Engine; use crate::foundations::{ - cast, elem, scope, Array, Content, Context, Depth, Func, NativeElement, Packed, Show, - Smart, StyleChain, Styles, TargetElem, Value, + cast, elem, scope, Array, Content, Context, Depth, Func, NativeElement, Packed, + Smart, StyleChain, Styles, Value, }; -use crate::html::{tag, HtmlElem}; -use crate::layout::{BlockElem, Em, Length, VElem}; -use crate::model::{ParElem, ParbreakElem}; +use crate::layout::{Em, Length}; use crate::text::TextElem; /// A bullet list. @@ -42,7 +40,7 @@ use crate::text::TextElem; /// followed by a space to create a list item. A list item can contain multiple /// paragraphs and other block-level content. All content that is indented /// more than an item's marker becomes part of that item. -#[elem(scope, title = "Bullet List", Show)] +#[elem(scope, title = "Bullet List")] pub struct ListElem { /// Defines the default [spacing]($list.spacing) of the list. If it is /// `{false}`, the items are spaced apart with @@ -136,45 +134,6 @@ impl ListElem { type ListItem; } -impl Show for Packed<ListElem> { - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let tight = self.tight.get(styles); - - if styles.get(TargetElem::target).is_html() { - return Ok(HtmlElem::new(tag::ul) - .with_body(Some(Content::sequence(self.children.iter().map(|item| { - // Text in wide lists shall always turn into paragraphs. - let mut body = item.body.clone(); - if !tight { - body += ParbreakElem::shared(); - } - HtmlElem::new(tag::li) - .with_body(Some(body)) - .pack() - .spanned(item.span()) - })))) - .pack() - .spanned(self.span())); - } - - let mut realized = - BlockElem::multi_layouter(self.clone(), engine.routines.layout_list) - .pack() - .spanned(self.span()); - - if tight { - let spacing = self - .spacing - .get(styles) - .unwrap_or_else(|| styles.get(ParElem::leading)); - let v = VElem::new(spacing.into()).with_weak(true).with_attach(true).pack(); - realized = v + realized; - } - - Ok(realized) - } -} - /// A bullet list item. #[elem(name = "item", title = "Bullet List Item")] pub struct ListItem { diff --git a/crates/typst-library/src/model/mod.rs b/crates/typst-library/src/model/mod.rs index 9bdbf001..a0f7e11a 100644 --- a/crates/typst-library/src/model/mod.rs +++ b/crates/typst-library/src/model/mod.rs @@ -46,23 +46,23 @@ use crate::foundations::Scope; pub fn define(global: &mut Scope) { global.start_category(crate::Category::Model); global.define_elem::<DocumentElem>(); - global.define_elem::<RefElem>(); + global.define_elem::<ParElem>(); + global.define_elem::<ParbreakElem>(); + global.define_elem::<StrongElem>(); + global.define_elem::<EmphElem>(); + global.define_elem::<ListElem>(); + global.define_elem::<EnumElem>(); + global.define_elem::<TermsElem>(); global.define_elem::<LinkElem>(); - global.define_elem::<OutlineElem>(); global.define_elem::<HeadingElem>(); global.define_elem::<FigureElem>(); - global.define_elem::<FootnoteElem>(); global.define_elem::<QuoteElem>(); + global.define_elem::<FootnoteElem>(); + global.define_elem::<OutlineElem>(); + global.define_elem::<RefElem>(); global.define_elem::<CiteElem>(); global.define_elem::<BibliographyElem>(); - global.define_elem::<EnumElem>(); - global.define_elem::<ListElem>(); - global.define_elem::<ParbreakElem>(); - global.define_elem::<ParElem>(); global.define_elem::<TableElem>(); - global.define_elem::<TermsElem>(); - global.define_elem::<EmphElem>(); - global.define_elem::<StrongElem>(); global.define_func::<numbering>(); global.reset_category(); } diff --git a/crates/typst-library/src/model/outline.rs b/crates/typst-library/src/model/outline.rs index bb061fb7..4bda02ba 100644 --- a/crates/typst-library/src/model/outline.rs +++ b/crates/typst-library/src/model/outline.rs @@ -1,7 +1,7 @@ use std::num::NonZeroUsize; use std::str::FromStr; -use comemo::{Track, Tracked}; +use comemo::Tracked; use smallvec::SmallVec; use typst_syntax::Span; use typst_utils::{Get, NonZeroExt}; @@ -10,7 +10,7 @@ use crate::diag::{bail, error, At, HintedStrResult, SourceResult, StrResult}; use crate::engine::Engine; use crate::foundations::{ cast, elem, func, scope, select_where, Args, Construct, Content, Context, Func, - LocatableSelector, NativeElement, Packed, Resolve, Show, ShowSet, Smart, StyleChain, + LocatableSelector, NativeElement, Packed, Resolve, ShowSet, Smart, StyleChain, Styles, }; use crate::introspection::{ @@ -20,8 +20,7 @@ use crate::layout::{ Abs, Axes, BlockBody, BlockElem, BoxElem, Dir, Em, Fr, HElem, Length, Region, Rel, RepeatElem, Sides, }; -use crate::math::EquationElem; -use crate::model::{Destination, HeadingElem, NumberingPattern, ParElem, Refable}; +use crate::model::{HeadingElem, NumberingPattern, ParElem, Refable}; use crate::text::{LocalName, SpaceElem, TextElem}; /// A table of contents, figures, or other elements. @@ -147,7 +146,7 @@ use crate::text::{LocalName, SpaceElem, TextElem}; /// /// [^1]: The outline of equations is the exception to this rule as it does not /// have a body and thus does not use indented layout. -#[elem(scope, keywords = ["Table of Contents", "toc"], Show, ShowSet, LocalName, Locatable)] +#[elem(scope, keywords = ["Table of Contents", "toc"], ShowSet, LocalName, Locatable)] pub struct OutlineElem { /// The title of the outline. /// @@ -249,44 +248,6 @@ impl OutlineElem { type OutlineEntry; } -impl Show for Packed<OutlineElem> { - #[typst_macros::time(name = "outline", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let span = self.span(); - - // Build the outline title. - let mut seq = vec![]; - if let Some(title) = self.title.get_cloned(styles).unwrap_or_else(|| { - Some(TextElem::packed(Self::local_name_in(styles)).spanned(span)) - }) { - seq.push( - HeadingElem::new(title) - .with_depth(NonZeroUsize::ONE) - .pack() - .spanned(span), - ); - } - - let elems = engine.introspector.query(&self.target.get_ref(styles).0); - let depth = self.depth.get(styles).unwrap_or(NonZeroUsize::MAX); - - // Build the outline entries. - for elem in elems { - let Some(outlinable) = elem.with::<dyn Outlinable>() else { - bail!(span, "cannot outline {}", elem.func().name()); - }; - - let level = outlinable.level(); - if outlinable.outlined() && level <= depth { - let entry = OutlineEntry::new(level, elem); - seq.push(entry.pack().spanned(span)); - } - } - - Ok(Content::sequence(seq)) - } -} - impl ShowSet for Packed<OutlineElem> { fn show_set(&self, styles: StyleChain) -> Styles { let mut out = Styles::new(); @@ -363,7 +324,7 @@ pub trait Outlinable: Refable { /// With show-set and show rules on outline entries, you can richly customize /// the outline's appearance. See the /// [section on styling the outline]($outline/#styling-the-outline) for details. -#[elem(scope, name = "entry", title = "Outline Entry", Show)] +#[elem(scope, name = "entry", title = "Outline Entry")] pub struct OutlineEntry { /// The nesting level of this outline entry. Starts at `{1}` for top-level /// entries. @@ -408,30 +369,6 @@ pub struct OutlineEntry { pub parent: Option<Packed<OutlineElem>>, } -impl Show for Packed<OutlineEntry> { - #[typst_macros::time(name = "outline.entry", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let span = self.span(); - let context = Context::new(None, Some(styles)); - let context = context.track(); - - let prefix = self.prefix(engine, context, span)?; - let inner = self.inner(engine, context, span)?; - let block = if self.element.is::<EquationElem>() { - let body = prefix.unwrap_or_default() + inner; - BlockElem::new() - .with_body(Some(BlockBody::Content(body))) - .pack() - .spanned(span) - } else { - self.indented(engine, context, span, prefix, inner, Em::new(0.5).into())? - }; - - let loc = self.element_location().at(span)?; - Ok(block.linked(Destination::Location(loc))) - } -} - #[scope] impl OutlineEntry { /// A helper function for producing an indented entry layout: Lays out a @@ -654,7 +591,8 @@ impl OutlineEntry { .ok_or_else(|| error!("cannot outline {}", self.element.func().name())) } - fn element_location(&self) -> HintedStrResult<Location> { + /// Returns the location of the outlined element. + pub fn element_location(&self) -> HintedStrResult<Location> { let elem = &self.element; elem.location().ok_or_else(|| { if elem.can::<dyn Locatable>() && elem.can::<dyn Outlinable>() { @@ -730,8 +668,8 @@ fn query_prefix_widths( } /// Helper type for introspection-based prefix alignment. -#[elem(Construct, Locatable, Show)] -struct PrefixInfo { +#[elem(Construct, Locatable)] +pub(crate) struct PrefixInfo { /// The location of the outline this prefix is part of. This is used to /// scope prefix computations to a specific outline. #[required] @@ -753,9 +691,3 @@ impl Construct for PrefixInfo { bail!(args.span, "cannot be constructed manually"); } } - -impl Show for Packed<PrefixInfo> { - fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> { - Ok(Content::empty()) - } -} diff --git a/crates/typst-library/src/model/quote.rs b/crates/typst-library/src/model/quote.rs index a8cf3eae..9960b758 100644 --- a/crates/typst-library/src/model/quote.rs +++ b/crates/typst-library/src/model/quote.rs @@ -1,16 +1,13 @@ -use crate::diag::SourceResult; -use crate::engine::Engine; +use typst_syntax::Span; + use crate::foundations::{ - cast, elem, Content, Depth, Label, NativeElement, Packed, Show, ShowSet, Smart, - StyleChain, Styles, TargetElem, + cast, elem, Content, Depth, Label, NativeElement, Packed, ShowSet, Smart, StyleChain, + Styles, }; -use crate::html::{attr, tag, HtmlElem}; use crate::introspection::Locatable; -use crate::layout::{ - Alignment, BlockBody, BlockElem, Em, HElem, PadElem, Spacing, VElem, -}; -use crate::model::{CitationForm, CiteElem, Destination, LinkElem, LinkTarget}; -use crate::text::{SmartQuoteElem, SmartQuotes, SpaceElem, TextElem}; +use crate::layout::{BlockElem, Em, PadElem}; +use crate::model::{CitationForm, CiteElem}; +use crate::text::{SmartQuotes, SpaceElem, TextElem}; /// Displays a quote alongside an optional attribution. /// @@ -44,7 +41,7 @@ use crate::text::{SmartQuoteElem, SmartQuotes, SpaceElem, TextElem}; /// flame of Udûn. Go back to the Shadow! You cannot pass. /// ] /// ``` -#[elem(Locatable, ShowSet, Show)] +#[elem(Locatable, ShowSet)] pub struct QuoteElem { /// Whether this is a block quote. /// @@ -62,7 +59,7 @@ pub struct QuoteElem { /// Ich bin ein Berliner. /// ] /// ``` - block: bool, + pub block: bool, /// Whether double quotes should be added around this quote. /// @@ -88,7 +85,7 @@ pub struct QuoteElem { /// translate the quote: /// #quote[I am a Berliner.] /// ``` - quotes: Smart<bool>, + pub quotes: Smart<bool>, /// The attribution of this quote, usually the author or source. Can be a /// label pointing to a bibliography entry or any content. By default only @@ -123,17 +120,36 @@ pub struct QuoteElem { /// /// #bibliography("works.bib", style: "apa") /// ``` - attribution: Option<Attribution>, + pub attribution: Option<Attribution>, /// The quote. #[required] - body: Content, + pub body: Content, /// The nesting depth. #[internal] #[fold] #[ghost] - depth: Depth, + pub depth: Depth, +} + +impl QuoteElem { + /// Quotes the body content with the appropriate quotes based on the current + /// styles and surroundings. + pub fn quoted(body: Content, styles: StyleChain<'_>) -> Content { + let quotes = SmartQuotes::get_in(styles); + + // Alternate between single and double quotes. + let Depth(depth) = styles.get(QuoteElem::depth); + let double = depth % 2 == 0; + + Content::sequence([ + TextElem::packed(quotes.open(double)), + body, + TextElem::packed(quotes.close(double)), + ]) + .set(QuoteElem::depth, Depth(1)) + } } /// Attribution for a [quote](QuoteElem). @@ -143,6 +159,23 @@ pub enum Attribution { Label(Label), } +impl Attribution { + /// Realize as an em dash followed by text or a citation. + pub fn realize(&self, span: Span) -> Content { + Content::sequence([ + TextElem::packed('—'), + SpaceElem::shared().clone(), + match self { + Attribution::Content(content) => content.clone(), + Attribution::Label(label) => CiteElem::new(*label) + .with_form(Some(CitationForm::Prose)) + .pack() + .spanned(span), + }, + ]) + } +} + cast! { Attribution, self => match self { @@ -153,96 +186,6 @@ cast! { 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.get(styles); - let html = styles.get(TargetElem::target).is_html(); - - if self.quotes.get(styles).unwrap_or(!block) { - let quotes = SmartQuotes::get( - styles.get_ref(SmartQuoteElem::quotes), - styles.get(TextElem::lang), - styles.get(TextElem::region), - styles.get(SmartQuoteElem::alternative), - ); - - // Alternate between single and double quotes. - let Depth(depth) = styles.get(QuoteElem::depth); - let double = depth % 2 == 0; - - if !html { - // Add zero-width weak spacing to make the quotes "sticky". - let hole = HElem::hole().pack(); - realized = Content::sequence([hole.clone(), realized, hole]); - } - realized = Content::sequence([ - TextElem::packed(quotes.open(double)), - realized, - TextElem::packed(quotes.close(double)), - ]) - .set(QuoteElem::depth, Depth(1)); - } - - let attribution = self.attribution.get_ref(styles); - - if block { - realized = if html { - let mut elem = HtmlElem::new(tag::blockquote).with_body(Some(realized)); - if let Some(Attribution::Content(attribution)) = attribution { - if let Some(link) = attribution.to_packed::<LinkElem>() { - if let LinkTarget::Dest(Destination::Url(url)) = &link.dest { - elem = elem.with_attr(attr::cite, url.clone().into_inner()); - } - } - } - elem.pack() - } else { - BlockElem::new().with_body(Some(BlockBody::Content(realized))).pack() - } - .spanned(self.span()); - - if let Some(attribution) = attribution { - let attribution = match attribution { - Attribution::Content(content) => content.clone(), - Attribution::Label(label) => CiteElem::new(*label) - .with_form(Some(CitationForm::Prose)) - .pack() - .spanned(self.span()), - }; - let attribution = Content::sequence([ - TextElem::packed('—'), - SpaceElem::shared().clone(), - attribution, - ]); - - if html { - realized += attribution; - } else { - // Bring the attribution a bit closer to the quote. - let gap = Spacing::Rel(Em::new(0.9).into()); - let v = VElem::new(gap).with_weak(true).pack(); - realized += v; - realized += BlockElem::new() - .with_body(Some(BlockBody::Content(attribution))) - .pack() - .aligned(Alignment::END); - } - } - - if !html { - realized = PadElem::new(realized).pack(); - } - } else if let Some(Attribution::Label(label)) = attribution { - 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(); diff --git a/crates/typst-library/src/model/reference.rs b/crates/typst-library/src/model/reference.rs index 2d04a97a..4877409f 100644 --- a/crates/typst-library/src/model/reference.rs +++ b/crates/typst-library/src/model/reference.rs @@ -5,7 +5,7 @@ use crate::diag::{bail, At, Hint, SourceResult}; use crate::engine::Engine; use crate::foundations::{ cast, elem, Cast, Content, Context, Func, IntoValue, Label, NativeElement, Packed, - Repr, Show, Smart, StyleChain, Synthesize, + Repr, Smart, StyleChain, Synthesize, }; use crate::introspection::{Counter, CounterKey, Locatable}; use crate::math::EquationElem; @@ -134,7 +134,7 @@ use crate::text::TextElem; /// In @beginning we prove @pythagoras. /// $ a^2 + b^2 = c^2 $ <pythagoras> /// ``` -#[elem(title = "Reference", Synthesize, Locatable, Show)] +#[elem(title = "Reference", Synthesize, Locatable)] pub struct RefElem { /// The target label that should be referenced. /// @@ -220,9 +220,13 @@ impl Synthesize for Packed<RefElem> { } } -impl Show for Packed<RefElem> { - #[typst_macros::time(name = "ref", span = self.span())] - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { +impl Packed<RefElem> { + /// Realize as a linked, textual reference. + pub fn realize( + &self, + engine: &mut Engine, + styles: StyleChain, + ) -> SourceResult<Content> { let elem = engine.introspector.query_label(self.target); let span = self.span(); @@ -242,7 +246,7 @@ impl Show for Packed<RefElem> { .at(span)?; let supplement = engine.introspector.page_supplement(loc); - return show_reference( + return realize_reference( self, engine, styles, @@ -306,7 +310,7 @@ impl Show for Packed<RefElem> { )) .at(span)?; - show_reference( + realize_reference( self, engine, styles, @@ -319,7 +323,7 @@ impl Show for Packed<RefElem> { } /// Show a reference. -fn show_reference( +fn realize_reference( reference: &Packed<RefElem>, engine: &mut Engine, styles: StyleChain, diff --git a/crates/typst-library/src/model/strong.rs b/crates/typst-library/src/model/strong.rs index 08cf4839..7751c95b 100644 --- a/crates/typst-library/src/model/strong.rs +++ b/crates/typst-library/src/model/strong.rs @@ -1,10 +1,4 @@ -use crate::diag::SourceResult; -use crate::engine::Engine; -use crate::foundations::{ - elem, Content, NativeElement, Packed, Show, StyleChain, TargetElem, -}; -use crate::html::{tag, HtmlElem}; -use crate::text::{TextElem, WeightDelta}; +use crate::foundations::{elem, Content}; /// Strongly emphasizes content by increasing the font weight. /// @@ -24,7 +18,7 @@ use crate::text::{TextElem, WeightDelta}; /// simply enclose it in stars/asterisks (`*`). Note that this only works at /// word boundaries. To strongly emphasize part of a word, you have to use the /// function. -#[elem(title = "Strong Emphasis", keywords = ["bold", "weight"], Show)] +#[elem(title = "Strong Emphasis", keywords = ["bold", "weight"])] pub struct StrongElem { /// The delta to apply on the font weight. /// @@ -39,18 +33,3 @@ pub struct StrongElem { #[required] pub body: Content, } - -impl Show for Packed<StrongElem> { - #[typst_macros::time(name = "strong", span = self.span())] - fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let body = self.body.clone(); - Ok(if styles.get(TargetElem::target).is_html() { - HtmlElem::new(tag::strong) - .with_body(Some(body)) - .pack() - .spanned(self.span()) - } else { - body.set(TextElem::delta, WeightDelta(self.delta.get(styles))) - }) - } -} diff --git a/crates/typst-library/src/model/table.rs b/crates/typst-library/src/model/table.rs index 72c5acc5..e46efc81 100644 --- a/crates/typst-library/src/model/table.rs +++ b/crates/typst-library/src/model/table.rs @@ -3,19 +3,11 @@ use std::sync::Arc; use typst_utils::NonZeroExt; -use crate::diag::{bail, HintedStrResult, HintedString, SourceResult}; -use crate::engine::Engine; -use crate::foundations::{ - cast, elem, scope, Content, NativeElement, Packed, Show, Smart, StyleChain, - TargetElem, -}; -use crate::html::{attr, tag, HtmlAttrs, HtmlElem, HtmlTag}; -use crate::introspection::Locator; -use crate::layout::grid::resolve::{table_to_cellgrid, Cell, CellGrid, Entry}; +use crate::diag::{bail, HintedStrResult, HintedString}; +use crate::foundations::{cast, elem, scope, Content, Packed, Smart}; use crate::layout::{ - show_grid_cell, Abs, Alignment, BlockElem, Celled, GridCell, GridFooter, GridHLine, - GridHeader, GridVLine, Length, OuterHAlignment, OuterVAlignment, Rel, Sides, - TrackSizings, + Abs, Alignment, Celled, GridCell, GridFooter, GridHLine, GridHeader, GridVLine, + Length, OuterHAlignment, OuterVAlignment, Rel, Sides, TrackSizings, }; use crate::model::Figurable; use crate::text::LocalName; @@ -121,7 +113,7 @@ use crate::visualize::{Paint, Stroke}; /// [Robert], b, a, b, /// ) /// ``` -#[elem(scope, Show, LocalName, Figurable)] +#[elem(scope, LocalName, Figurable)] pub struct TableElem { /// The column sizes. See the [grid documentation]($grid) for more /// information on track sizing. @@ -255,113 +247,6 @@ impl TableElem { type TableFooter; } -fn show_cell_html(tag: HtmlTag, cell: &Cell, styles: StyleChain) -> Content { - let cell = cell.body.clone(); - let Some(cell) = cell.to_packed::<TableCell>() else { return cell }; - let mut attrs = HtmlAttrs::default(); - let span = |n: NonZeroUsize| (n != NonZeroUsize::MIN).then(|| n.to_string()); - if let Some(colspan) = span(cell.colspan.get(styles)) { - attrs.push(attr::colspan, colspan); - } - if let Some(rowspan) = span(cell.rowspan.get(styles)) { - attrs.push(attr::rowspan, rowspan); - } - HtmlElem::new(tag) - .with_body(Some(cell.body.clone())) - .with_attrs(attrs) - .pack() - .spanned(cell.span()) -} - -fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content { - let elem = |tag, body| HtmlElem::new(tag).with_body(Some(body)).pack(); - let mut rows: Vec<_> = grid.entries.chunks(grid.non_gutter_column_count()).collect(); - - let tr = |tag, row: &[Entry]| { - let row = row - .iter() - .flat_map(|entry| entry.as_cell()) - .map(|cell| show_cell_html(tag, cell, styles)); - elem(tag::tr, Content::sequence(row)) - }; - - // TODO(subfooters): similarly to headers, take consecutive footers from - // the end for 'tfoot'. - let footer = grid.footer.map(|ft| { - let rows = rows.drain(ft.start..); - elem(tag::tfoot, Content::sequence(rows.map(|row| tr(tag::td, row)))) - }); - - // Store all consecutive headers at the start in 'thead'. All remaining - // headers are just 'th' rows across the table body. - let mut consecutive_header_end = 0; - let first_mid_table_header = grid - .headers - .iter() - .take_while(|hd| { - let is_consecutive = hd.range.start == consecutive_header_end; - consecutive_header_end = hd.range.end; - - is_consecutive - }) - .count(); - - let (y_offset, header) = if first_mid_table_header > 0 { - let removed_header_rows = - grid.headers.get(first_mid_table_header - 1).unwrap().range.end; - let rows = rows.drain(..removed_header_rows); - - ( - removed_header_rows, - Some(elem(tag::thead, Content::sequence(rows.map(|row| tr(tag::th, row))))), - ) - } else { - (0, None) - }; - - // TODO: Consider improving accessibility properties of multi-level headers - // inside tables in the future, e.g. indicating which columns they are - // relative to and so on. See also: - // https://www.w3.org/WAI/tutorials/tables/multi-level/ - let mut next_header = first_mid_table_header; - let mut body = - Content::sequence(rows.into_iter().enumerate().map(|(relative_y, row)| { - let y = relative_y + y_offset; - if let Some(current_header) = - grid.headers.get(next_header).filter(|h| h.range.contains(&y)) - { - if y + 1 == current_header.range.end { - next_header += 1; - } - - tr(tag::th, row) - } else { - tr(tag::td, row) - } - })); - - if header.is_some() || footer.is_some() { - body = elem(tag::tbody, body); - } - - let content = header.into_iter().chain(core::iter::once(body)).chain(footer); - elem(tag::table, Content::sequence(content)) -} - -impl Show for Packed<TableElem> { - fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - Ok(if styles.get(TargetElem::target).is_html() { - // TODO: This is a hack, it is not clear whether the locator is actually used by HTML. - // How can we find out whether locator is actually used? - let locator = Locator::root(); - show_cellgrid_html(table_to_cellgrid(self, engine, locator, styles)?, styles) - } else { - BlockElem::multi_layouter(self.clone(), engine.routines.layout_table).pack() - } - .spanned(self.span())) - } -} - impl LocalName for Packed<TableElem> { const KEY: &'static str = "table"; } @@ -761,7 +646,7 @@ pub struct TableVLine { /// [Vikram], [49], [Perseverance], /// ) /// ``` -#[elem(name = "cell", title = "Table Cell", Show)] +#[elem(name = "cell", title = "Table Cell")] pub struct TableCell { /// The cell's body. #[required] @@ -808,12 +693,6 @@ cast! { v: Content => v.into(), } -impl Show for Packed<TableCell> { - fn show(&self, _engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - show_grid_cell(self.body.clone(), self.inset.get(styles), self.align.get(styles)) - } -} - impl Default for Packed<TableCell> { fn default() -> Self { Packed::new( diff --git a/crates/typst-library/src/model/terms.rs b/crates/typst-library/src/model/terms.rs index 280c2d67..71b1bad6 100644 --- a/crates/typst-library/src/model/terms.rs +++ b/crates/typst-library/src/model/terms.rs @@ -1,15 +1,9 @@ -use typst_utils::{Get, Numeric}; - -use crate::diag::{bail, SourceResult}; -use crate::engine::Engine; +use crate::diag::bail; use crate::foundations::{ - cast, elem, scope, Array, Content, NativeElement, Packed, Show, Smart, StyleChain, - Styles, TargetElem, + cast, elem, scope, Array, Content, NativeElement, Packed, Smart, Styles, }; -use crate::html::{tag, HtmlElem}; -use crate::layout::{Em, HElem, Length, Sides, StackChild, StackElem, VElem}; -use crate::model::{ListItemLike, ListLike, ParElem, ParbreakElem}; -use crate::text::TextElem; +use crate::layout::{Em, HElem, Length}; +use crate::model::{ListItemLike, ListLike}; /// A list of terms and their descriptions. /// @@ -27,7 +21,7 @@ use crate::text::TextElem; /// # Syntax /// This function also has dedicated syntax: Starting a line with a slash, /// followed by a term, a colon and a description creates a term list item. -#[elem(scope, title = "Term List", Show)] +#[elem(scope, title = "Term List")] pub struct TermsElem { /// Defines the default [spacing]($terms.spacing) of the term list. If it is /// `{false}`, the items are spaced apart with @@ -117,94 +111,6 @@ impl TermsElem { type TermItem; } -impl Show for Packed<TermsElem> { - fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> { - let span = self.span(); - let tight = self.tight.get(styles); - - if styles.get(TargetElem::target).is_html() { - return Ok(HtmlElem::new(tag::dl) - .with_body(Some(Content::sequence(self.children.iter().flat_map( - |item| { - // Text in wide term lists shall always turn into paragraphs. - let mut description = item.description.clone(); - if !tight { - description += ParbreakElem::shared(); - } - - [ - HtmlElem::new(tag::dt) - .with_body(Some(item.term.clone())) - .pack() - .spanned(item.term.span()), - HtmlElem::new(tag::dd) - .with_body(Some(description)) - .pack() - .spanned(item.description.span()), - ] - }, - )))) - .pack()); - } - - let separator = self.separator.get_ref(styles); - let indent = self.indent.get(styles); - let hanging_indent = self.hanging_indent.get(styles); - let gutter = self.spacing.get(styles).unwrap_or_else(|| { - if tight { - styles.get(ParElem::leading) - } else { - styles.get(ParElem::spacing) - } - }); - - let pad = hanging_indent + indent; - let unpad = (!hanging_indent.is_zero()) - .then(|| HElem::new((-hanging_indent).into()).pack().spanned(span)); - - let mut children = vec![]; - for child in self.children.iter() { - let mut seq = vec![]; - seq.extend(unpad.clone()); - seq.push(child.term.clone().strong()); - seq.push((*separator).clone()); - seq.push(child.description.clone()); - - // Text in wide term lists shall always turn into paragraphs. - if !tight { - seq.push(ParbreakElem::shared().clone()); - } - - children.push(StackChild::Block(Content::sequence(seq))); - } - - let padding = - Sides::default().with(styles.resolve(TextElem::dir).start(), pad.into()); - - let mut realized = StackElem::new(children) - .with_spacing(Some(gutter.into())) - .pack() - .spanned(span) - .padded(padding) - .set(TermsElem::within, true); - - if tight { - let spacing = self - .spacing - .get(styles) - .unwrap_or_else(|| styles.get(ParElem::leading)); - let v = VElem::new(spacing.into()) - .with_weak(true) - .with_attach(true) - .pack() - .spanned(span); - realized = v + realized; - } - - Ok(realized) - } -} - /// A term list item. #[elem(name = "item", title = "Term List Item")] pub struct TermItem { |
