mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Locatability and synthesis improvements
This commit is contained in:
parent
880b1847bd
commit
724e9b140c
@ -27,7 +27,7 @@ pub fn read(
|
|||||||
let Spanned { v: path, span } = path;
|
let Spanned { v: path, span } = path;
|
||||||
let path = vm.locate(&path).at(span)?;
|
let path = vm.locate(&path).at(span)?;
|
||||||
let data = vm.world().file(&path).at(span)?;
|
let data = vm.world().file(&path).at(span)?;
|
||||||
let text = String::from_utf8(data.to_vec())
|
let text = std::str::from_utf8(&data)
|
||||||
.map_err(|_| "file is not valid utf-8")
|
.map_err(|_| "file is not valid utf-8")
|
||||||
.at(span)?;
|
.at(span)?;
|
||||||
Value::Str(text.into())
|
Value::Str(text.into())
|
||||||
|
@ -23,7 +23,7 @@ use crate::text::TextNode;
|
|||||||
///
|
///
|
||||||
/// Display: Figure
|
/// Display: Figure
|
||||||
/// Category: meta
|
/// Category: meta
|
||||||
#[node(Synthesize, Show, LocalName)]
|
#[node(Locatable, Synthesize, Show, LocalName)]
|
||||||
pub struct FigureNode {
|
pub struct FigureNode {
|
||||||
/// The content of the figure. Often, an [image]($func/image).
|
/// The content of the figure. Often, an [image]($func/image).
|
||||||
#[required]
|
#[required]
|
||||||
@ -57,26 +57,24 @@ impl FigureNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Synthesize for FigureNode {
|
impl Synthesize for FigureNode {
|
||||||
fn synthesize(&self, vt: &mut Vt, styles: StyleChain) -> Content {
|
fn synthesize(&mut self, vt: &Vt, styles: StyleChain) {
|
||||||
let my_id = vt.identify(self);
|
let my_id = self.0.stable_id().unwrap();
|
||||||
let element = self.element();
|
let element = self.element();
|
||||||
|
|
||||||
let numbering = self.numbering(styles);
|
|
||||||
let mut number = None;
|
let mut number = None;
|
||||||
|
let numbering = self.numbering(styles);
|
||||||
if numbering.is_some() {
|
if numbering.is_some() {
|
||||||
number = NonZeroUsize::new(
|
number = NonZeroUsize::new(
|
||||||
1 + vt
|
1 + vt
|
||||||
.locate(Selector::node::<Self>())
|
.locate_node::<Self>()
|
||||||
.into_iter()
|
|
||||||
.take_while(|&(id, _)| id != my_id)
|
.take_while(|&(id, _)| id != my_id)
|
||||||
.filter(|(_, node)| node.to::<Self>().unwrap().element() == element)
|
.filter(|(_, figure)| figure.element() == element)
|
||||||
.count(),
|
.count(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = self.clone().with_number(number).with_numbering(numbering).pack();
|
self.push_number(number);
|
||||||
let meta = Meta::Node(my_id, node.clone());
|
self.push_numbering(numbering);
|
||||||
node.styled(MetaNode::set_data(vec![meta]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ use crate::text::{TextNode, TextSize};
|
|||||||
///
|
///
|
||||||
/// Display: Heading
|
/// Display: Heading
|
||||||
/// Category: meta
|
/// Category: meta
|
||||||
#[node(Synthesize, Show, Finalize, LocalName)]
|
#[node(Locatable, Synthesize, Show, Finalize, LocalName)]
|
||||||
pub struct HeadingNode {
|
pub struct HeadingNode {
|
||||||
/// The logical nesting depth of the heading, starting from one.
|
/// The logical nesting depth of the heading, starting from one.
|
||||||
#[default(NonZeroUsize::new(1).unwrap())]
|
#[default(NonZeroUsize::new(1).unwrap())]
|
||||||
@ -83,19 +83,16 @@ pub struct HeadingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Synthesize for HeadingNode {
|
impl Synthesize for HeadingNode {
|
||||||
fn synthesize(&self, vt: &mut Vt, styles: StyleChain) -> Content {
|
fn synthesize(&mut self, vt: &Vt, styles: StyleChain) {
|
||||||
let my_id = vt.identify(self);
|
let my_id = self.0.stable_id().unwrap();
|
||||||
let numbering = self.numbering(styles);
|
let numbering = self.numbering(styles);
|
||||||
|
|
||||||
let mut counter = HeadingCounter::new();
|
let mut counter = HeadingCounter::new();
|
||||||
if numbering.is_some() {
|
if numbering.is_some() {
|
||||||
// Advance past existing headings.
|
// Advance past existing headings.
|
||||||
for (_, node) in vt
|
for (_, heading) in
|
||||||
.locate(Selector::node::<Self>())
|
vt.locate_node::<Self>().take_while(|&(id, _)| id != my_id)
|
||||||
.into_iter()
|
|
||||||
.take_while(|&(id, _)| id != my_id)
|
|
||||||
{
|
{
|
||||||
let heading = node.to::<Self>().unwrap();
|
|
||||||
if heading.numbering(StyleChain::default()).is_some() {
|
if heading.numbering(StyleChain::default()).is_some() {
|
||||||
counter.advance(heading);
|
counter.advance(heading);
|
||||||
}
|
}
|
||||||
@ -105,16 +102,10 @@ impl Synthesize for HeadingNode {
|
|||||||
counter.advance(self);
|
counter.advance(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = self
|
self.push_level(self.level(styles));
|
||||||
.clone()
|
self.push_outlined(self.outlined(styles));
|
||||||
.with_level(self.level(styles))
|
self.push_numbers(numbering.is_some().then(|| counter.take()));
|
||||||
.with_outlined(self.outlined(styles))
|
self.push_numbering(numbering);
|
||||||
.with_numbers(numbering.is_some().then(|| counter.take()))
|
|
||||||
.with_numbering(numbering)
|
|
||||||
.pack();
|
|
||||||
|
|
||||||
let meta = Meta::Node(my_id, node.clone());
|
|
||||||
node.styled(MetaNode::set_data(vec![meta]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ impl Show for LinkNode {
|
|||||||
impl Finalize for LinkNode {
|
impl Finalize for LinkNode {
|
||||||
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
|
fn finalize(&self, realized: Content, _: StyleChain) -> Content {
|
||||||
realized
|
realized
|
||||||
.styled(MetaNode::set_data(vec![Meta::Link(self.dest())]))
|
.linked(self.dest())
|
||||||
.styled(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false))))
|
.styled(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,15 +74,14 @@ pub struct OutlineNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Synthesize for OutlineNode {
|
impl Synthesize for OutlineNode {
|
||||||
fn synthesize(&self, vt: &mut Vt, _: StyleChain) -> Content {
|
fn synthesize(&mut self, vt: &Vt, _: StyleChain) {
|
||||||
let headings = vt
|
let headings = vt
|
||||||
.locate(Selector::node::<HeadingNode>())
|
.locate_node::<HeadingNode>()
|
||||||
.into_iter()
|
.map(|(_, node)| node.clone())
|
||||||
.map(|(_, node)| node.to::<HeadingNode>().unwrap().clone())
|
|
||||||
.filter(|node| node.outlined(StyleChain::default()))
|
.filter(|node| node.outlined(StyleChain::default()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.clone().with_headings(headings).pack()
|
self.push_headings(headings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ use crate::text::TextNode;
|
|||||||
/// Category: meta
|
/// Category: meta
|
||||||
#[node(Synthesize, Show)]
|
#[node(Synthesize, Show)]
|
||||||
pub struct RefNode {
|
pub struct RefNode {
|
||||||
/// The label that should be referenced.
|
/// The target label that should be referenced.
|
||||||
#[required]
|
#[required]
|
||||||
pub label: Label,
|
pub target: Label,
|
||||||
|
|
||||||
/// The prefix before the referenced number.
|
/// The prefix before the referenced number.
|
||||||
///
|
///
|
||||||
@ -59,20 +59,19 @@ pub struct RefNode {
|
|||||||
/// ```
|
/// ```
|
||||||
pub prefix: Smart<Option<Func>>,
|
pub prefix: Smart<Option<Func>>,
|
||||||
|
|
||||||
/// All elements with the `target` label in the document.
|
/// All elements with the target label in the document.
|
||||||
#[synthesized]
|
#[synthesized]
|
||||||
pub matches: Vec<Content>,
|
pub matches: Vec<Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Synthesize for RefNode {
|
impl Synthesize for RefNode {
|
||||||
fn synthesize(&self, vt: &mut Vt, _: StyleChain) -> Content {
|
fn synthesize(&mut self, vt: &Vt, _: StyleChain) {
|
||||||
let matches = vt
|
let matches = vt
|
||||||
.locate(Selector::Label(self.label()))
|
.locate(Selector::Label(self.target()))
|
||||||
.into_iter()
|
|
||||||
.map(|(_, node)| node.clone())
|
.map(|(_, node)| node.clone())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.clone().with_matches(matches).pack()
|
self.push_matches(matches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ pub use typst::eval::{
|
|||||||
pub use typst::geom::*;
|
pub use typst::geom::*;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::model::{
|
pub use typst::model::{
|
||||||
node, Construct, Content, Finalize, Fold, Introspector, Label, Node, NodeId, Resolve,
|
node, Construct, Content, Finalize, Fold, Introspector, Label, Locatable, Node,
|
||||||
Selector, Set, Show, StabilityProvider, StyleChain, StyleMap, StyleVec, Synthesize,
|
NodeId, Resolve, Selector, Set, Show, StabilityProvider, StableId, StyleChain,
|
||||||
Unlabellable, Vt,
|
StyleMap, StyleVec, Synthesize, Unlabellable, Vt,
|
||||||
};
|
};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::syntax::{Span, Spanned};
|
pub use typst::syntax::{Span, Spanned};
|
||||||
|
@ -42,7 +42,7 @@ impl ContentExt for Content {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn linked(self, dest: Destination) -> Self {
|
fn linked(self, dest: Destination) -> Self {
|
||||||
self.styled(MetaNode::set_data(vec![Meta::Link(dest.clone())]))
|
self.styled(MetaNode::set_data(vec![Meta::Link(dest)]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn aligned(self, aligns: Axes<Option<GenAlign>>) -> Self {
|
fn aligned(self, aligns: Axes<Option<GenAlign>>) -> Self {
|
||||||
|
@ -121,8 +121,8 @@ impl RawNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Synthesize for RawNode {
|
impl Synthesize for RawNode {
|
||||||
fn synthesize(&self, _: &mut Vt, styles: StyleChain) -> Content {
|
fn synthesize(&mut self, _: &Vt, styles: StyleChain) {
|
||||||
self.clone().with_lang(self.lang(styles)).pack()
|
self.push_lang(self.lang(styles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ struct Field {
|
|||||||
ident: Ident,
|
ident: Ident,
|
||||||
ident_in: Ident,
|
ident_in: Ident,
|
||||||
with_ident: Ident,
|
with_ident: Ident,
|
||||||
|
push_ident: Ident,
|
||||||
set_ident: Ident,
|
set_ident: Ident,
|
||||||
ty: syn::Type,
|
ty: syn::Type,
|
||||||
output: syn::Type,
|
output: syn::Type,
|
||||||
@ -81,6 +82,10 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Node> {
|
|||||||
let required = has_attr(&mut attrs, "required") || variadic;
|
let required = has_attr(&mut attrs, "required") || variadic;
|
||||||
let positional = has_attr(&mut attrs, "positional") || required;
|
let positional = has_attr(&mut attrs, "positional") || required;
|
||||||
|
|
||||||
|
if ident == "label" {
|
||||||
|
bail!(ident, "invalid field name");
|
||||||
|
}
|
||||||
|
|
||||||
let mut field = Field {
|
let mut field = Field {
|
||||||
name: kebab_case(&ident),
|
name: kebab_case(&ident),
|
||||||
docs: documentation(&attrs),
|
docs: documentation(&attrs),
|
||||||
@ -100,6 +105,7 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Node> {
|
|||||||
ident: ident.clone(),
|
ident: ident.clone(),
|
||||||
ident_in: Ident::new(&format!("{}_in", ident), ident.span()),
|
ident_in: Ident::new(&format!("{}_in", ident), ident.span()),
|
||||||
with_ident: Ident::new(&format!("with_{}", ident), ident.span()),
|
with_ident: Ident::new(&format!("with_{}", ident), ident.span()),
|
||||||
|
push_ident: Ident::new(&format!("push_{}", ident), ident.span()),
|
||||||
set_ident: Ident::new(&format!("set_{}", ident), ident.span()),
|
set_ident: Ident::new(&format!("set_{}", ident), ident.span()),
|
||||||
ty: field.ty.clone(),
|
ty: field.ty.clone(),
|
||||||
output: field.ty.clone(),
|
output: field.ty.clone(),
|
||||||
@ -162,17 +168,23 @@ fn create(node: &Node) -> TokenStream {
|
|||||||
let new = create_new_func(node);
|
let new = create_new_func(node);
|
||||||
let field_methods = all.clone().map(create_field_method);
|
let field_methods = all.clone().map(create_field_method);
|
||||||
let field_in_methods = settable.clone().map(create_field_in_method);
|
let field_in_methods = settable.clone().map(create_field_in_method);
|
||||||
let with_fields_methods = all.map(create_with_field_method);
|
let with_field_methods = all.clone().map(create_with_field_method);
|
||||||
|
let push_field_methods = all.map(create_push_field_method);
|
||||||
let field_style_methods = settable.map(create_set_field_method);
|
let field_style_methods = settable.map(create_set_field_method);
|
||||||
|
|
||||||
// Trait implementations.
|
// Trait implementations.
|
||||||
let construct = node
|
let node_impl = create_node_impl(node);
|
||||||
|
let construct_impl = node
|
||||||
.capable
|
.capable
|
||||||
.iter()
|
.iter()
|
||||||
.all(|capability| capability != "Construct")
|
.all(|capability| capability != "Construct")
|
||||||
.then(|| create_construct_impl(node));
|
.then(|| create_construct_impl(node));
|
||||||
let set = create_set_impl(node);
|
let set_impl = create_set_impl(node);
|
||||||
let node = create_node_impl(node);
|
let locatable_impl = node
|
||||||
|
.capable
|
||||||
|
.iter()
|
||||||
|
.any(|capability| capability == "Locatable")
|
||||||
|
.then(|| quote! { impl ::typst::model::Locatable for #ident {} });
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #docs]
|
#[doc = #docs]
|
||||||
@ -184,7 +196,8 @@ fn create(node: &Node) -> TokenStream {
|
|||||||
#new
|
#new
|
||||||
#(#field_methods)*
|
#(#field_methods)*
|
||||||
#(#field_in_methods)*
|
#(#field_in_methods)*
|
||||||
#(#with_fields_methods)*
|
#(#with_field_methods)*
|
||||||
|
#(#push_field_methods)*
|
||||||
#(#field_style_methods)*
|
#(#field_style_methods)*
|
||||||
|
|
||||||
/// The node's span.
|
/// The node's span.
|
||||||
@ -193,9 +206,10 @@ fn create(node: &Node) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#node
|
#node_impl
|
||||||
#construct
|
#construct_impl
|
||||||
#set
|
#set_impl
|
||||||
|
#locatable_impl
|
||||||
|
|
||||||
impl From<#ident> for ::typst::eval::Value {
|
impl From<#ident> for ::typst::eval::Value {
|
||||||
fn from(value: #ident) -> Self {
|
fn from(value: #ident) -> Self {
|
||||||
@ -232,6 +246,7 @@ fn create_field_method(field: &Field) -> TokenStream {
|
|||||||
if field.inherent() || field.synthesized {
|
if field.inherent() || field.synthesized {
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #docs]
|
#[doc = #docs]
|
||||||
|
#[track_caller]
|
||||||
#vis fn #ident(&self) -> #output {
|
#vis fn #ident(&self) -> #output {
|
||||||
self.0.expect_field(#name)
|
self.0.expect_field(#name)
|
||||||
}
|
}
|
||||||
@ -293,6 +308,18 @@ fn create_with_field_method(field: &Field) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a set-style method for a field.
|
||||||
|
fn create_push_field_method(field: &Field) -> TokenStream {
|
||||||
|
let Field { vis, ident, push_ident, name, ty, .. } = field;
|
||||||
|
let doc = format!("Push the [`{}`](Self::{}) field.", name, ident);
|
||||||
|
quote! {
|
||||||
|
#[doc = #doc]
|
||||||
|
#vis fn #push_ident(&mut self, #ident: #ty) {
|
||||||
|
self.0.push_field(#name, #ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a setter method for a field.
|
/// Create a setter method for a field.
|
||||||
fn create_set_field_method(field: &Field) -> TokenStream {
|
fn create_set_field_method(field: &Field) -> TokenStream {
|
||||||
let Field { vis, ident, set_ident, name, ty, .. } = field;
|
let Field { vis, ident, set_ident, name, ty, .. } = field;
|
||||||
|
@ -8,7 +8,7 @@ use comemo::Tracked;
|
|||||||
use ecow::{eco_format, EcoString, EcoVec};
|
use ecow::{eco_format, EcoString, EcoVec};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use super::{node, Guard, Recipe, Style, StyleMap};
|
use super::{node, Guard, Locatable, Recipe, StableId, Style, StyleMap, Synthesize};
|
||||||
use crate::diag::{SourceResult, StrResult};
|
use crate::diag::{SourceResult, StrResult};
|
||||||
use crate::eval::{
|
use crate::eval::{
|
||||||
cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm,
|
cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm,
|
||||||
@ -29,8 +29,9 @@ pub struct Content {
|
|||||||
/// Modifiers that can be attached to content.
|
/// Modifiers that can be attached to content.
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
enum Modifier {
|
enum Modifier {
|
||||||
Synthesized,
|
Prepared,
|
||||||
Guard(Guard),
|
Guard(Guard),
|
||||||
|
Id(StableId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Content {
|
impl Content {
|
||||||
@ -101,6 +102,16 @@ impl Content {
|
|||||||
Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) })
|
Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cast to a trait object if this content has the given capability.
|
||||||
|
pub fn with_mut<C>(&mut self) -> Option<&mut C>
|
||||||
|
where
|
||||||
|
C: ?Sized + 'static,
|
||||||
|
{
|
||||||
|
let vtable = (self.id.0.vtable)(TypeId::of::<C>())?;
|
||||||
|
let data = self as *mut Self as *mut ();
|
||||||
|
Some(unsafe { &mut *crate::util::fat::from_raw_parts_mut(data, vtable) })
|
||||||
|
}
|
||||||
|
|
||||||
/// The node's span.
|
/// The node's span.
|
||||||
pub fn span(&self) -> Span {
|
pub fn span(&self) -> Span {
|
||||||
self.span
|
self.span
|
||||||
@ -233,17 +244,6 @@ impl Content {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark this content as prepared.
|
|
||||||
pub fn synthesized(mut self) -> Self {
|
|
||||||
self.modifiers.push(Modifier::Synthesized);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this node was prepared.
|
|
||||||
pub fn is_synthesized(&self) -> bool {
|
|
||||||
self.modifiers.contains(&Modifier::Synthesized)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether no show rule was executed for this node so far.
|
/// Whether no show rule was executed for this node so far.
|
||||||
pub(super) fn is_pristine(&self) -> bool {
|
pub(super) fn is_pristine(&self) -> bool {
|
||||||
!self
|
!self
|
||||||
@ -257,6 +257,37 @@ impl Content {
|
|||||||
self.modifiers.contains(&Modifier::Guard(id))
|
self.modifiers.contains(&Modifier::Guard(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this node was prepared.
|
||||||
|
pub fn is_prepared(&self) -> bool {
|
||||||
|
self.modifiers.contains(&Modifier::Prepared)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the node needs to be realized specially.
|
||||||
|
pub fn needs_preparation(&self) -> bool {
|
||||||
|
(self.can::<dyn Locatable>()
|
||||||
|
|| self.can::<dyn Synthesize>()
|
||||||
|
|| self.label().is_some())
|
||||||
|
&& !self.is_prepared()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark this content as prepared.
|
||||||
|
pub fn mark_prepared(&mut self) {
|
||||||
|
self.modifiers.push(Modifier::Prepared);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attach a stable id to this content.
|
||||||
|
pub fn set_stable_id(&mut self, id: StableId) {
|
||||||
|
self.modifiers.push(Modifier::Id(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This content's stable identifier.
|
||||||
|
pub fn stable_id(&self) -> Option<StableId> {
|
||||||
|
self.modifiers.iter().find_map(|modifier| match modifier {
|
||||||
|
Modifier::Id(id) => Some(*id),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy the modifiers from another piece of content.
|
/// Copy the modifiers from another piece of content.
|
||||||
pub(super) fn copy_modifiers(&mut self, from: &Content) {
|
pub(super) fn copy_modifiers(&mut self, from: &Content) {
|
||||||
self.span = from.span;
|
self.span = from.span;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use super::{Content, NodeId, Recipe, Selector, StyleChain, Vt};
|
use super::{Content, NodeId, Recipe, Selector, StyleChain, Vt};
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
|
use crate::doc::{Meta, MetaNode};
|
||||||
|
|
||||||
/// Whether the target is affected by show rules in the given style chain.
|
/// Whether the target is affected by show rules in the given style chain.
|
||||||
pub fn applicable(target: &Content, styles: StyleChain) -> bool {
|
pub fn applicable(target: &Content, styles: StyleChain) -> bool {
|
||||||
if target.can::<dyn Synthesize>() && !target.is_synthesized() {
|
if target.needs_preparation() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,21 +32,31 @@ pub fn realize(
|
|||||||
target: &Content,
|
target: &Content,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Option<Content>> {
|
) -> SourceResult<Option<Content>> {
|
||||||
|
// Pre-process.
|
||||||
|
if target.needs_preparation() {
|
||||||
|
let mut node = target.clone();
|
||||||
|
if target.can::<dyn Locatable>() || target.label().is_some() {
|
||||||
|
let id = vt.identify(target);
|
||||||
|
node.set_stable_id(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(node) = node.with_mut::<dyn Synthesize>() {
|
||||||
|
node.synthesize(vt, styles);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.mark_prepared();
|
||||||
|
|
||||||
|
if let Some(id) = node.stable_id() {
|
||||||
|
let meta = Meta::Node(id, node.clone());
|
||||||
|
return Ok(Some(node.styled(MetaNode::set_data(vec![meta]))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Some(node));
|
||||||
|
}
|
||||||
|
|
||||||
// Find out how many recipes there are.
|
// Find out how many recipes there are.
|
||||||
let mut n = styles.recipes().count();
|
let mut n = styles.recipes().count();
|
||||||
|
|
||||||
// Synthesize if not already happened for this node.
|
|
||||||
if target.can::<dyn Synthesize>() && !target.is_synthesized() {
|
|
||||||
return Ok(Some(
|
|
||||||
target
|
|
||||||
.clone()
|
|
||||||
.synthesized()
|
|
||||||
.with::<dyn Synthesize>()
|
|
||||||
.unwrap()
|
|
||||||
.synthesize(vt, styles),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find an applicable recipe.
|
// Find an applicable recipe.
|
||||||
let mut realized = None;
|
let mut realized = None;
|
||||||
for recipe in styles.recipes() {
|
for recipe in styles.recipes() {
|
||||||
@ -144,10 +155,13 @@ fn try_apply(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes this node locatable through `vt.locate`.
|
||||||
|
pub trait Locatable {}
|
||||||
|
|
||||||
/// Synthesize fields on a node. This happens before execution of any show rule.
|
/// Synthesize fields on a node. This happens before execution of any show rule.
|
||||||
pub trait Synthesize {
|
pub trait Synthesize {
|
||||||
/// Prepare the node for show rule application.
|
/// Prepare the node for show rule application.
|
||||||
fn synthesize(&self, vt: &mut Vt, styles: StyleChain) -> Content;
|
fn synthesize(&mut self, vt: &Vt, styles: StyleChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The base recipe for a node.
|
/// The base recipe for a node.
|
||||||
|
@ -5,7 +5,7 @@ use std::num::NonZeroUsize;
|
|||||||
|
|
||||||
use comemo::{Track, Tracked, TrackedMut};
|
use comemo::{Track, Tracked, TrackedMut};
|
||||||
|
|
||||||
use super::{Content, Selector, StyleChain};
|
use super::{Content, Node, Selector, StyleChain};
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::doc::{Document, Element, Frame, Location, Meta};
|
use crate::doc::{Document, Element, Frame, Location, Meta};
|
||||||
use crate::geom::Transform;
|
use crate::geom::Transform;
|
||||||
@ -83,8 +83,17 @@ impl<'a> Vt<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Locate all metadata matches for the given selector.
|
/// Locate all metadata matches for the given selector.
|
||||||
pub fn locate(&self, selector: Selector) -> Vec<(StableId, &Content)> {
|
pub fn locate(
|
||||||
self.introspector.locate(selector)
|
&self,
|
||||||
|
selector: Selector,
|
||||||
|
) -> impl Iterator<Item = (StableId, &Content)> {
|
||||||
|
self.introspector.locate(selector).into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Locate all metadata matches for the given node.
|
||||||
|
pub fn locate_node<T: Node>(&self) -> impl Iterator<Item = (StableId, &T)> {
|
||||||
|
self.locate(Selector::node::<T>())
|
||||||
|
.map(|(id, content)| (id, content.to::<T>().unwrap()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +51,8 @@ Hello *#x*
|
|||||||
---
|
---
|
||||||
// Test conditional set.
|
// Test conditional set.
|
||||||
#show ref: it => {
|
#show ref: it => {
|
||||||
set text(red) if it.label == <unknown>
|
set text(red) if it.target == <unknown>
|
||||||
"@" + str(it.label)
|
"@" + str(it.target)
|
||||||
}
|
}
|
||||||
|
|
||||||
@hello from the @unknown
|
@hello from the @unknown
|
||||||
|
Loading…
x
Reference in New Issue
Block a user