mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Allow elem
synthesized fields to take a default value (#2687)
This commit is contained in:
parent
624ff5cb7a
commit
5aaaacbf47
@ -484,19 +484,23 @@ pub struct FigureCaption {
|
|||||||
|
|
||||||
/// The figure's supplement.
|
/// The figure's supplement.
|
||||||
#[synthesized]
|
#[synthesized]
|
||||||
|
#[default(None)]
|
||||||
pub supplement: Option<Content>,
|
pub supplement: Option<Content>,
|
||||||
|
|
||||||
/// How to number the figure.
|
/// How to number the figure.
|
||||||
#[synthesized]
|
#[synthesized]
|
||||||
|
#[default(None)]
|
||||||
pub numbering: Option<Numbering>,
|
pub numbering: Option<Numbering>,
|
||||||
|
|
||||||
/// The counter for the figure.
|
/// The counter for the figure.
|
||||||
#[synthesized]
|
#[synthesized]
|
||||||
|
#[default(None)]
|
||||||
pub counter: Option<Counter>,
|
pub counter: Option<Counter>,
|
||||||
|
|
||||||
/// The figure's location.
|
/// The figure's location.
|
||||||
#[internal]
|
#[internal]
|
||||||
#[synthesized]
|
#[synthesized]
|
||||||
|
#[default(None)]
|
||||||
pub figure_location: Option<Location>,
|
pub figure_location: Option<Location>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +270,12 @@ impl Show for FootnoteEntry {
|
|||||||
let default = StyleChain::default();
|
let default = StyleChain::default();
|
||||||
let numbering = note.numbering(default);
|
let numbering = note.numbering(default);
|
||||||
let counter = Counter::of(FootnoteElem::elem());
|
let counter = Counter::of(FootnoteElem::elem());
|
||||||
let loc = note.location().unwrap();
|
let Some(loc) = note.location() else {
|
||||||
|
bail!(error!(self.span(), "footnote entry must have a location").with_hint(
|
||||||
|
"try using a query or a show rule to customize the footnote instead"
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
let num = counter.at(vt, loc)?.display(vt, numbering)?;
|
let num = counter.at(vt, loc)?.display(vt, numbering)?;
|
||||||
let sup = SuperElem::new(num)
|
let sup = SuperElem::new(num)
|
||||||
.pack()
|
.pack()
|
||||||
|
@ -489,7 +489,14 @@ impl Show for OutlineEntry {
|
|||||||
|
|
||||||
// In case a user constructs an outline entry with an arbitrary element.
|
// In case a user constructs an outline entry with an arbitrary element.
|
||||||
let Some(location) = elem.location() else {
|
let Some(location) = elem.location() else {
|
||||||
|
if elem.can::<dyn Locatable>() && elem.can::<dyn Outlinable>() {
|
||||||
|
bail!(error!(self.span(), "{} must have a location", elem.func().name())
|
||||||
|
.with_hint(
|
||||||
|
"try using a query or a show rule to customize the outline.entry instead",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
bail!(self.span(), "cannot outline {}", elem.func().name())
|
bail!(self.span(), "cannot outline {}", elem.func().name())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// The body text remains overridable.
|
// The body text remains overridable.
|
||||||
|
@ -129,7 +129,7 @@ struct Field {
|
|||||||
borrowed: bool,
|
borrowed: bool,
|
||||||
forced_variant: Option<usize>,
|
forced_variant: Option<usize>,
|
||||||
parse: Option<BlockWithReturn>,
|
parse: Option<BlockWithReturn>,
|
||||||
default: syn::Expr,
|
default: Option<syn::Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Field {
|
impl Field {
|
||||||
@ -229,9 +229,7 @@ fn parse_field(field: &syn::Field) -> Result<Field> {
|
|||||||
fold: has_attr(&mut attrs, "fold"),
|
fold: has_attr(&mut attrs, "fold"),
|
||||||
resolve: has_attr(&mut attrs, "resolve"),
|
resolve: has_attr(&mut attrs, "resolve"),
|
||||||
parse: parse_attr(&mut attrs, "parse")?.flatten(),
|
parse: parse_attr(&mut attrs, "parse")?.flatten(),
|
||||||
default: parse_attr::<syn::Expr>(&mut attrs, "default")?
|
default: parse_attr::<syn::Expr>(&mut attrs, "default")?.flatten(),
|
||||||
.flatten()
|
|
||||||
.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() }),
|
|
||||||
vis: field.vis.clone(),
|
vis: field.vis.clone(),
|
||||||
ident: ident.clone(),
|
ident: ident.clone(),
|
||||||
ident_in: Ident::new(&format!("{}_in", ident), ident.span()),
|
ident_in: Ident::new(&format!("{}_in", ident), ident.span()),
|
||||||
@ -344,11 +342,17 @@ fn create(element: &Elem) -> TokenStream {
|
|||||||
|
|
||||||
/// Create a field declaration.
|
/// Create a field declaration.
|
||||||
fn create_field(field: &Field) -> TokenStream {
|
fn create_field(field: &Field) -> TokenStream {
|
||||||
let Field { ident, ty, docs, required, .. } = field;
|
let Field {
|
||||||
|
ident, ty, docs, required, synthesized, default, ..
|
||||||
|
} = field;
|
||||||
|
|
||||||
let ty = required
|
let ty = required.then(|| quote! { #ty }).unwrap_or_else(|| {
|
||||||
.then(|| quote! { #ty })
|
if *synthesized && default.is_some() {
|
||||||
.unwrap_or_else(|| quote! { Option<#ty> });
|
quote! { #ty }
|
||||||
|
} else {
|
||||||
|
quote! { ::std::option::Option<#ty> }
|
||||||
|
}
|
||||||
|
});
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #docs]
|
#[doc = #docs]
|
||||||
#ident: #ty
|
#ident: #ty
|
||||||
@ -457,9 +461,18 @@ fn create_new_func(element: &Elem) -> TokenStream {
|
|||||||
let defaults = element
|
let defaults = element
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|field| !field.external && (field.synthesized || !field.inherent()))
|
.filter(|field| !field.external && !field.inherent() && !field.synthesized)
|
||||||
.map(|Field { ident, .. }| {
|
.map(|Field { ident, .. }| quote! { #ident: None });
|
||||||
|
let default_synthesized = element
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.filter(|field| !field.external && field.synthesized)
|
||||||
|
.map(|Field { ident, default, .. }| {
|
||||||
|
if let Some(expr) = default {
|
||||||
|
quote! { #ident: #expr }
|
||||||
|
} else {
|
||||||
quote! { #ident: None }
|
quote! { #ident: None }
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let label_and_location = element.unless_capability("Unlabellable", || {
|
let label_and_location = element.unless_capability("Unlabellable", || {
|
||||||
@ -479,6 +492,7 @@ fn create_new_func(element: &Elem) -> TokenStream {
|
|||||||
guards: ::std::vec::Vec::with_capacity(0),
|
guards: ::std::vec::Vec::with_capacity(0),
|
||||||
#(#required,)*
|
#(#required,)*
|
||||||
#(#defaults,)*
|
#(#defaults,)*
|
||||||
|
#(#default_synthesized,)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -486,10 +500,19 @@ fn create_new_func(element: &Elem) -> TokenStream {
|
|||||||
|
|
||||||
/// Create a builder pattern method for a field.
|
/// Create a builder pattern method for a field.
|
||||||
fn create_with_field_method(field: &Field) -> TokenStream {
|
fn create_with_field_method(field: &Field) -> TokenStream {
|
||||||
let Field { vis, ident, with_ident, name, ty, .. } = field;
|
let Field {
|
||||||
|
vis,
|
||||||
|
ident,
|
||||||
|
with_ident,
|
||||||
|
name,
|
||||||
|
ty,
|
||||||
|
synthesized,
|
||||||
|
default,
|
||||||
|
..
|
||||||
|
} = field;
|
||||||
let doc = format!("Set the [`{}`](Self::{}) field.", name, ident);
|
let doc = format!("Set the [`{}`](Self::{}) field.", name, ident);
|
||||||
|
|
||||||
let set = if field.inherent() {
|
let set = if field.inherent() || (*synthesized && default.is_some()) {
|
||||||
quote! { self.#ident = #ident; }
|
quote! { self.#ident = #ident; }
|
||||||
} else {
|
} else {
|
||||||
quote! { self.#ident = Some(#ident); }
|
quote! { self.#ident = Some(#ident); }
|
||||||
@ -505,9 +528,19 @@ fn create_with_field_method(field: &Field) -> TokenStream {
|
|||||||
|
|
||||||
/// Create a set-style method for a field.
|
/// Create a set-style method for a field.
|
||||||
fn create_push_field_method(field: &Field) -> TokenStream {
|
fn create_push_field_method(field: &Field) -> TokenStream {
|
||||||
let Field { vis, ident, push_ident, name, ty, .. } = field;
|
let Field {
|
||||||
|
vis,
|
||||||
|
ident,
|
||||||
|
push_ident,
|
||||||
|
name,
|
||||||
|
ty,
|
||||||
|
synthesized,
|
||||||
|
default,
|
||||||
|
..
|
||||||
|
} = field;
|
||||||
let doc = format!("Push the [`{}`](Self::{}) field.", name, ident);
|
let doc = format!("Push the [`{}`](Self::{}) field.", name, ident);
|
||||||
let set = if field.inherent() && !field.synthesized {
|
let set = if (field.inherent() && !synthesized) || (*synthesized && default.is_some())
|
||||||
|
{
|
||||||
quote! { self.#ident = #ident; }
|
quote! { self.#ident = #ident; }
|
||||||
} else {
|
} else {
|
||||||
quote! { self.#ident = Some(#ident); }
|
quote! { self.#ident = Some(#ident); }
|
||||||
@ -561,10 +594,11 @@ fn create_field_in_method(element: &Elem, field: &Field) -> TokenStream {
|
|||||||
/// Create an accessor methods for a field.
|
/// Create an accessor methods for a field.
|
||||||
fn create_field_method(element: &Elem, field: &Field) -> TokenStream {
|
fn create_field_method(element: &Elem, field: &Field) -> TokenStream {
|
||||||
let Field { vis, docs, ident, output, .. } = field;
|
let Field { vis, docs, ident, output, .. } = field;
|
||||||
if field.inherent() && !field.synthesized {
|
if (field.inherent() && !field.synthesized)
|
||||||
|
|| (field.synthesized && field.default.is_some())
|
||||||
|
{
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #docs]
|
#[doc = #docs]
|
||||||
#[track_caller]
|
|
||||||
#vis fn #ident(&self) -> &#output {
|
#vis fn #ident(&self) -> &#output {
|
||||||
&self.#ident
|
&self.#ident
|
||||||
}
|
}
|
||||||
@ -618,6 +652,9 @@ fn create_style_chain_access(
|
|||||||
(true, true, _) => quote! { get_resolve_fold },
|
(true, true, _) => quote! { get_resolve_fold },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let default = default
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() });
|
||||||
let (init, default) = field.fold.then(|| (None, quote! { || #default })).unwrap_or_else(|| (
|
let (init, default) = field.fold.then(|| (None, quote! { || #default })).unwrap_or_else(|| (
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
static DEFAULT: ::once_cell::sync::Lazy<#ty> = ::once_cell::sync::Lazy::new(|| #default);
|
static DEFAULT: ::once_cell::sync::Lazy<#ty> = ::once_cell::sync::Lazy::new(|| #default);
|
||||||
@ -1141,6 +1178,9 @@ fn create_param_info(field: &Field) -> TokenStream {
|
|||||||
let settable = field.settable();
|
let settable = field.settable();
|
||||||
let default_ty = if *fold { &output } else { &ty };
|
let default_ty = if *fold { &output } else { &ty };
|
||||||
let default = quote_option(&settable.then(|| {
|
let default = quote_option(&settable.then(|| {
|
||||||
|
let default = default
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() });
|
||||||
quote! {
|
quote! {
|
||||||
|| {
|
|| {
|
||||||
let typed: #default_ty = #default;
|
let typed: #default_ty = #default;
|
||||||
|
40
tests/typ/bugs/subelement-panic.typ
Normal file
40
tests/typ/bugs/subelement-panic.typ
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Test that figure captions don't cause panics.
|
||||||
|
// Ref: false
|
||||||
|
|
||||||
|
---
|
||||||
|
// #2530
|
||||||
|
#figure(caption: [test])[].caption
|
||||||
|
|
||||||
|
---
|
||||||
|
// #2165
|
||||||
|
#figure.caption[]
|
||||||
|
|
||||||
|
---
|
||||||
|
// #2328
|
||||||
|
// Error: 4-43 footnote entry must have a location
|
||||||
|
// Hint: 4-43 try using a query or a show rule to customize the footnote instead
|
||||||
|
HI#footnote.entry(clearance: 2.5em)[There]
|
||||||
|
|
||||||
|
---
|
||||||
|
// Enum item (pre-emptive)
|
||||||
|
#enum.item(none)[Hello]
|
||||||
|
#enum.item(17)[Hello]
|
||||||
|
|
||||||
|
---
|
||||||
|
// List item (pre-emptive)
|
||||||
|
#list.item[Hello]
|
||||||
|
|
||||||
|
---
|
||||||
|
// Term item (pre-emptive)
|
||||||
|
#terms.item[Hello][World!]
|
||||||
|
|
||||||
|
---
|
||||||
|
// Outline entry (pre-emptive)
|
||||||
|
// Error: 2-48 cannot outline text
|
||||||
|
#outline.entry(1, [Hello], [World!], none, [1])
|
||||||
|
|
||||||
|
---
|
||||||
|
// Outline entry (pre-emptive, improved error)
|
||||||
|
// Error: 2-55 heading must have a location
|
||||||
|
// Hint: 2-55 try using a query or a show rule to customize the outline.entry instead
|
||||||
|
#outline.entry(1, heading[Hello], [World!], none, [1])
|
Loading…
x
Reference in New Issue
Block a user