summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/model
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2025-07-09 10:16:36 +0200
committerGitHub <noreply@github.com>2025-07-09 08:16:36 +0000
commite5e1dcd9c01341d2cd3473ac94a70223d5966086 (patch)
treed648efad9463cb10270d55ba35210eeb1e91ee22 /crates/typst-library/src/model
parent0a3c6939dd274f40672484695d909c2cc0d0d755 (diff)
Target-specific native show rules (#6569)
Diffstat (limited to 'crates/typst-library/src/model')
-rw-r--r--crates/typst-library/src/model/bibliography.rs92
-rw-r--r--crates/typst-library/src/model/cite.rs10
-rw-r--r--crates/typst-library/src/model/emph.rs25
-rw-r--r--crates/typst-library/src/model/enum.rs63
-rw-r--r--crates/typst-library/src/model/figure.rs140
-rw-r--r--crates/typst-library/src/model/footnote.rs64
-rw-r--r--crates/typst-library/src/model/heading.rs109
-rw-r--r--crates/typst-library/src/model/link.rs41
-rw-r--r--crates/typst-library/src/model/list.rs49
-rw-r--r--crates/typst-library/src/model/mod.rs20
-rw-r--r--crates/typst-library/src/model/outline.rs86
-rw-r--r--crates/typst-library/src/model/quote.rs155
-rw-r--r--crates/typst-library/src/model/reference.rs20
-rw-r--r--crates/typst-library/src/model/strong.rs25
-rw-r--r--crates/typst-library/src/model/table.rs133
-rw-r--r--crates/typst-library/src/model/terms.rs104
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 {