diff --git a/crates/typst-library/src/model/figure.rs b/crates/typst-library/src/model/figure.rs index 5a137edbd..1d3d75fe8 100644 --- a/crates/typst-library/src/model/figure.rs +++ b/crates/typst-library/src/model/figure.rs @@ -7,6 +7,7 @@ use typst_utils::NonZeroExt; use crate::diag::{bail, SourceResult}; use crate::engine::Engine; +use crate::foundations::func; use crate::foundations::{ cast, elem, scope, select_where, Content, Element, NativeElement, Packed, Selector, Show, ShowSet, Smart, StyleChain, Styles, Synthesize, TargetElem, @@ -238,7 +239,7 @@ pub struct FigureElem { /// These are the counters you'll need to modify if you want to skip a /// number or reset the counter. #[synthesized] - pub counter: Option, + pub counter: Counter, } #[scope] @@ -312,15 +313,21 @@ impl Synthesize for Packed { if let Some(caption) = &mut caption { caption.synthesize(engine, styles)?; caption.push_kind(kind.clone()); - caption.push_supplement(supplement.clone()); - caption.push_numbering(numbering.clone()); - caption.push_counter(Some(counter.clone())); - caption.push_figure_location(location); + if let Some(supplement) = supplement.clone() { + caption.push_supplement(supplement); + } + if let Some(numbering) = numbering.clone() { + caption.push_numbering(numbering); + } + caption.push_counter(counter.clone()); + if let Some(location) = location { + caption.push_figure_location(location); + } } elem.push_kind(Smart::Custom(kind)); elem.push_supplement(Smart::Custom(supplement.map(Supplement::Content))); - elem.push_counter(Some(counter)); + elem.push_counter(counter); elem.push_caption(caption); Ok(()) @@ -420,7 +427,6 @@ impl Refable for Packed { (**self) .counter() .cloned() - .flatten() .unwrap_or_else(|| Counter::of(FigureElem::elem())) } @@ -470,7 +476,7 @@ impl Outlinable for Packed { /// caption: [A rectangle], /// ) /// ``` -#[elem(name = "caption", Synthesize, Show)] +#[elem(scope, name = "caption", Synthesize, Show)] pub struct FigureCaption { /// The caption's position in the figure. Either `{top}` or `{bottom}`. /// @@ -541,20 +547,72 @@ pub struct FigureCaption { /// The figure's supplement. #[synthesized] - pub supplement: Option, + pub supplement: Content, /// How to number the figure. #[synthesized] - pub numbering: Option, + pub numbering: Numbering, /// The counter for the figure. #[synthesized] - pub counter: Option, + pub counter: Counter, /// The figure's location. #[internal] #[synthesized] - pub figure_location: Option, + pub figure_location: Location, +} + +#[scope] +impl FigureCaption { + /// The kind of the corresponding figure. + /// + /// This returns `{none}` in case the corresponding figure does not appear + /// in the document. + #[func] + pub fn kind_(self) -> Option { + self.kind + } + + /// The supplement of the corresponding figure. + /// + /// This returns `{none}` in case the corresponding figure does not appear + /// in the document. + #[func] + pub fn supplement_(self) -> Option { + self.supplement + } + + /// The numbering of the corresponding figure. + /// + /// This returns `{none}` in case the corresponding figure does not appear + /// in the document. + #[func] + pub fn numbering_(self) -> Option { + self.numbering + } + + /// The counter for the corresponding figure: + /// `{counter(figure.where(kind: kind))}` for a figure of kind `{kind}`. + /// + /// For example, for a table figure, this returns + /// `{counter(figure.where(kind: table))}`. + /// + /// This returns `{none}` in case the corresponding figure does not appear + /// in the document, or it is not numbered. + #[func] + pub fn counter_(self) -> Option { + self.counter + } + + /// The location of the corresponding figure. + /// + /// This returns `{none}` in case the corresponding figure does not appear + /// in the document. + #[func] + pub fn figure_location_(self) -> Option { + self.figure_location + } } impl FigureCaption { @@ -592,12 +650,7 @@ impl Show for Packed { fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult { let mut realized = self.body.clone(); - if let ( - Some(Some(mut supplement)), - Some(Some(numbering)), - Some(Some(counter)), - Some(Some(location)), - ) = ( + if let (Some(mut supplement), Some(numbering), Some(counter), Some(location)) = ( self.supplement().cloned(), self.numbering(), self.counter(), diff --git a/crates/typst-library/src/model/reference.rs b/crates/typst-library/src/model/reference.rs index 7d44cccc0..8d7b4510e 100644 --- a/crates/typst-library/src/model/reference.rs +++ b/crates/typst-library/src/model/reference.rs @@ -1,8 +1,9 @@ use comemo::Track; use ecow::eco_format; -use crate::diag::{bail, At, Hint, SourceResult}; +use crate::diag::{bail, At, Hint, SourceResult, StrResult}; use crate::engine::Engine; +use crate::foundations::func; use crate::foundations::{ cast, elem, Cast, Content, Context, Func, IntoValue, Label, NativeElement, Packed, Show, Smart, StyleChain, Synthesize, @@ -13,6 +14,7 @@ use crate::model::{ BibliographyElem, CiteElem, Destination, Figurable, FootnoteElem, Numbering, }; use crate::text::TextElem; +use typst_macros::scope; /// A reference to a label or bibliography. /// @@ -107,7 +109,7 @@ use crate::text::TextElem; /// In @beginning we prove @pythagoras. /// $ a^2 + b^2 = c^2 $ /// ``` -#[elem(title = "Reference", Synthesize, Locatable, Show)] +#[elem(scope, title = "Reference", Locatable, Show)] pub struct RefElem { /// The target label that should be referenced. /// @@ -161,37 +163,6 @@ pub struct RefElem { /// ``` #[default(RefForm::Normal)] pub form: RefForm, - - /// A synthesized citation. - #[synthesized] - pub citation: Option>, - - /// The referenced element. - #[synthesized] - pub element: Option, -} - -impl Synthesize for Packed { - fn synthesize( - &mut self, - engine: &mut Engine, - styles: StyleChain, - ) -> SourceResult<()> { - let citation = to_citation(self, engine, styles)?; - - let elem = self.as_mut(); - elem.push_citation(Some(citation)); - elem.push_element(None); - - if !BibliographyElem::has(engine, elem.target) { - if let Ok(found) = engine.introspector.query_label(elem.target).cloned() { - elem.push_element(Some(found)); - return Ok(()); - } - } - - Ok(()) - } } impl Show for Packed { @@ -334,6 +305,25 @@ fn to_citation( Ok(elem) } +#[scope] +impl RefElem { + /// The referenced element. + /// + /// If this is a reference to a bibliography entry, this returns `{none}`. + #[func] + pub fn element(self, engine: &mut Engine) -> StrResult> { + if BibliographyElem::has(engine, self.target) { + return Ok(None); + } + engine.introspector.query_label(self.target).cloned().map(Some) + } +} + +cast! { + RefElem, + v: Content => v.unpack::().map_err(|_| "expected reference")? +} + /// Additional content for a reference. #[derive(Debug, Clone, PartialEq, Hash)] pub enum Supplement { diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs index d5c07424d..977823412 100644 --- a/crates/typst-library/src/text/raw.rs +++ b/crates/typst-library/src/text/raw.rs @@ -6,6 +6,7 @@ use comemo::Tracked; use ecow::{eco_format, EcoString, EcoVec}; use syntect::highlighting as synt; use syntect::parsing::{SyntaxDefinition, SyntaxSet, SyntaxSetBuilder}; +use typst_macros::func; use typst_syntax::{split_newlines, LinkedNode, Span, Spanned}; use typst_utils::ManuallyHash; use unicode_segmentation::UnicodeSegmentation; @@ -14,8 +15,8 @@ use super::Lang; use crate::diag::{At, FileError, SourceResult, StrResult}; use crate::engine::Engine; use crate::foundations::{ - cast, elem, scope, Bytes, Content, Derived, NativeElement, OneOrMultiple, Packed, - PlainText, Show, ShowSet, Smart, StyleChain, Styles, Synthesize, TargetElem, + cast, elem, scope, Bytes, Content, Context, Derived, NativeElement, OneOrMultiple, + Packed, PlainText, Show, ShowSet, Smart, StyleChain, Styles, TargetElem, }; use crate::html::{tag, HtmlElem}; use crate::layout::{BlockBody, BlockElem, Em, HAlignment}; @@ -72,16 +73,7 @@ use crate::World; /// needed, start the text with a single space (which will be trimmed) or use /// the single backtick syntax. If your text should start or end with a /// backtick, put a space before or after it (it will be trimmed). -#[elem( - scope, - title = "Raw Text / Code", - Synthesize, - Show, - ShowSet, - LocalName, - Figurable, - PlainText -)] +#[elem(scope, title = "Raw Text / Code", Show, ShowSet, LocalName, Figurable, PlainText)] pub struct RawElem { /// The raw text. /// @@ -265,19 +257,27 @@ pub struct RawElem { /// ```` #[default(2)] pub tab_size: usize, - - /// The stylized lines of raw text. - /// - /// Made accessible for the [`raw.line` element]($raw.line). - /// Allows more styling control in `show` rules. - #[synthesized] - pub lines: Vec>, } #[scope] impl RawElem { #[elem] type RawLine; + + /// The stylized lines of raw text. + #[func(contextual)] + pub fn lines( + self, + context: Tracked, + span: Span, + ) -> SourceResult>> { + Ok(self.highlight(context.styles().at(span)?, span)) + } +} + +cast! { + RawElem, + v: Content => v.unpack::().map_err(|_| "expected raw text / code")? } impl RawElem { @@ -299,24 +299,13 @@ impl RawElem { ]) .collect() } -} -impl Synthesize for Packed { - fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> { - let seq = self.highlight(styles); - self.push_lines(seq); - Ok(()) - } -} - -impl Packed { #[comemo::memoize] - fn highlight(&self, styles: StyleChain) -> Vec> { - let elem = self.as_ref(); - let lines = preprocess(&elem.text, styles, self.span()); + fn highlight(&self, styles: StyleChain, span: Span) -> Vec> { + let lines = preprocess(&self.text, styles, span); let count = lines.len() as i64; - let lang = elem + let lang = self .lang(styles) .as_ref() .as_ref() @@ -335,8 +324,8 @@ impl Packed { }) }; - let syntaxes = LazyCell::new(|| elem.syntaxes(styles)); - let theme: &synt::Theme = match elem.theme(styles) { + let syntaxes = LazyCell::new(|| self.syntaxes(styles)); + let theme: &synt::Theme = match self.theme(styles) { Smart::Auto => &RAW_THEME, Smart::Custom(Some(theme)) => theme.derived.get(), Smart::Custom(None) => return non_highlighted_result(lines).collect(), @@ -432,7 +421,7 @@ impl Packed { impl Show for Packed { #[typst_macros::time(name = "raw", span = self.span())] fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult { - let lines = self.lines().map(|v| v.as_slice()).unwrap_or_default(); + let lines = self.as_ref().highlight(styles, self.span()); let mut seq = EcoVec::with_capacity((2 * lines.len()).saturating_sub(1)); for (i, line) in lines.iter().enumerate() {