mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Fix show-set semantics (#3311)
This commit is contained in:
parent
426445edfc
commit
7d33436e55
@ -866,6 +866,28 @@ fn create_fields_impl(element: &Elem) -> TokenStream {
|
||||
quote! { Fields::#enum_ident => #expr }
|
||||
});
|
||||
|
||||
// Sets fields from the style chain.
|
||||
let materializes = visible_non_ghost()
|
||||
.filter(|field| !field.required && !field.synthesized)
|
||||
.map(|field| {
|
||||
let Field { ident, .. } = field;
|
||||
let value = create_style_chain_access(
|
||||
field,
|
||||
false,
|
||||
if field.ghost { quote!(None) } else { quote!(self.#ident.as_ref()) },
|
||||
);
|
||||
|
||||
if field.fold {
|
||||
quote! { self.#ident = Some(#value); }
|
||||
} else {
|
||||
quote! {
|
||||
if self.#ident.is_none() {
|
||||
self.#ident = Some(#value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Creation of the `fields` dictionary for inherent fields.
|
||||
let field_inserts = visible_non_ghost().map(|field| {
|
||||
let Field { ident, name, .. } = field;
|
||||
@ -917,6 +939,10 @@ fn create_fields_impl(element: &Elem) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn materialize(&mut self, styles: #foundations::StyleChain) {
|
||||
#(#materializes)*
|
||||
}
|
||||
|
||||
fn fields(&self) -> #foundations::Dict {
|
||||
let mut fields = #foundations::Dict::new();
|
||||
#(#field_inserts)*
|
||||
|
@ -14,11 +14,10 @@ use smallvec::smallvec;
|
||||
use crate::diag::{SourceResult, StrResult};
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
elem, func, scope, ty, Dict, Element, Fields, Finalize, Guard, IntoValue, Label,
|
||||
NativeElement, Recipe, Repr, Selector, Str, Style, StyleChain, Styles, Synthesize,
|
||||
Value,
|
||||
elem, func, scope, ty, Dict, Element, Fields, Guard, IntoValue, Label, NativeElement,
|
||||
Recipe, Repr, Selector, Str, Style, StyleChain, Styles, Value,
|
||||
};
|
||||
use crate::introspection::{Locatable, Location, Meta, MetaElem};
|
||||
use crate::introspection::{Location, Meta, MetaElem};
|
||||
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
|
||||
use crate::model::{Destination, EmphElem, StrongElem};
|
||||
use crate::syntax::Span;
|
||||
@ -142,6 +141,16 @@ impl Content {
|
||||
self
|
||||
}
|
||||
|
||||
/// Check whether a show rule recipe is disabled.
|
||||
pub fn is_guarded(&self, guard: Guard) -> bool {
|
||||
self.inner.lifecycle.contains(guard.0)
|
||||
}
|
||||
|
||||
/// Whether this content has already been prepared.
|
||||
pub fn is_prepared(&self) -> bool {
|
||||
self.inner.lifecycle.contains(0)
|
||||
}
|
||||
|
||||
/// Set the location of the content.
|
||||
pub fn set_location(&mut self, location: Location) {
|
||||
self.make_mut().location = Some(location);
|
||||
@ -153,25 +162,6 @@ impl Content {
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether the content needs to be realized specially.
|
||||
pub fn needs_preparation(&self) -> bool {
|
||||
!self.is_prepared()
|
||||
&& (self.can::<dyn Locatable>()
|
||||
|| self.can::<dyn Synthesize>()
|
||||
|| self.can::<dyn Finalize>()
|
||||
|| self.label().is_some())
|
||||
}
|
||||
|
||||
/// Check whether a show rule recipe is disabled.
|
||||
pub fn is_guarded(&self, guard: Guard) -> bool {
|
||||
self.inner.lifecycle.contains(guard.0)
|
||||
}
|
||||
|
||||
/// Whether this content has already been prepared.
|
||||
pub fn is_prepared(&self) -> bool {
|
||||
self.inner.lifecycle.contains(0)
|
||||
}
|
||||
|
||||
/// Mark this content as prepared.
|
||||
pub fn mark_prepared(&mut self) {
|
||||
self.make_mut().lifecycle.insert(0);
|
||||
@ -227,6 +217,11 @@ impl Content {
|
||||
self.get_by_name(name).ok_or_else(|| missing_field(name))
|
||||
}
|
||||
|
||||
/// Resolve all fields with the styles and save them in-place.
|
||||
pub fn materialize(&mut self, styles: StyleChain) {
|
||||
self.make_mut().elem.materialize(styles);
|
||||
}
|
||||
|
||||
/// Create a new sequence element from multiples elements.
|
||||
pub fn sequence(iter: impl IntoIterator<Item = Self>) -> Self {
|
||||
let mut iter = iter.into_iter();
|
||||
|
@ -223,6 +223,9 @@ pub trait Fields {
|
||||
/// Get the field with the given ID in the presence of styles.
|
||||
fn field_with_styles(&self, id: u8, styles: StyleChain) -> Option<Value>;
|
||||
|
||||
/// Resolve all fields with the styles and save them in-place.
|
||||
fn materialize(&mut self, styles: StyleChain);
|
||||
|
||||
/// Get the fields of the element.
|
||||
fn fields(&self) -> Dict;
|
||||
}
|
||||
@ -282,17 +285,20 @@ pub trait Synthesize {
|
||||
-> SourceResult<()>;
|
||||
}
|
||||
|
||||
/// The base recipe for an element.
|
||||
/// Defines a built-in show rule for an element.
|
||||
pub trait Show {
|
||||
/// Execute the base recipe for this element.
|
||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content>;
|
||||
}
|
||||
|
||||
/// Post-process an element after it was realized.
|
||||
pub trait Finalize {
|
||||
/// Defines built-in show set rules for an element.
|
||||
///
|
||||
/// This is a bit more powerful than a user-defined show-set because it can
|
||||
/// access the element's fields.
|
||||
pub trait ShowSet {
|
||||
/// Finalize the fully realized form of the element. Use this for effects
|
||||
/// that should work even in the face of a user-defined show rule.
|
||||
fn finalize(&self, realized: Content, styles: StyleChain) -> Content;
|
||||
fn show_set(&self, styles: StyleChain) -> Styles;
|
||||
}
|
||||
|
||||
/// How the element interacts with other elements.
|
||||
|
@ -147,9 +147,15 @@ impl Styles {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Prehashed<Style>> for Styles {
|
||||
fn from(style: Prehashed<Style>) -> Self {
|
||||
Self(eco_vec![style])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Style> for Styles {
|
||||
fn from(entry: Style) -> Self {
|
||||
Self(eco_vec![Prehashed::new(entry)])
|
||||
fn from(style: Style) -> Self {
|
||||
Self(eco_vec![Prehashed::new(style)])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use unicode_math_class::MathClass;
|
||||
use crate::diag::{bail, SourceResult};
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
elem, Content, Finalize, NativeElement, Packed, Resolve, Smart, StyleChain,
|
||||
elem, Content, NativeElement, Packed, Resolve, ShowSet, Smart, StyleChain, Styles,
|
||||
Synthesize,
|
||||
};
|
||||
use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
|
||||
@ -49,7 +49,7 @@ use crate::World;
|
||||
#[elem(
|
||||
Locatable,
|
||||
Synthesize,
|
||||
Finalize,
|
||||
ShowSet,
|
||||
LayoutSingle,
|
||||
LayoutMath,
|
||||
Count,
|
||||
@ -145,27 +145,23 @@ impl Synthesize for Packed<EquationElem> {
|
||||
}
|
||||
};
|
||||
|
||||
let elem = self.as_mut();
|
||||
elem.push_block(elem.block(styles));
|
||||
elem.push_numbering(elem.numbering(styles));
|
||||
elem.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
|
||||
|
||||
self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Packed<EquationElem> {
|
||||
fn finalize(&self, realized: Content, style: StyleChain) -> Content {
|
||||
let mut realized = realized;
|
||||
if self.block(style) {
|
||||
realized = realized.styled(AlignElem::set_alignment(Alignment::CENTER));
|
||||
realized = realized.styled(EquationElem::set_size(MathSize::Display));
|
||||
impl ShowSet for Packed<EquationElem> {
|
||||
fn show_set(&self, styles: StyleChain) -> Styles {
|
||||
let mut out = Styles::new();
|
||||
if self.block(styles) {
|
||||
out.set(AlignElem::set_alignment(Alignment::CENTER));
|
||||
out.set(EquationElem::set_size(MathSize::Display));
|
||||
}
|
||||
realized
|
||||
.styled(TextElem::set_weight(FontWeight::from_number(450)))
|
||||
.styled(TextElem::set_font(FontList(vec![FontFamily::new(
|
||||
"New Computer Modern Math",
|
||||
)])))
|
||||
out.set(TextElem::set_weight(FontWeight::from_number(450)));
|
||||
out.set(TextElem::set_font(FontList(vec![FontFamily::new(
|
||||
"New Computer Modern Math",
|
||||
)])));
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,9 @@ use crate::diag::{bail, error, At, FileError, SourceResult, StrResult};
|
||||
use crate::engine::Engine;
|
||||
use crate::eval::{eval_string, EvalMode};
|
||||
use crate::foundations::{
|
||||
cast, elem, ty, Args, Array, Bytes, CastInfo, Content, Finalize, FromValue,
|
||||
IntoValue, Label, NativeElement, Packed, Reflect, Repr, Scope, Show, Smart, Str,
|
||||
StyleChain, Synthesize, Type, Value,
|
||||
cast, elem, ty, Args, Array, Bytes, CastInfo, Content, FromValue, IntoValue, Label,
|
||||
NativeElement, Packed, Reflect, Repr, Scope, Show, ShowSet, Smart, Str, StyleChain,
|
||||
Styles, Synthesize, Type, Value,
|
||||
};
|
||||
use crate::introspection::{Introspector, Locatable, Location};
|
||||
use crate::layout::{
|
||||
@ -84,7 +84,7 @@ use crate::World;
|
||||
///
|
||||
/// #bibliography("works.bib")
|
||||
/// ```
|
||||
#[elem(Locatable, Synthesize, Show, Finalize, LocalName)]
|
||||
#[elem(Locatable, Synthesize, Show, ShowSet, LocalName)]
|
||||
pub struct BibliographyElem {
|
||||
/// Path(s) to Hayagriva `.yml` and/or BibLaTeX `.bib` files.
|
||||
#[required]
|
||||
@ -199,8 +199,6 @@ impl BibliographyElem {
|
||||
impl Synthesize for Packed<BibliographyElem> {
|
||||
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
|
||||
let elem = self.as_mut();
|
||||
elem.push_full(elem.full(styles));
|
||||
elem.push_style(elem.style(styles));
|
||||
elem.push_lang(TextElem::lang_in(styles));
|
||||
elem.push_region(TextElem::region_in(styles));
|
||||
Ok(())
|
||||
@ -275,12 +273,13 @@ impl Show for Packed<BibliographyElem> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Packed<BibliographyElem> {
|
||||
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
|
||||
impl ShowSet for Packed<BibliographyElem> {
|
||||
fn show_set(&self, _: StyleChain) -> Styles {
|
||||
const INDENT: Em = Em::new(1.0);
|
||||
realized
|
||||
.styled(HeadingElem::set_numbering(None))
|
||||
.styled(PadElem::set_left(INDENT.into()))
|
||||
let mut out = Styles::new();
|
||||
out.set(HeadingElem::set_numbering(None));
|
||||
out.set(PadElem::set_left(INDENT.into()));
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,9 +111,6 @@ pub struct CiteElem {
|
||||
impl Synthesize for Packed<CiteElem> {
|
||||
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
|
||||
let elem = self.as_mut();
|
||||
elem.push_supplement(elem.supplement(styles));
|
||||
elem.push_form(elem.form(styles));
|
||||
elem.push_style(elem.style(styles));
|
||||
elem.push_lang(TextElem::lang_in(styles));
|
||||
elem.push_region(TextElem::region_in(styles));
|
||||
Ok(())
|
||||
|
@ -7,8 +7,8 @@ use ecow::EcoString;
|
||||
use crate::diag::{bail, SourceResult};
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
cast, elem, scope, select_where, Content, Element, Finalize, NativeElement, Packed,
|
||||
Selector, Show, Smart, StyleChain, Synthesize,
|
||||
cast, elem, scope, select_where, Content, Element, NativeElement, Packed, Selector,
|
||||
Show, ShowSet, Smart, StyleChain, Styles, Synthesize,
|
||||
};
|
||||
use crate::introspection::{
|
||||
Count, Counter, CounterKey, CounterUpdate, Locatable, Location,
|
||||
@ -101,7 +101,7 @@ use crate::visualize::ImageElem;
|
||||
/// caption: [I'm up here],
|
||||
/// )
|
||||
/// ```
|
||||
#[elem(scope, Locatable, Synthesize, Count, Show, Finalize, Refable, Outlinable)]
|
||||
#[elem(scope, Locatable, Synthesize, Count, Show, ShowSet, Refable, Outlinable)]
|
||||
pub struct FigureElem {
|
||||
/// The content of the figure. Often, an [image]($image).
|
||||
#[required]
|
||||
@ -165,7 +165,6 @@ pub struct FigureElem {
|
||||
/// supplement: [Atom],
|
||||
/// )
|
||||
/// ```
|
||||
#[default(Smart::Auto)]
|
||||
pub kind: Smart<FigureKind>,
|
||||
|
||||
/// The figure's supplement.
|
||||
@ -230,9 +229,7 @@ impl Synthesize for Packed<FigureElem> {
|
||||
) -> SourceResult<()> {
|
||||
let span = self.span();
|
||||
let location = self.location();
|
||||
|
||||
let elem = self.as_mut();
|
||||
let placement = elem.placement(styles);
|
||||
let numbering = elem.numbering(styles);
|
||||
|
||||
// Determine the figure's kind.
|
||||
@ -295,13 +292,10 @@ impl Synthesize for Packed<FigureElem> {
|
||||
caption.push_figure_location(location);
|
||||
}
|
||||
|
||||
elem.push_placement(placement);
|
||||
elem.push_caption(caption);
|
||||
elem.push_kind(Smart::Custom(kind));
|
||||
elem.push_supplement(Smart::Custom(supplement.map(Supplement::Content)));
|
||||
elem.push_numbering(numbering);
|
||||
elem.push_outlined(elem.outlined(styles));
|
||||
elem.push_counter(Some(counter));
|
||||
elem.push_caption(caption);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -342,10 +336,11 @@ impl Show for Packed<FigureElem> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Packed<FigureElem> {
|
||||
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
|
||||
// Allow breakable figures with `show figure: set block(breakable: true)`.
|
||||
realized.styled(BlockElem::set_breakable(false))
|
||||
impl ShowSet for Packed<FigureElem> {
|
||||
fn show_set(&self, _: StyleChain) -> Styles {
|
||||
// Still allows breakable figures with
|
||||
// `show figure: set block(breakable: true)`.
|
||||
BlockElem::set_breakable(false).wrap().into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,7 +429,7 @@ impl Outlinable for Packed<FigureElem> {
|
||||
/// caption: [A rectangle],
|
||||
/// )
|
||||
/// ```
|
||||
#[elem(name = "caption", Synthesize, Show)]
|
||||
#[elem(name = "caption", Show)]
|
||||
pub struct FigureCaption {
|
||||
/// The caption's position in the figure. Either `{top}` or `{bottom}`.
|
||||
///
|
||||
@ -551,15 +546,6 @@ impl FigureCaption {
|
||||
}
|
||||
}
|
||||
|
||||
impl Synthesize for Packed<FigureCaption> {
|
||||
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
|
||||
let elem = self.as_mut();
|
||||
elem.push_position(elem.position(styles));
|
||||
elem.push_separator(Smart::Custom(elem.get_separator(styles)));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Show for Packed<FigureCaption> {
|
||||
#[typst_macros::time(name = "figure.caption", span = self.span())]
|
||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||
|
@ -4,8 +4,8 @@ use std::str::FromStr;
|
||||
use crate::diag::{bail, At, SourceResult, StrResult};
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
cast, elem, scope, Content, Finalize, Label, NativeElement, Packed, Show, Smart,
|
||||
StyleChain, Synthesize,
|
||||
cast, elem, scope, Content, Label, NativeElement, Packed, Show, ShowSet, Smart,
|
||||
StyleChain, Styles,
|
||||
};
|
||||
use crate::introspection::{Count, Counter, CounterUpdate, Locatable, Location};
|
||||
use crate::layout::{Abs, Em, HElem, Length, Ratio};
|
||||
@ -50,7 +50,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, Synthesize, Show, Count)]
|
||||
#[elem(scope, Locatable, Show, Count)]
|
||||
pub struct FootnoteElem {
|
||||
/// How to number footnotes.
|
||||
///
|
||||
@ -123,14 +123,6 @@ impl Packed<FootnoteElem> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Synthesize for Packed<FootnoteElem> {
|
||||
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
|
||||
let elem = self.as_mut();
|
||||
elem.push_numbering(elem.numbering(styles).clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Show for Packed<FootnoteElem> {
|
||||
#[typst_macros::time(name = "footnote", span = self.span())]
|
||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||
@ -188,7 +180,7 @@ cast! {
|
||||
/// #footnote[It's down here]
|
||||
/// has red text!
|
||||
/// ```
|
||||
#[elem(name = "entry", title = "Footnote Entry", Show, Finalize)]
|
||||
#[elem(name = "entry", title = "Footnote Entry", Show, ShowSet)]
|
||||
pub struct FootnoteEntry {
|
||||
/// The footnote for this entry. It's location can be used to determine
|
||||
/// the footnote counter state.
|
||||
@ -303,13 +295,14 @@ impl Show for Packed<FootnoteEntry> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Packed<FootnoteEntry> {
|
||||
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
|
||||
impl ShowSet for Packed<FootnoteEntry> {
|
||||
fn show_set(&self, _: StyleChain) -> Styles {
|
||||
let text_size = Em::new(0.85);
|
||||
let leading = Em::new(0.5);
|
||||
realized
|
||||
.styled(ParElem::set_leading(leading.into()))
|
||||
.styled(TextElem::set_size(TextSize(text_size.into())))
|
||||
let mut out = Styles::new();
|
||||
out.set(ParElem::set_leading(leading.into()));
|
||||
out.set(TextElem::set_size(TextSize(text_size.into())));
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use std::num::NonZeroUsize;
|
||||
use crate::diag::SourceResult;
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
elem, Content, Finalize, NativeElement, Packed, Show, Smart, StyleChain, Styles,
|
||||
elem, Content, NativeElement, Packed, Show, ShowSet, Smart, StyleChain, Styles,
|
||||
Synthesize,
|
||||
};
|
||||
use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
|
||||
@ -43,7 +43,7 @@ use crate::util::{option_eq, NonZeroExt};
|
||||
/// Headings have dedicated syntax: They can be created by starting a line with
|
||||
/// one or multiple equals signs, followed by a space. The number of equals
|
||||
/// signs determines the heading's logical nesting depth.
|
||||
#[elem(Locatable, Synthesize, Count, Show, Finalize, LocalName, Refable, Outlinable)]
|
||||
#[elem(Locatable, Synthesize, Count, Show, ShowSet, LocalName, Refable, Outlinable)]
|
||||
pub struct HeadingElem {
|
||||
/// The logical nesting depth of the heading, starting from one.
|
||||
#[default(NonZeroUsize::ONE)]
|
||||
@ -140,13 +140,7 @@ impl Synthesize for Packed<HeadingElem> {
|
||||
}
|
||||
};
|
||||
|
||||
let elem = self.as_mut();
|
||||
elem.push_level(elem.level(styles));
|
||||
elem.push_numbering(elem.numbering(styles).clone());
|
||||
elem.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
|
||||
elem.push_outlined(elem.outlined(styles));
|
||||
elem.push_bookmarked(elem.bookmarked(styles));
|
||||
|
||||
self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -166,8 +160,8 @@ impl Show for Packed<HeadingElem> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Packed<HeadingElem> {
|
||||
fn finalize(&self, realized: Content, styles: StyleChain) -> Content {
|
||||
impl ShowSet for Packed<HeadingElem> {
|
||||
fn show_set(&self, styles: StyleChain) -> Styles {
|
||||
let level = (**self).level(styles).get();
|
||||
let scale = match level {
|
||||
1 => 1.4,
|
||||
@ -179,13 +173,13 @@ impl Finalize for Packed<HeadingElem> {
|
||||
let above = Em::new(if level == 1 { 1.8 } else { 1.44 }) / scale;
|
||||
let below = Em::new(0.75) / scale;
|
||||
|
||||
let mut styles = Styles::new();
|
||||
styles.set(TextElem::set_size(TextSize(size.into())));
|
||||
styles.set(TextElem::set_weight(FontWeight::BOLD));
|
||||
styles.set(BlockElem::set_above(VElem::block_around(above.into())));
|
||||
styles.set(BlockElem::set_below(VElem::block_around(below.into())));
|
||||
styles.set(BlockElem::set_sticky(true));
|
||||
realized.styled_with_map(styles)
|
||||
let mut out = Styles::new();
|
||||
out.set(TextElem::set_size(TextSize(size.into())));
|
||||
out.set(TextElem::set_weight(FontWeight::BOLD));
|
||||
out.set(BlockElem::set_above(VElem::block_around(above.into())));
|
||||
out.set(BlockElem::set_below(VElem::block_around(below.into())));
|
||||
out.set(BlockElem::set_sticky(true));
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ use std::str::FromStr;
|
||||
use crate::diag::{bail, At, SourceResult};
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
cast, elem, scope, select_where, Content, Finalize, Func, LocatableSelector,
|
||||
NativeElement, Packed, Show, Smart, StyleChain,
|
||||
cast, elem, scope, select_where, Content, Func, LocatableSelector, NativeElement,
|
||||
Packed, Show, ShowSet, Smart, StyleChain, Styles,
|
||||
};
|
||||
use crate::introspection::{Counter, CounterKey, Locatable};
|
||||
use crate::layout::{BoxElem, Fr, HElem, HideElem, Length, Rel, RepeatElem, Spacing};
|
||||
@ -57,7 +57,7 @@ use crate::util::{option_eq, NonZeroExt};
|
||||
/// `title` and `indent` parameters. If desired, however, it is possible to have
|
||||
/// more control over the outline's look and style through the
|
||||
/// [`outline.entry`]($outline.entry) element.
|
||||
#[elem(scope, keywords = ["Table of Contents"], Show, Finalize, LocalName)]
|
||||
#[elem(scope, keywords = ["Table of Contents"], Show, ShowSet, LocalName)]
|
||||
pub struct OutlineElem {
|
||||
/// The title of the outline.
|
||||
///
|
||||
@ -250,11 +250,12 @@ impl Show for Packed<OutlineElem> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Packed<OutlineElem> {
|
||||
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
|
||||
realized
|
||||
.styled(HeadingElem::set_outlined(false))
|
||||
.styled(HeadingElem::set_numbering(None))
|
||||
impl ShowSet for Packed<OutlineElem> {
|
||||
fn show_set(&self, _: StyleChain) -> Styles {
|
||||
let mut out = Styles::new();
|
||||
out.set(HeadingElem::set_outlined(false));
|
||||
out.set(HeadingElem::set_numbering(None));
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::diag::SourceResult;
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
cast, elem, Content, Finalize, Label, NativeElement, Packed, Show, Smart, StyleChain,
|
||||
Synthesize,
|
||||
cast, elem, Content, Label, NativeElement, Packed, Show, ShowSet, Smart, StyleChain,
|
||||
Styles,
|
||||
};
|
||||
use crate::layout::{Alignment, BlockElem, Em, HElem, PadElem, Spacing, VElem};
|
||||
use crate::model::{CitationForm, CiteElem};
|
||||
@ -40,7 +40,7 @@ use crate::text::{SmartQuoteElem, SpaceElem, TextElem};
|
||||
/// flame of Udûn. Go back to the Shadow! You cannot pass.
|
||||
/// ]
|
||||
/// ```
|
||||
#[elem(Finalize, Show, Synthesize)]
|
||||
#[elem(ShowSet, Show)]
|
||||
pub struct QuoteElem {
|
||||
/// Whether this is a block quote.
|
||||
///
|
||||
@ -145,15 +145,6 @@ cast! {
|
||||
label: Label => Self::Label(label),
|
||||
}
|
||||
|
||||
impl Synthesize for Packed<QuoteElem> {
|
||||
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
|
||||
let elem = self.as_mut();
|
||||
elem.push_block(elem.block(styles));
|
||||
elem.push_quotes(elem.quotes(styles));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Show for Packed<QuoteElem> {
|
||||
#[typst_macros::time(name = "quote", span = self.span())]
|
||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||
@ -205,15 +196,16 @@ impl Show for Packed<QuoteElem> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Packed<QuoteElem> {
|
||||
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
|
||||
impl ShowSet for Packed<QuoteElem> {
|
||||
fn show_set(&self, _: StyleChain) -> Styles {
|
||||
let x = Em::new(1.0).into();
|
||||
let above = Em::new(2.4).into();
|
||||
let below = Em::new(1.8).into();
|
||||
realized
|
||||
.styled(PadElem::set_left(x))
|
||||
.styled(PadElem::set_right(x))
|
||||
.styled(BlockElem::set_above(VElem::block_around(above)))
|
||||
.styled(BlockElem::set_below(VElem::block_around(below)))
|
||||
let mut out = Styles::new();
|
||||
out.set(PadElem::set_left(x));
|
||||
out.set(PadElem::set_right(x));
|
||||
out.set(BlockElem::set_above(VElem::block_around(above)));
|
||||
out.set(BlockElem::set_below(VElem::block_around(below)));
|
||||
out
|
||||
}
|
||||
}
|
||||
|
@ -251,8 +251,11 @@ fn to_citation(
|
||||
},
|
||||
));
|
||||
|
||||
if let Some(loc) = reference.location() {
|
||||
elem.set_location(loc);
|
||||
}
|
||||
|
||||
elem.synthesize(engine, styles)?;
|
||||
elem.set_location(reference.location().unwrap());
|
||||
|
||||
Ok(elem)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ mod behave;
|
||||
pub use self::behave::BehavedBuilder;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::OnceCell;
|
||||
use std::mem;
|
||||
|
||||
use smallvec::smallvec;
|
||||
@ -13,8 +14,9 @@ use typed_arena::Arena;
|
||||
use crate::diag::{bail, SourceResult};
|
||||
use crate::engine::{Engine, Route};
|
||||
use crate::foundations::{
|
||||
Behave, Behaviour, Content, Finalize, Guard, NativeElement, Packed, Recipe, Selector,
|
||||
Show, StyleChain, StyleVec, StyleVecBuilder, Styles, Synthesize,
|
||||
Behave, Behaviour, Content, Guard, NativeElement, Packed, Recipe, Regex, Selector,
|
||||
Show, ShowSet, StyleChain, StyleVec, StyleVecBuilder, Styles, Synthesize,
|
||||
Transformation,
|
||||
};
|
||||
use crate::introspection::{Locatable, Meta, MetaElem};
|
||||
use crate::layout::{
|
||||
@ -56,7 +58,7 @@ pub fn realize_block<'a>(
|
||||
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
|
||||
// These elements implement `Layout` but still require a flow for
|
||||
// proper layout.
|
||||
if content.can::<dyn LayoutMultiple>() && !applicable(content, styles) {
|
||||
if content.can::<dyn LayoutMultiple>() && verdict(engine, content, styles).is_none() {
|
||||
return Ok((Cow::Borrowed(content), styles));
|
||||
}
|
||||
|
||||
@ -69,161 +71,260 @@ pub fn realize_block<'a>(
|
||||
Ok((Cow::Owned(FlowElem::new(children.to_vec()).pack().spanned(span)), shared))
|
||||
}
|
||||
|
||||
/// Whether the target is affected by show rules in the given style chain.
|
||||
pub fn applicable(target: &Content, styles: StyleChain) -> bool {
|
||||
if target.needs_preparation() || target.can::<dyn Show>() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find out how many recipes there are.
|
||||
let mut n = styles.recipes().count();
|
||||
|
||||
// Find out whether any recipe matches and is unguarded.
|
||||
for recipe in styles.recipes() {
|
||||
if !target.is_guarded(Guard(n)) && recipe.applicable(target, styles) {
|
||||
return true;
|
||||
}
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Apply the show rules in the given style chain to a target.
|
||||
/// Apply the show rules in the given style chain to a target element.
|
||||
pub fn realize(
|
||||
engine: &mut Engine,
|
||||
target: &Content,
|
||||
styles: StyleChain,
|
||||
) -> SourceResult<Option<Content>> {
|
||||
// Pre-process.
|
||||
if target.needs_preparation() {
|
||||
let mut elem = target.clone();
|
||||
if target.can::<dyn Locatable>() || target.label().is_some() {
|
||||
let location = engine.locator.locate(hash128(target));
|
||||
elem.set_location(location);
|
||||
}
|
||||
let Some(Verdict { prepared, mut map, step }) = verdict(engine, target, styles)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
if let Some(synthesizable) = elem.with_mut::<dyn Synthesize>() {
|
||||
synthesizable.synthesize(engine, styles)?;
|
||||
}
|
||||
// Create a fresh copy that we can mutate.
|
||||
let mut target = target.clone();
|
||||
|
||||
elem.mark_prepared();
|
||||
|
||||
let span = elem.span();
|
||||
let meta = elem.location().is_some().then(|| Meta::Elem(elem.clone()));
|
||||
|
||||
let mut content = elem;
|
||||
if let Some(finalizable) = target.with::<dyn Finalize>() {
|
||||
content = finalizable.finalize(content, styles);
|
||||
}
|
||||
|
||||
if let Some(meta) = meta {
|
||||
return Ok(Some(
|
||||
(content + MetaElem::new().pack().spanned(span))
|
||||
.styled(MetaElem::set_data(smallvec![meta])),
|
||||
));
|
||||
} else {
|
||||
return Ok(Some(content));
|
||||
}
|
||||
// If the element isn't yet prepared (we're seeing it for the first time),
|
||||
// prepare it.
|
||||
let mut meta = None;
|
||||
if !prepared {
|
||||
meta = prepare(engine, &mut target, &mut map, styles)?;
|
||||
}
|
||||
|
||||
// Find out how many recipes there are.
|
||||
let mut n = styles.recipes().count();
|
||||
// Apply the step.
|
||||
let mut output = match step {
|
||||
// Apply a user-defined show rule.
|
||||
Some(Step::Recipe(recipe, guard)) => show(engine, target, recipe, guard)?,
|
||||
|
||||
// Find an applicable show rule recipe.
|
||||
for recipe in styles.recipes() {
|
||||
let guard = Guard(n);
|
||||
if !target.is_guarded(guard) && recipe.applicable(target, styles) {
|
||||
if let Some(content) = try_apply(engine, target, recipe, guard)? {
|
||||
return Ok(Some(content));
|
||||
// If the verdict picks this step, the `target` is guaranteed
|
||||
// to have a built-in show rule.
|
||||
Some(Step::Builtin) => {
|
||||
target.with::<dyn Show>().unwrap().show(engine, styles.chain(&map))?
|
||||
}
|
||||
|
||||
// Nothing to do.
|
||||
None => target,
|
||||
};
|
||||
|
||||
// If necessary, apply metadata generated in the preparation.
|
||||
if let Some(meta) = meta {
|
||||
output += meta.pack();
|
||||
}
|
||||
|
||||
Ok(Some(output.styled_with_map(map)))
|
||||
}
|
||||
|
||||
/// What to do with an element when encountering it during realization.
|
||||
struct Verdict<'a> {
|
||||
/// Whether the element is already prepated (i.e. things that should only
|
||||
/// happen once have happened).
|
||||
prepared: bool,
|
||||
/// A map of styles to apply to the element.
|
||||
map: Styles,
|
||||
/// An optional transformation step to apply to the element.
|
||||
step: Option<Step<'a>>,
|
||||
}
|
||||
|
||||
/// An optional transformation step to apply to an element.
|
||||
enum Step<'a> {
|
||||
/// A user-defined transformational show rule.
|
||||
Recipe(&'a Recipe, Guard),
|
||||
/// The built-in show rule.
|
||||
Builtin,
|
||||
}
|
||||
|
||||
/// Inspects a target element and the current styles and determines how to
|
||||
/// proceed with the styling.
|
||||
fn verdict<'a>(
|
||||
engine: &mut Engine,
|
||||
target: &'a Content,
|
||||
styles: StyleChain<'a>,
|
||||
) -> Option<Verdict<'a>> {
|
||||
let mut target = target;
|
||||
let mut map = Styles::new();
|
||||
let mut step = None;
|
||||
let mut slot;
|
||||
|
||||
let depth = OnceCell::new();
|
||||
let prepared = target.is_prepared();
|
||||
|
||||
// Do pre-synthesis on a cloned element to be able to match on synthesized
|
||||
// fields before real synthesis runs (during preparation). It's really
|
||||
// unfortunate that we have to do this, but otherwise
|
||||
// `show figure.where(kind: table)` won't work :(
|
||||
if !prepared && target.can::<dyn Synthesize>() {
|
||||
slot = target.clone();
|
||||
slot.with_mut::<dyn Synthesize>()
|
||||
.unwrap()
|
||||
.synthesize(engine, styles)
|
||||
.ok();
|
||||
target = &slot;
|
||||
}
|
||||
|
||||
for (i, recipe) in styles.recipes().enumerate() {
|
||||
// We're not interested in recipes that don't match.
|
||||
if !recipe.applicable(target, styles) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Transformation::Style(transform) = &recipe.transform {
|
||||
// If this is a show-set for an unprepared element, we need to apply
|
||||
// it.
|
||||
if !prepared {
|
||||
map.apply(transform.clone());
|
||||
}
|
||||
} else if step.is_none() {
|
||||
// Lazily compute the total number of recipes in the style chain. We
|
||||
// need it to determine whether a particular show rule was already
|
||||
// applied to the `target` previously. For this purpose, show rules
|
||||
// are indexed from the top of the chain as the chain might grow to
|
||||
// the bottom.
|
||||
let depth = *depth.get_or_init(|| styles.recipes().count());
|
||||
let guard = Guard(depth - i);
|
||||
|
||||
if !target.is_guarded(guard) {
|
||||
// If we find a matching, unguarded replacement show rule,
|
||||
// remember it, but still continue searching for potential
|
||||
// show-set styles that might change the verdict.
|
||||
step = Some(Step::Recipe(recipe, guard));
|
||||
|
||||
// If we found a show rule and are already prepared, there is
|
||||
// nothing else to do, so we can just break.
|
||||
if prepared {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
// Apply the built-in show rule if there was no matching recipe.
|
||||
if let Some(showable) = target.with::<dyn Show>() {
|
||||
return Ok(Some(showable.show(engine, styles)?));
|
||||
// If we found no user-defined rule, also consider the built-in show rule.
|
||||
if step.is_none() && target.can::<dyn Show>() {
|
||||
step = Some(Step::Builtin);
|
||||
}
|
||||
|
||||
// If there's no nothing to do, there is also no verdict.
|
||||
if step.is_none()
|
||||
&& map.is_empty()
|
||||
&& (prepared || {
|
||||
target.label().is_none()
|
||||
&& !target.can::<dyn ShowSet>()
|
||||
&& !target.can::<dyn Locatable>()
|
||||
&& !target.can::<dyn Synthesize>()
|
||||
})
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Verdict { prepared, map, step })
|
||||
}
|
||||
|
||||
/// This is only executed the first time an element is visited.
|
||||
fn prepare(
|
||||
engine: &mut Engine,
|
||||
target: &mut Content,
|
||||
map: &mut Styles,
|
||||
styles: StyleChain,
|
||||
) -> SourceResult<Option<Packed<MetaElem>>> {
|
||||
// Generate a location for the element, which uniquely identifies it in
|
||||
// the document. This has some overhead, so we only do it for elements
|
||||
// that are explicitly marked as locatable and labelled elements.
|
||||
if target.can::<dyn Locatable>() || target.label().is_some() {
|
||||
let location = engine.locator.locate(hash128(&target));
|
||||
target.set_location(location);
|
||||
}
|
||||
|
||||
// Apply built-in show-set rules. User-defined show-set rules are already
|
||||
// considered in the map built while determining the verdict.
|
||||
if let Some(show_settable) = target.with::<dyn ShowSet>() {
|
||||
map.apply(show_settable.show_set(styles));
|
||||
}
|
||||
|
||||
// If necessary, generated "synthesized" fields (which are derived from
|
||||
// other fields or queries). Do this after show-set so that show-set styles
|
||||
// are respected.
|
||||
if let Some(synthesizable) = target.with_mut::<dyn Synthesize>() {
|
||||
synthesizable.synthesize(engine, styles.chain(map))?;
|
||||
}
|
||||
|
||||
// Copy style chain fields into the element itself, so that they are
|
||||
// available in rules.
|
||||
target.materialize(styles.chain(map));
|
||||
|
||||
// Ensure that this preparation only runs once by marking the element as
|
||||
// prepared.
|
||||
target.mark_prepared();
|
||||
|
||||
// Apply metadata be able to find the element in the frames.
|
||||
// Do this after synthesis, so that it includes the synthesized fields.
|
||||
if target.location().is_some() {
|
||||
// Add a style to the whole element's subtree identifying it as
|
||||
// belonging to the element.
|
||||
map.set(MetaElem::set_data(smallvec![Meta::Elem(target.clone())]));
|
||||
|
||||
// Return an extra meta elem that will be attached so that the metadata
|
||||
// styles are not lost in case the element's show rule results in
|
||||
// nothing.
|
||||
return Ok(Some(Packed::new(MetaElem::new()).spanned(target.span())));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Try to apply a recipe to the target.
|
||||
fn try_apply(
|
||||
/// Apply a user-defined show rule.
|
||||
fn show(
|
||||
engine: &mut Engine,
|
||||
target: &Content,
|
||||
target: Content,
|
||||
recipe: &Recipe,
|
||||
guard: Guard,
|
||||
) -> SourceResult<Option<Content>> {
|
||||
) -> SourceResult<Content> {
|
||||
match &recipe.selector {
|
||||
Some(Selector::Elem(element, _)) => {
|
||||
if target.func() != *element {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
recipe.apply(engine, target.clone().guarded(guard)).map(Some)
|
||||
}
|
||||
|
||||
Some(Selector::Label(label)) => {
|
||||
if target.label() != Some(*label) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
recipe.apply(engine, target.clone().guarded(guard)).map(Some)
|
||||
}
|
||||
|
||||
Some(Selector::Regex(regex)) => {
|
||||
let Some(elem) = target.to_packed::<TextElem>() else {
|
||||
return Ok(None);
|
||||
};
|
||||
// If the verdict picks this rule, the `target` is guaranteed
|
||||
// to be a text element.
|
||||
let text = target.into_packed::<TextElem>().unwrap();
|
||||
show_regex(engine, &text, regex, recipe, guard)
|
||||
}
|
||||
_ => recipe.apply(engine, target.guarded(guard)),
|
||||
}
|
||||
}
|
||||
|
||||
let make = |s: &str| {
|
||||
let mut fresh = elem.clone();
|
||||
fresh.push_text(s.into());
|
||||
fresh.pack()
|
||||
};
|
||||
/// Apply a regex show rule recipe to a target.
|
||||
fn show_regex(
|
||||
engine: &mut Engine,
|
||||
elem: &Packed<TextElem>,
|
||||
regex: &Regex,
|
||||
recipe: &Recipe,
|
||||
guard: Guard,
|
||||
) -> SourceResult<Content> {
|
||||
let make = |s: &str| {
|
||||
let mut fresh = elem.clone();
|
||||
fresh.push_text(s.into());
|
||||
fresh.pack()
|
||||
};
|
||||
|
||||
let mut result = vec![];
|
||||
let mut cursor = 0;
|
||||
let mut result = vec![];
|
||||
let mut cursor = 0;
|
||||
|
||||
let text = elem.text();
|
||||
let text = elem.text();
|
||||
|
||||
for m in regex.find_iter(elem.text()) {
|
||||
let start = m.start();
|
||||
if cursor < start {
|
||||
result.push(make(&text[cursor..start]));
|
||||
}
|
||||
|
||||
let piece = make(m.as_str()).guarded(guard);
|
||||
let transformed = recipe.apply(engine, piece)?;
|
||||
result.push(transformed);
|
||||
cursor = m.end();
|
||||
}
|
||||
|
||||
if result.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if cursor < text.len() {
|
||||
result.push(make(&text[cursor..]));
|
||||
}
|
||||
|
||||
Ok(Some(Content::sequence(result)))
|
||||
for m in regex.find_iter(elem.text()) {
|
||||
let start = m.start();
|
||||
if cursor < start {
|
||||
result.push(make(&text[cursor..start]));
|
||||
}
|
||||
|
||||
// Not supported here.
|
||||
Some(
|
||||
Selector::Or(_)
|
||||
| Selector::And(_)
|
||||
| Selector::Location(_)
|
||||
| Selector::Can(_)
|
||||
| Selector::Before { .. }
|
||||
| Selector::After { .. },
|
||||
) => Ok(None),
|
||||
|
||||
None => Ok(None),
|
||||
let piece = make(m.as_str()).guarded(guard);
|
||||
let transformed = recipe.apply(engine, piece)?;
|
||||
result.push(transformed);
|
||||
cursor = m.end();
|
||||
}
|
||||
|
||||
if cursor < text.len() {
|
||||
result.push(make(&text[cursor..]));
|
||||
}
|
||||
|
||||
Ok(Content::sequence(result))
|
||||
}
|
||||
|
||||
/// Builds a document or a flow element from content.
|
||||
|
@ -12,8 +12,8 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use crate::diag::{At, FileError, SourceResult, StrResult};
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
cast, elem, scope, Args, Array, Bytes, Content, Finalize, Fold, NativeElement,
|
||||
Packed, PlainText, Show, Smart, StyleChain, Styles, Synthesize, Value,
|
||||
cast, elem, scope, Args, Array, Bytes, Content, Fold, NativeElement, Packed,
|
||||
PlainText, Show, ShowSet, Smart, StyleChain, Styles, Synthesize, Value,
|
||||
};
|
||||
use crate::layout::{BlockElem, Em, HAlignment};
|
||||
use crate::model::Figurable;
|
||||
@ -74,7 +74,7 @@ type LineFn<'a> = &'a mut dyn FnMut(i64, Range<usize>, &mut Vec<Content>);
|
||||
title = "Raw Text / Code",
|
||||
Synthesize,
|
||||
Show,
|
||||
Finalize,
|
||||
ShowSet,
|
||||
LocalName,
|
||||
Figurable,
|
||||
PlainText
|
||||
@ -289,11 +289,17 @@ impl RawElem {
|
||||
|
||||
impl Synthesize for Packed<RawElem> {
|
||||
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
|
||||
let span = self.span();
|
||||
let elem = self.as_mut();
|
||||
let seq = self.highlight(styles);
|
||||
self.push_lines(seq);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let lang = elem.lang(styles).clone();
|
||||
elem.push_lang(lang);
|
||||
impl Packed<RawElem> {
|
||||
#[comemo::memoize]
|
||||
fn highlight(&self, styles: StyleChain) -> Vec<Packed<RawLine>> {
|
||||
let elem = self.as_ref();
|
||||
let span = self.span();
|
||||
|
||||
let mut text = elem.text().clone();
|
||||
if text.contains('\t') {
|
||||
@ -389,9 +395,7 @@ impl Synthesize for Packed<RawElem> {
|
||||
}));
|
||||
};
|
||||
|
||||
elem.push_lines(seq);
|
||||
|
||||
Ok(())
|
||||
seq
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,16 +425,15 @@ impl Show for Packed<RawElem> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Finalize for Packed<RawElem> {
|
||||
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
|
||||
let mut styles = Styles::new();
|
||||
styles.set(TextElem::set_overhang(false));
|
||||
styles.set(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false))));
|
||||
styles.set(TextElem::set_size(TextSize(Em::new(0.8).into())));
|
||||
styles
|
||||
.set(TextElem::set_font(FontList(vec![FontFamily::new("DejaVu Sans Mono")])));
|
||||
styles.set(SmartQuoteElem::set_enabled(false));
|
||||
realized.styled_with_map(styles)
|
||||
impl ShowSet for Packed<RawElem> {
|
||||
fn show_set(&self, _: StyleChain) -> Styles {
|
||||
let mut out = Styles::new();
|
||||
out.set(TextElem::set_overhang(false));
|
||||
out.set(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false))));
|
||||
out.set(TextElem::set_size(TextSize(Em::new(0.8).into())));
|
||||
out.set(TextElem::set_font(FontList(vec![FontFamily::new("DejaVu Sans Mono")])));
|
||||
out.set(SmartQuoteElem::set_enabled(false));
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 8.1 KiB |
BIN
tests/ref/compiler/show-set-func.png
Normal file
BIN
tests/ref/compiler/show-set-func.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
BIN
tests/ref/compiler/show-set.png
Normal file
BIN
tests/ref/compiler/show-set.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
@ -1,19 +1,30 @@
|
||||
// Tests for field introspection.
|
||||
// Tests content field access.
|
||||
|
||||
---
|
||||
// Verify that non-inherent fields are hidden if not set.
|
||||
#show figure: it => [
|
||||
`repr(it)`: #repr(it) \
|
||||
`it.has("gap"): `#repr(it.has("gap")) \
|
||||
]
|
||||
// Ensure that fields from set rules are materialized into the element before
|
||||
// a show rule runs.
|
||||
#set table(columns: (10pt, auto))
|
||||
#show table: it => it.columns
|
||||
#table[A][B][C][D]
|
||||
|
||||
#figure[]
|
||||
---
|
||||
// Test it again with a different element.
|
||||
#set heading(numbering: "(I)")
|
||||
#show heading: set text(size: 11pt, weight: "regular")
|
||||
#show heading: it => it.numbering
|
||||
= Heading
|
||||
|
||||
#figure([], gap: 1pt)
|
||||
---
|
||||
// Test it with query.
|
||||
#set raw(lang: "rust")
|
||||
#locate(loc => {
|
||||
let elem = query(<myraw>, loc).first()
|
||||
elem.lang
|
||||
})
|
||||
`raw` <myraw>
|
||||
|
||||
---
|
||||
// Integrated test for content fields.
|
||||
|
||||
#let compute(equation, ..vars) = {
|
||||
let vars = vars.named()
|
||||
let f(elem) = {
|
||||
|
@ -31,9 +31,9 @@ You can use the ```rs *const T``` pointer or
|
||||
the ```rs &mut T``` reference.
|
||||
|
||||
---
|
||||
#show heading: set text(green)
|
||||
#show heading.where(level: 1): set text(red)
|
||||
#show heading.where(level: 2): set text(blue)
|
||||
#show heading: set text(green)
|
||||
= Red
|
||||
== Blue
|
||||
=== Green
|
||||
|
16
tests/typ/compiler/show-set-func.typ
Normal file
16
tests/typ/compiler/show-set-func.typ
Normal file
@ -0,0 +1,16 @@
|
||||
// Test set rules on an element in show rules for said element.
|
||||
|
||||
---
|
||||
// These are both red because in the expanded form, `set text(red)` ends up
|
||||
// closer to the content than `set text(blue)`.
|
||||
#show strong: it => { set text(red); it }
|
||||
Hello *World*
|
||||
|
||||
#show strong: it => { set text(blue); it }
|
||||
Hello *World*
|
||||
|
||||
---
|
||||
// This doesn't have an effect. An element is materialized before any show
|
||||
// rules run.
|
||||
#show heading: it => { set heading(numbering: "(I)"); it }
|
||||
= Heading
|
55
tests/typ/compiler/show-set.typ
Normal file
55
tests/typ/compiler/show-set.typ
Normal file
@ -0,0 +1,55 @@
|
||||
// Test show-set rules.
|
||||
|
||||
---
|
||||
// Test overriding show-set rules.
|
||||
#show strong: set text(red)
|
||||
Hello *World*
|
||||
|
||||
#show strong: set text(blue)
|
||||
Hello *World*
|
||||
|
||||
---
|
||||
// Test show-set rule on the same element.
|
||||
#set figure(supplement: [Default])
|
||||
#show figure.where(kind: table): set figure(supplement: [Tableau])
|
||||
#figure(
|
||||
table(columns: 2)[A][B][C][D],
|
||||
caption: [Four letters],
|
||||
)
|
||||
|
||||
---
|
||||
// Test both things at once.
|
||||
#show heading: set text(red)
|
||||
= Level 1
|
||||
== Level 2
|
||||
|
||||
#show heading.where(level: 1): set text(blue)
|
||||
#show heading.where(level: 1): set text(green)
|
||||
#show heading.where(level: 1): set heading(numbering: "(I)")
|
||||
= Level 1
|
||||
== Level 2
|
||||
|
||||
---
|
||||
// Test setting the thing we just matched on.
|
||||
// This is quite cursed, but it works.
|
||||
#set heading(numbering: "(I)")
|
||||
#show heading.where(numbering: "(I)"): set heading(numbering: "1.")
|
||||
= Heading
|
||||
|
||||
---
|
||||
// Same thing, but even more cursed, because `kind` is synthesized.
|
||||
#show figure.where(kind: table): set figure(kind: raw)
|
||||
#figure(table[A], caption: [Code])
|
||||
|
||||
---
|
||||
// Test that show-set rules on the same element don't affect each other. This
|
||||
// could be implemented, but isn't as of yet.
|
||||
#show heading.where(level: 1): set heading(numbering: "(I)")
|
||||
#show heading.where(numbering: "(I)"): set text(red)
|
||||
= Heading
|
||||
|
||||
---
|
||||
// Test show-set rules on layoutable element to ensure it is realized
|
||||
// even though it implements `LayoutMultiple`.
|
||||
#show table: set text(red)
|
||||
#pad(table(columns: 4)[A][B][C][D])
|
Loading…
x
Reference in New Issue
Block a user