Remove classes and improve naming
This commit is contained in:
parent
5ac7eb3860
commit
2890a156d2
@ -8,15 +8,16 @@ use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Error, Ident, Result};
|
||||
|
||||
/// Turn a node into a class.
|
||||
#[proc_macro_attribute]
|
||||
pub fn class(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||
pub fn node(stream: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let impl_block = syn::parse_macro_input!(item as syn::ItemImpl);
|
||||
expand(impl_block).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||
expand(TokenStream2::from(stream), impl_block)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Expand an impl block for a node.
|
||||
fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
|
||||
fn expand(stream: TokenStream2, mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
|
||||
// Split the node type into name and generic type arguments.
|
||||
let params = &impl_block.generics.params;
|
||||
let self_ty = &*impl_block.self_ty;
|
||||
@ -77,13 +78,20 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
|
||||
});
|
||||
|
||||
parse_quote! {
|
||||
fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
|
||||
fn set(args: &mut Args) -> TypResult<StyleMap> {
|
||||
let mut styles = StyleMap::new();
|
||||
#(#sets)*
|
||||
Ok(())
|
||||
Ok(styles)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let showable = match stream.to_string().as_str() {
|
||||
"" => false,
|
||||
"showable" => true,
|
||||
_ => return Err(Error::new(stream.span(), "unrecognized argument")),
|
||||
};
|
||||
|
||||
// Put everything into a module with a hopefully unique type to isolate
|
||||
// it from the outside.
|
||||
Ok(quote! {
|
||||
@ -92,16 +100,14 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
|
||||
use std::any::TypeId;
|
||||
use std::marker::PhantomData;
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::eval::{Construct, Nonfolding, Property, Set};
|
||||
use crate::eval;
|
||||
use super::*;
|
||||
|
||||
#impl_block
|
||||
|
||||
impl<#params> Construct for #self_ty {
|
||||
impl<#params> eval::Node for #self_ty {
|
||||
const SHOWABLE: bool = #showable;
|
||||
#construct
|
||||
}
|
||||
|
||||
impl<#params> Set for #self_ty {
|
||||
#set
|
||||
}
|
||||
|
||||
@ -206,8 +212,8 @@ fn process_const(
|
||||
));
|
||||
}
|
||||
|
||||
let nonfolding = fold.is_none().then(|| {
|
||||
quote! { impl<#params> Nonfolding for #key {} }
|
||||
let referencable = fold.is_none().then(|| {
|
||||
quote! { impl<#params> eval::Referencable for #key {} }
|
||||
});
|
||||
|
||||
// Generate the module code.
|
||||
@ -226,7 +232,7 @@ fn process_const(
|
||||
}
|
||||
}
|
||||
|
||||
impl<#params> Property for #key {
|
||||
impl<#params> eval::Key for #key {
|
||||
type Value = #value_ty;
|
||||
|
||||
const NAME: &'static str = #name;
|
||||
@ -247,7 +253,7 @@ fn process_const(
|
||||
#fold
|
||||
}
|
||||
|
||||
#nonfolding
|
||||
#referencable
|
||||
}
|
||||
};
|
||||
|
||||
|
202
src/eval/args.rs
Normal file
202
src/eval/args.rs
Normal file
@ -0,0 +1,202 @@
|
||||
use std::fmt::{self, Debug, Formatter, Write};
|
||||
|
||||
use super::{Cast, Value};
|
||||
use crate::diag::{At, TypResult};
|
||||
use crate::syntax::{Span, Spanned};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// Evaluated arguments to a function.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct Args {
|
||||
/// The span of the whole argument list.
|
||||
pub span: Span,
|
||||
/// The positional and named arguments.
|
||||
pub items: Vec<Arg>,
|
||||
}
|
||||
|
||||
/// An argument to a function call: `12` or `draw: false`.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct Arg {
|
||||
/// The span of the whole argument.
|
||||
pub span: Span,
|
||||
/// The name of the argument (`None` for positional arguments).
|
||||
pub name: Option<EcoString>,
|
||||
/// The value of the argument.
|
||||
pub value: Spanned<Value>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
/// Create positional arguments from a span and values.
|
||||
pub fn from_values(span: Span, values: impl IntoIterator<Item = Value>) -> Self {
|
||||
Self {
|
||||
span,
|
||||
items: values
|
||||
.into_iter()
|
||||
.map(|value| Arg {
|
||||
span,
|
||||
name: None,
|
||||
value: Spanned::new(value, span),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume and cast the first positional argument.
|
||||
///
|
||||
/// Returns a `missing argument: {what}` error if no positional argument is
|
||||
/// left.
|
||||
pub fn expect<T>(&mut self, what: &str) -> TypResult<T>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
match self.eat()? {
|
||||
Some(v) => Ok(v),
|
||||
None => bail!(self.span, "missing argument: {}", what),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume and cast the first positional argument if there is one.
|
||||
pub fn eat<T>(&mut self) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
for (i, slot) in self.items.iter().enumerate() {
|
||||
if slot.name.is_none() {
|
||||
let value = self.items.remove(i).value;
|
||||
let span = value.span;
|
||||
return T::cast(value).at(span).map(Some);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Find and consume the first castable positional argument.
|
||||
pub fn find<T>(&mut self) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
for (i, slot) in self.items.iter().enumerate() {
|
||||
if slot.name.is_none() && T::is(&slot.value) {
|
||||
let value = self.items.remove(i).value;
|
||||
let span = value.span;
|
||||
return T::cast(value).at(span).map(Some);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Find and consume all castable positional arguments.
|
||||
pub fn all<T>(&mut self) -> TypResult<Vec<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
let mut list = vec![];
|
||||
while let Some(value) = self.find()? {
|
||||
list.push(value);
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
/// Cast and remove the value for the given named argument, returning an
|
||||
/// error if the conversion fails.
|
||||
pub fn named<T>(&mut self, name: &str) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
// We don't quit once we have a match because when multiple matches
|
||||
// exist, we want to remove all of them and use the last one.
|
||||
let mut i = 0;
|
||||
let mut found = None;
|
||||
while i < self.items.len() {
|
||||
if self.items[i].name.as_deref() == Some(name) {
|
||||
let value = self.items.remove(i).value;
|
||||
let span = value.span;
|
||||
found = Some(T::cast(value).at(span)?);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
Ok(found)
|
||||
}
|
||||
|
||||
/// Same as named, but with fallback to find.
|
||||
pub fn named_or_find<T>(&mut self, name: &str) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
match self.named(name)? {
|
||||
Some(value) => Ok(Some(value)),
|
||||
None => self.find(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Take out all arguments into a new instance.
|
||||
pub fn take(&mut self) -> Self {
|
||||
Self {
|
||||
span: self.span,
|
||||
items: std::mem::take(&mut self.items),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an "unexpected argument" error if there is any remaining
|
||||
/// argument.
|
||||
pub fn finish(self) -> TypResult<()> {
|
||||
if let Some(arg) = self.items.first() {
|
||||
bail!(arg.span, "unexpected argument");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reinterpret these arguments as actually being an array index.
|
||||
pub fn into_index(self) -> TypResult<i64> {
|
||||
self.into_castable("index")
|
||||
}
|
||||
|
||||
/// Reinterpret these arguments as actually being a dictionary key.
|
||||
pub fn into_key(self) -> TypResult<EcoString> {
|
||||
self.into_castable("key")
|
||||
}
|
||||
|
||||
/// Reinterpret these arguments as actually being a single castable thing.
|
||||
fn into_castable<T: Cast>(self, what: &str) -> TypResult<T> {
|
||||
let mut iter = self.items.into_iter();
|
||||
let value = match iter.next() {
|
||||
Some(Arg { name: None, value, .. }) => value.v.cast().at(value.span)?,
|
||||
None => {
|
||||
bail!(self.span, "missing {}", what);
|
||||
}
|
||||
Some(Arg { name: Some(_), span, .. }) => {
|
||||
bail!(span, "named pair is not allowed here");
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(arg) = iter.next() {
|
||||
bail!(arg.span, "only one {} is allowed", what);
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Args {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write_char('(')?;
|
||||
for (i, arg) in self.items.iter().enumerate() {
|
||||
arg.fmt(f)?;
|
||||
if i + 1 < self.items.len() {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
}
|
||||
f.write_char(')')
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Arg {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
if let Some(name) = &self.name {
|
||||
f.write_str(name)?;
|
||||
f.write_str(": ")?;
|
||||
}
|
||||
Debug::fmt(&self.value.v, f)
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
use std::any::TypeId;
|
||||
use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use super::{Args, Content, Func, StyleMap, Value};
|
||||
use crate::diag::TypResult;
|
||||
use crate::Context;
|
||||
|
||||
/// A class of nodes.
|
||||
///
|
||||
/// You can [construct] an instance of a class in Typst code by invoking the
|
||||
/// class as a callable. This always produces a content value, but not
|
||||
/// necessarily a simple inline or block node. For example, the `text`
|
||||
/// constructor does not actually create a [`TextNode`]. Instead it applies
|
||||
/// styling to whatever content you pass in and returns it structurally
|
||||
/// unchanged.
|
||||
///
|
||||
/// The arguments you can pass to a class constructor fall into two categories:
|
||||
/// Data that is inherent to the instance (e.g. the text/content of a heading)
|
||||
/// and style properties (e.g. the fill color of a heading). As the latter are
|
||||
/// often shared by many instances throughout a document, they can also be
|
||||
/// conveniently configured through class's [`set`] rule. Then, they apply to
|
||||
/// all nodes that are instantiated into the content block where the `set` was
|
||||
/// executed.
|
||||
///
|
||||
/// ```typst
|
||||
/// This is normal.
|
||||
/// [
|
||||
/// #set text(weight: "bold")
|
||||
/// #set heading(fill: blue)
|
||||
/// = A blue & bold heading
|
||||
/// ]
|
||||
/// Normal again.
|
||||
/// ```
|
||||
///
|
||||
/// [construct]: Self::construct
|
||||
/// [`TextNode`]: crate::library::text::TextNode
|
||||
/// [`set`]: Self::set
|
||||
#[derive(Clone)]
|
||||
pub struct Class {
|
||||
name: &'static str,
|
||||
id: TypeId,
|
||||
construct: fn(&mut Context, &mut Args) -> TypResult<Value>,
|
||||
set: fn(&mut Args, &mut StyleMap) -> TypResult<()>,
|
||||
}
|
||||
|
||||
impl Class {
|
||||
/// Create a new class.
|
||||
pub fn new<T>(name: &'static str) -> Self
|
||||
where
|
||||
T: Construct + Set + 'static,
|
||||
{
|
||||
Self {
|
||||
name,
|
||||
id: TypeId::of::<T>(),
|
||||
construct: |ctx, args| {
|
||||
let mut styles = StyleMap::new();
|
||||
T::set(args, &mut styles)?;
|
||||
let content = T::construct(ctx, args)?;
|
||||
Ok(Value::Content(content.styled_with_map(styles.scoped())))
|
||||
},
|
||||
set: T::set,
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of the class.
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// The type id of the class.
|
||||
pub fn id(&self) -> TypeId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Return the class constructor as a function.
|
||||
pub fn constructor(&self) -> Func {
|
||||
Func::native(self.name, self.construct)
|
||||
}
|
||||
|
||||
/// Construct an instance of the class.
|
||||
///
|
||||
/// This parses both property and data arguments (in this order), styles the
|
||||
/// content constructed from the data with the style properties and wraps it
|
||||
/// in a value.
|
||||
pub fn construct(&self, ctx: &mut Context, mut args: Args) -> TypResult<Value> {
|
||||
let value = (self.construct)(ctx, &mut args)?;
|
||||
args.finish()?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Execute the class's set rule.
|
||||
///
|
||||
/// This parses property arguments and return the resulting styles.
|
||||
pub fn set(&self, mut args: Args) -> TypResult<StyleMap> {
|
||||
let mut styles = StyleMap::new();
|
||||
(self.set)(&mut args, &mut styles)?;
|
||||
args.finish()?;
|
||||
Ok(styles)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Class {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write_str("<class ")?;
|
||||
f.write_str(self.name())?;
|
||||
f.write_char('>')
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Class {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Class {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(self.construct as usize).hash(state);
|
||||
(self.set as usize).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct an instance of a class.
|
||||
pub trait Construct {
|
||||
/// Construct an instance of this class from the arguments.
|
||||
///
|
||||
/// This is passed only the arguments that remain after execution of the
|
||||
/// class's set rule.
|
||||
fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>;
|
||||
}
|
||||
|
||||
/// Set style properties of a class.
|
||||
pub trait Set {
|
||||
/// Parse the arguments and insert style properties of this class into the
|
||||
/// given style map.
|
||||
fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()>;
|
||||
}
|
@ -6,11 +6,11 @@ use std::ops::{Add, AddAssign};
|
||||
use typed_arena::Arena;
|
||||
|
||||
use super::{
|
||||
CollapsingBuilder, Interruption, Layout, LayoutNode, Property, Show, ShowNode,
|
||||
StyleMap, StyleVecBuilder,
|
||||
CollapsingBuilder, Interruption, Key, Layout, LayoutNode, Show, ShowNode, StyleMap,
|
||||
StyleVecBuilder,
|
||||
};
|
||||
use crate::diag::StrResult;
|
||||
use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, SpacingKind};
|
||||
use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, Spacing};
|
||||
use crate::library::prelude::*;
|
||||
use crate::library::structure::{ListItem, ListKind, ListNode, ORDERED, UNORDERED};
|
||||
use crate::library::text::{DecoNode, ParChild, ParNode, TextNode, UNDERLINE};
|
||||
@ -42,7 +42,7 @@ pub enum Content {
|
||||
/// A line break.
|
||||
Linebreak,
|
||||
/// Horizontal spacing.
|
||||
Horizontal(SpacingKind),
|
||||
Horizontal(Spacing),
|
||||
/// Plain text.
|
||||
Text(EcoString),
|
||||
/// An inline-level node.
|
||||
@ -52,7 +52,7 @@ pub enum Content {
|
||||
/// A column break.
|
||||
Colbreak,
|
||||
/// Vertical spacing.
|
||||
Vertical(SpacingKind),
|
||||
Vertical(Spacing),
|
||||
/// A block-level node.
|
||||
Block(LayoutNode),
|
||||
/// An item in an unordered list.
|
||||
@ -102,7 +102,7 @@ impl Content {
|
||||
}
|
||||
|
||||
/// Style this content with a single style property.
|
||||
pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self {
|
||||
pub fn styled<P: Key>(mut self, key: P, value: P::Value) -> Self {
|
||||
if let Self::Styled(styled) = &mut self {
|
||||
if let Some((_, map)) = Arc::get_mut(styled) {
|
||||
if !map.has_scoped() {
|
||||
@ -465,7 +465,7 @@ impl<'a> Builder<'a> {
|
||||
})
|
||||
.unwrap_or_default()
|
||||
{
|
||||
par.push_front(ParChild::Spacing(SpacingKind::Linear(indent)))
|
||||
par.push_front(ParChild::Spacing(Spacing::Linear(indent)))
|
||||
}
|
||||
|
||||
let node = ParNode(par).pack();
|
||||
|
282
src/eval/func.rs
282
src/eval/func.rs
@ -1,11 +1,12 @@
|
||||
use std::any::TypeId;
|
||||
use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{Cast, Control, Eval, Scope, Scopes, Value};
|
||||
use crate::diag::{At, TypResult};
|
||||
use super::{Args, Content, Control, Eval, Scope, Scopes, StyleMap, Value};
|
||||
use crate::diag::{StrResult, TypResult};
|
||||
use crate::syntax::ast::Expr;
|
||||
use crate::syntax::{Span, Spanned};
|
||||
use crate::syntax::Span;
|
||||
use crate::util::EcoString;
|
||||
use crate::Context;
|
||||
|
||||
@ -26,18 +27,50 @@ enum Repr {
|
||||
|
||||
impl Func {
|
||||
/// Create a new function from a native rust function.
|
||||
pub fn native(
|
||||
pub fn from_fn(
|
||||
name: &'static str,
|
||||
func: fn(&mut Context, &mut Args) -> TypResult<Value>,
|
||||
) -> Self {
|
||||
Self(Arc::new(Repr::Native(Native { name, func })))
|
||||
Self(Arc::new(Repr::Native(Native {
|
||||
name,
|
||||
func,
|
||||
set: None,
|
||||
show: None,
|
||||
})))
|
||||
}
|
||||
|
||||
/// Create a new function from a native rust node.
|
||||
pub fn from_node<T: Node>(name: &'static str) -> Self {
|
||||
Self(Arc::new(Repr::Native(Native {
|
||||
name,
|
||||
func: |ctx, args| {
|
||||
let styles = T::set(args)?;
|
||||
let content = T::construct(ctx, args)?;
|
||||
Ok(Value::Content(content.styled_with_map(styles.scoped())))
|
||||
},
|
||||
set: Some(T::set),
|
||||
show: if T::SHOWABLE {
|
||||
Some(|recipe, span| {
|
||||
let mut styles = StyleMap::new();
|
||||
styles.set_recipe(TypeId::of::<T>(), recipe, span);
|
||||
styles
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})))
|
||||
}
|
||||
|
||||
/// Create a new function from a closure.
|
||||
pub fn closure(closure: Closure) -> Self {
|
||||
pub fn from_closure(closure: Closure) -> Self {
|
||||
Self(Arc::new(Repr::Closure(closure)))
|
||||
}
|
||||
|
||||
/// Apply the given arguments to the function.
|
||||
pub fn with(self, args: Args) -> Self {
|
||||
Self(Arc::new(Repr::With(self, args)))
|
||||
}
|
||||
|
||||
/// The name of the function.
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
match self.0.as_ref() {
|
||||
@ -61,9 +94,22 @@ impl Func {
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Apply the given arguments to the function.
|
||||
pub fn with(self, args: Args) -> Self {
|
||||
Self(Arc::new(Repr::With(self, args)))
|
||||
/// Execute the function's set rule.
|
||||
pub fn set(&self, mut args: Args) -> TypResult<StyleMap> {
|
||||
let styles = match self.0.as_ref() {
|
||||
Repr::Native(Native { set: Some(set), .. }) => set(&mut args)?,
|
||||
_ => StyleMap::new(),
|
||||
};
|
||||
args.finish()?;
|
||||
Ok(styles)
|
||||
}
|
||||
|
||||
/// Execute the function's show rule.
|
||||
pub fn show(&self, recipe: Func, span: Span) -> StrResult<StyleMap> {
|
||||
match self.0.as_ref() {
|
||||
Repr::Native(Native { show: Some(show), .. }) => Ok(show(recipe, span)),
|
||||
_ => Err("this function cannot be customized with show")?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,14 +136,36 @@ struct Native {
|
||||
pub name: &'static str,
|
||||
/// The function pointer.
|
||||
pub func: fn(&mut Context, &mut Args) -> TypResult<Value>,
|
||||
/// The set rule.
|
||||
pub set: Option<fn(&mut Args) -> TypResult<StyleMap>>,
|
||||
/// The show rule.
|
||||
pub show: Option<fn(Func, Span) -> StyleMap>,
|
||||
}
|
||||
|
||||
impl Hash for Native {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
(self.func as usize).hash(state);
|
||||
self.set.map(|set| set as usize).hash(state);
|
||||
self.show.map(|show| show as usize).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// A constructable, stylable content node.
|
||||
pub trait Node: 'static {
|
||||
/// Whether this node can be customized through a show rule.
|
||||
const SHOWABLE: bool;
|
||||
|
||||
/// Construct a node from the arguments.
|
||||
///
|
||||
/// This is passed only the arguments that remain after execution of the
|
||||
/// node's set rule.
|
||||
fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>;
|
||||
|
||||
/// Parse the arguments into style properties for this node.
|
||||
fn set(args: &mut Args) -> TypResult<StyleMap>;
|
||||
}
|
||||
|
||||
/// A user-defined closure.
|
||||
#[derive(Hash)]
|
||||
pub struct Closure {
|
||||
@ -146,199 +214,3 @@ impl Closure {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluated arguments to a function.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct Args {
|
||||
/// The span of the whole argument list.
|
||||
pub span: Span,
|
||||
/// The positional and named arguments.
|
||||
pub items: Vec<Arg>,
|
||||
}
|
||||
|
||||
/// An argument to a function call: `12` or `draw: false`.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct Arg {
|
||||
/// The span of the whole argument.
|
||||
pub span: Span,
|
||||
/// The name of the argument (`None` for positional arguments).
|
||||
pub name: Option<EcoString>,
|
||||
/// The value of the argument.
|
||||
pub value: Spanned<Value>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
/// Create positional arguments from a span and values.
|
||||
pub fn from_values(span: Span, values: impl IntoIterator<Item = Value>) -> Self {
|
||||
Self {
|
||||
span,
|
||||
items: values
|
||||
.into_iter()
|
||||
.map(|value| Arg {
|
||||
span,
|
||||
name: None,
|
||||
value: Spanned::new(value, span),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume and cast the first positional argument.
|
||||
///
|
||||
/// Returns a `missing argument: {what}` error if no positional argument is
|
||||
/// left.
|
||||
pub fn expect<T>(&mut self, what: &str) -> TypResult<T>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
match self.eat()? {
|
||||
Some(v) => Ok(v),
|
||||
None => bail!(self.span, "missing argument: {}", what),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume and cast the first positional argument if there is one.
|
||||
pub fn eat<T>(&mut self) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
for (i, slot) in self.items.iter().enumerate() {
|
||||
if slot.name.is_none() {
|
||||
let value = self.items.remove(i).value;
|
||||
let span = value.span;
|
||||
return T::cast(value).at(span).map(Some);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Find and consume the first castable positional argument.
|
||||
pub fn find<T>(&mut self) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
for (i, slot) in self.items.iter().enumerate() {
|
||||
if slot.name.is_none() && T::is(&slot.value) {
|
||||
let value = self.items.remove(i).value;
|
||||
let span = value.span;
|
||||
return T::cast(value).at(span).map(Some);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Find and consume all castable positional arguments.
|
||||
pub fn all<T>(&mut self) -> TypResult<Vec<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
let mut list = vec![];
|
||||
while let Some(value) = self.find()? {
|
||||
list.push(value);
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
/// Cast and remove the value for the given named argument, returning an
|
||||
/// error if the conversion fails.
|
||||
pub fn named<T>(&mut self, name: &str) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
// We don't quit once we have a match because when multiple matches
|
||||
// exist, we want to remove all of them and use the last one.
|
||||
let mut i = 0;
|
||||
let mut found = None;
|
||||
while i < self.items.len() {
|
||||
if self.items[i].name.as_deref() == Some(name) {
|
||||
let value = self.items.remove(i).value;
|
||||
let span = value.span;
|
||||
found = Some(T::cast(value).at(span)?);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
Ok(found)
|
||||
}
|
||||
|
||||
/// Same as named, but with fallback to find.
|
||||
pub fn named_or_find<T>(&mut self, name: &str) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
match self.named(name)? {
|
||||
Some(value) => Ok(Some(value)),
|
||||
None => self.find(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Take out all arguments into a new instance.
|
||||
pub fn take(&mut self) -> Self {
|
||||
Self {
|
||||
span: self.span,
|
||||
items: std::mem::take(&mut self.items),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an "unexpected argument" error if there is any remaining
|
||||
/// argument.
|
||||
pub fn finish(self) -> TypResult<()> {
|
||||
if let Some(arg) = self.items.first() {
|
||||
bail!(arg.span, "unexpected argument");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reinterpret these arguments as actually being an array index.
|
||||
pub fn into_index(self) -> TypResult<i64> {
|
||||
self.into_castable("index")
|
||||
}
|
||||
|
||||
/// Reinterpret these arguments as actually being a dictionary key.
|
||||
pub fn into_key(self) -> TypResult<EcoString> {
|
||||
self.into_castable("key")
|
||||
}
|
||||
|
||||
/// Reinterpret these arguments as actually being a single castable thing.
|
||||
fn into_castable<T: Cast>(self, what: &str) -> TypResult<T> {
|
||||
let mut iter = self.items.into_iter();
|
||||
let value = match iter.next() {
|
||||
Some(Arg { name: None, value, .. }) => value.v.cast().at(value.span)?,
|
||||
None => {
|
||||
bail!(self.span, "missing {}", what);
|
||||
}
|
||||
Some(Arg { name: Some(_), span, .. }) => {
|
||||
bail!(span, "named pair is not allowed here");
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(arg) = iter.next() {
|
||||
bail!(arg.span, "only one {} is allowed", what);
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Args {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write_char('(')?;
|
||||
for (i, arg) in self.items.iter().enumerate() {
|
||||
arg.fmt(f)?;
|
||||
if i + 1 < self.items.len() {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
}
|
||||
f.write_char(')')
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Arg {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
if let Some(name) = &self.name {
|
||||
f.write_str(name)?;
|
||||
f.write_str(": ")?;
|
||||
}
|
||||
Debug::fmt(&self.value.v, f)
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ mod dict;
|
||||
mod value;
|
||||
#[macro_use]
|
||||
mod styles;
|
||||
mod args;
|
||||
mod capture;
|
||||
mod class;
|
||||
mod collapse;
|
||||
mod content;
|
||||
mod control;
|
||||
@ -20,9 +20,9 @@ mod ops;
|
||||
mod scope;
|
||||
mod show;
|
||||
|
||||
pub use args::*;
|
||||
pub use array::*;
|
||||
pub use capture::*;
|
||||
pub use class::*;
|
||||
pub use collapse::*;
|
||||
pub use content::*;
|
||||
pub use control::*;
|
||||
@ -417,11 +417,6 @@ impl Eval for CallExpr {
|
||||
func.call(ctx, args).trace(point, self.span())
|
||||
}
|
||||
|
||||
Value::Class(class) => {
|
||||
let point = || Tracepoint::Call(Some(class.name().to_string()));
|
||||
class.construct(ctx, args).trace(point, self.span())
|
||||
}
|
||||
|
||||
v => bail!(
|
||||
span,
|
||||
"expected callable or collection, found {}",
|
||||
@ -520,7 +515,7 @@ impl Eval for ClosureExpr {
|
||||
}
|
||||
|
||||
// Define the actual function.
|
||||
Ok(Value::Func(Func::closure(Closure {
|
||||
Ok(Value::Func(Func::from_closure(Closure {
|
||||
name,
|
||||
captured,
|
||||
params,
|
||||
@ -558,10 +553,10 @@ impl Eval for SetExpr {
|
||||
type Output = StyleMap;
|
||||
|
||||
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
|
||||
let class = self.class();
|
||||
let class = class.eval(ctx, scp)?.cast::<Class>().at(class.span())?;
|
||||
let target = self.target();
|
||||
let target = target.eval(ctx, scp)?.cast::<Func>().at(target.span())?;
|
||||
let args = self.args().eval(ctx, scp)?;
|
||||
Ok(class.set(args)?)
|
||||
Ok(target.set(args)?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -569,13 +564,13 @@ impl Eval for ShowExpr {
|
||||
type Output = StyleMap;
|
||||
|
||||
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
|
||||
let class = self.class();
|
||||
let class = class.eval(ctx, scp)?.cast::<Class>().at(class.span())?;
|
||||
let closure = self.closure();
|
||||
let func = closure.eval(ctx, scp)?.cast::<Func>().at(closure.span())?;
|
||||
let mut styles = StyleMap::new();
|
||||
styles.set_recipe(class.id(), func, self.span());
|
||||
Ok(styles)
|
||||
let target = self.target();
|
||||
let target_span = target.span();
|
||||
let target = target.eval(ctx, scp)?.cast::<Func>().at(target_span)?;
|
||||
let recipe = self.recipe();
|
||||
let recipe_span = recipe.span();
|
||||
let recipe = recipe.eval(ctx, scp)?.cast::<Func>().at(recipe_span)?;
|
||||
Ok(target.show(recipe, recipe_span).at(target_span)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher};
|
||||
use std::iter;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use super::{Args, Class, Construct, Func, Set, Value};
|
||||
use super::{Args, Func, Node, Value};
|
||||
use crate::diag::TypResult;
|
||||
use crate::util::EcoString;
|
||||
use crate::Context;
|
||||
@ -83,21 +83,18 @@ impl Scope {
|
||||
self.values.insert(var.into(), slot);
|
||||
}
|
||||
|
||||
/// Define a constant native function.
|
||||
pub fn def_func(
|
||||
/// Define a function through a native rust function.
|
||||
pub fn def_fn(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
func: fn(&mut Context, &mut Args) -> TypResult<Value>,
|
||||
) {
|
||||
self.def_const(name, Func::native(name, func));
|
||||
self.def_const(name, Func::from_fn(name, func));
|
||||
}
|
||||
|
||||
/// Define a constant class.
|
||||
pub fn def_class<T>(&mut self, name: &'static str)
|
||||
where
|
||||
T: Construct + Set + 'static,
|
||||
{
|
||||
self.def_const(name, Class::new::<T>(name));
|
||||
/// Define a function through a native rust node.
|
||||
pub fn def_node<T: Node>(&mut self, name: &'static str) {
|
||||
self.def_const(name, Func::from_node::<T>(name));
|
||||
}
|
||||
|
||||
/// Look up the value of a variable.
|
||||
|
@ -30,19 +30,19 @@ impl StyleMap {
|
||||
}
|
||||
|
||||
/// Create a style map from a single property-value pair.
|
||||
pub fn with<P: Property>(key: P, value: P::Value) -> Self {
|
||||
pub fn with<K: Key>(key: K, value: K::Value) -> Self {
|
||||
let mut styles = Self::new();
|
||||
styles.set(key, value);
|
||||
styles
|
||||
}
|
||||
|
||||
/// Set the value for a style property.
|
||||
pub fn set<P: Property>(&mut self, key: P, value: P::Value) {
|
||||
pub fn set<K: Key>(&mut self, key: K, value: K::Value) {
|
||||
self.props.push(Entry::new(key, value));
|
||||
}
|
||||
|
||||
/// Set a value for a style property if it is `Some(_)`.
|
||||
pub fn set_opt<P: Property>(&mut self, key: P, value: Option<P::Value>) {
|
||||
pub fn set_opt<K: Key>(&mut self, key: K, value: Option<K::Value>) {
|
||||
if let Some(value) = value {
|
||||
self.set(key, value);
|
||||
}
|
||||
@ -126,8 +126,8 @@ pub enum Interruption {
|
||||
/// Style property keys.
|
||||
///
|
||||
/// This trait is not intended to be implemented manually, but rather through
|
||||
/// the `#[class]` proc-macro.
|
||||
pub trait Property: Sync + Send + 'static {
|
||||
/// the `#[node]` proc-macro.
|
||||
pub trait Key: Sync + Send + 'static {
|
||||
/// The type of value that is returned when getting this property from a
|
||||
/// style map. For example, this could be [`Length`](crate::geom::Length)
|
||||
/// for a `WIDTH` property.
|
||||
@ -148,7 +148,7 @@ pub trait Property: Sync + Send + 'static {
|
||||
/// A static reference to the default value of the property.
|
||||
///
|
||||
/// This is automatically implemented through lazy-initialization in the
|
||||
/// `#[class]` macro. This way, expensive defaults don't need to be
|
||||
/// `#[node]` macro. This way, expensive defaults don't need to be
|
||||
/// recreated all the time.
|
||||
fn default_ref() -> &'static Self::Value;
|
||||
|
||||
@ -162,8 +162,12 @@ pub trait Property: Sync + Send + 'static {
|
||||
}
|
||||
}
|
||||
|
||||
/// Marker trait that indicates that a property doesn't need folding.
|
||||
pub trait Nonfolding {}
|
||||
/// Marker trait indicating that a property can be accessed by reference.
|
||||
///
|
||||
/// This is implemented by a key if and only if `K::FOLDING` if false.
|
||||
/// Unfortunately, Rust's type system doesn't allow use to use an associated
|
||||
/// constant to bound a function, so we need this trait.
|
||||
pub trait Referencable {}
|
||||
|
||||
/// An entry for a single style property.
|
||||
#[derive(Clone)]
|
||||
@ -173,14 +177,14 @@ struct Entry {
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
fn new<P: Property>(key: P, value: P::Value) -> Self {
|
||||
fn new<K: Key>(key: K, value: K::Value) -> Self {
|
||||
Self {
|
||||
pair: Arc::new((key, value)),
|
||||
scoped: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is<P: Property>(&self) -> bool {
|
||||
fn is<P: Key>(&self) -> bool {
|
||||
self.pair.style_id() == TypeId::of::<P>()
|
||||
}
|
||||
|
||||
@ -192,7 +196,7 @@ impl Entry {
|
||||
self.pair.node_id() == node
|
||||
}
|
||||
|
||||
fn downcast<P: Property>(&self) -> Option<&P::Value> {
|
||||
fn downcast<K: Key>(&self) -> Option<&K::Value> {
|
||||
self.pair.as_any().downcast_ref()
|
||||
}
|
||||
|
||||
@ -244,18 +248,18 @@ trait Bounds: Sync + Send + 'static {
|
||||
fn style_id(&self) -> TypeId;
|
||||
}
|
||||
|
||||
impl<P: Property> Bounds for (P, P::Value) {
|
||||
impl<K: Key> Bounds for (K, K::Value) {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
&self.1
|
||||
}
|
||||
|
||||
fn dyn_fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{} = {:?}", P::NAME, self.1)
|
||||
write!(f, "{} = {:?}", K::NAME, self.1)
|
||||
}
|
||||
|
||||
fn dyn_eq(&self, other: &Entry) -> bool {
|
||||
self.style_id() == other.pair.style_id()
|
||||
&& if let Some(other) = other.downcast::<P>() {
|
||||
&& if let Some(other) = other.downcast::<K>() {
|
||||
&self.1 == other
|
||||
} else {
|
||||
false
|
||||
@ -270,11 +274,11 @@ impl<P: Property> Bounds for (P, P::Value) {
|
||||
}
|
||||
|
||||
fn node_id(&self) -> TypeId {
|
||||
P::node_id()
|
||||
K::node_id()
|
||||
}
|
||||
|
||||
fn style_id(&self) -> TypeId {
|
||||
TypeId::of::<P>()
|
||||
TypeId::of::<K>()
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,9 +370,9 @@ impl<'a> StyleChain<'a> {
|
||||
///
|
||||
/// Returns the property's default value if no map in the chain contains an
|
||||
/// entry for it.
|
||||
pub fn get<P: Property>(self, key: P) -> P::Value
|
||||
pub fn get<K: Key>(self, key: K) -> K::Value
|
||||
where
|
||||
P::Value: Copy,
|
||||
K::Value: Copy,
|
||||
{
|
||||
self.get_cloned(key)
|
||||
}
|
||||
@ -381,11 +385,11 @@ impl<'a> StyleChain<'a> {
|
||||
///
|
||||
/// Returns a lazily-initialized reference to the property's default value
|
||||
/// if no map in the chain contains an entry for it.
|
||||
pub fn get_ref<P: Property>(self, key: P) -> &'a P::Value
|
||||
pub fn get_ref<K: Key>(self, key: K) -> &'a K::Value
|
||||
where
|
||||
P: Nonfolding,
|
||||
K: Referencable,
|
||||
{
|
||||
self.values(key).next().unwrap_or_else(|| P::default_ref())
|
||||
self.values(key).next().unwrap_or_else(|| K::default_ref())
|
||||
}
|
||||
|
||||
/// Get the (folded) value of any style property.
|
||||
@ -396,15 +400,15 @@ impl<'a> StyleChain<'a> {
|
||||
///
|
||||
/// Returns the property's default value if no map in the chain contains an
|
||||
/// entry for it.
|
||||
pub fn get_cloned<P: Property>(self, key: P) -> P::Value {
|
||||
if P::FOLDING {
|
||||
pub fn get_cloned<K: Key>(self, key: K) -> K::Value {
|
||||
if K::FOLDING {
|
||||
self.values(key)
|
||||
.cloned()
|
||||
.chain(std::iter::once(P::default()))
|
||||
.reduce(P::fold)
|
||||
.chain(std::iter::once(K::default()))
|
||||
.reduce(K::fold)
|
||||
.unwrap()
|
||||
} else {
|
||||
self.values(key).next().cloned().unwrap_or_else(P::default)
|
||||
self.values(key).next().cloned().unwrap_or_else(K::default)
|
||||
}
|
||||
}
|
||||
|
||||
@ -445,19 +449,19 @@ impl<'a> StyleChain<'a> {
|
||||
|
||||
impl<'a> StyleChain<'a> {
|
||||
/// Iterate over all values for the given property in the chain.
|
||||
fn values<P: Property>(self, _: P) -> impl Iterator<Item = &'a P::Value> {
|
||||
fn values<K: Key>(self, _: K) -> impl Iterator<Item = &'a K::Value> {
|
||||
let mut depth = 0;
|
||||
self.links().flat_map(move |link| {
|
||||
let mut entries: &[Entry] = &[];
|
||||
match link {
|
||||
Link::Map(map) => entries = &map.props,
|
||||
Link::Barrier(id) => depth += (id == P::node_id()) as usize,
|
||||
Link::Barrier(id) => depth += (id == K::node_id()) as usize,
|
||||
}
|
||||
entries
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(move |entry| entry.is::<P>() && (!entry.scoped || depth <= 1))
|
||||
.filter_map(|entry| entry.downcast::<P>())
|
||||
.filter(move |entry| entry.is::<K>() && (!entry.scoped || depth <= 1))
|
||||
.filter_map(|entry| entry.downcast::<K>())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{ops, Args, Array, Class, Content, Dict, Func, Layout};
|
||||
use super::{ops, Args, Array, Content, Dict, Func, Layout};
|
||||
use crate::diag::{with_alternative, StrResult};
|
||||
use crate::geom::{Angle, Color, Fractional, Length, Linear, Relative, RgbaColor};
|
||||
use crate::syntax::Spanned;
|
||||
@ -47,8 +47,6 @@ pub enum Value {
|
||||
Func(Func),
|
||||
/// Captured arguments to a function.
|
||||
Args(Args),
|
||||
/// A class of nodes.
|
||||
Class(Class),
|
||||
/// A dynamic value.
|
||||
Dyn(Dynamic),
|
||||
}
|
||||
@ -90,7 +88,6 @@ impl Value {
|
||||
Self::Dict(_) => Dict::TYPE_NAME,
|
||||
Self::Func(_) => Func::TYPE_NAME,
|
||||
Self::Args(_) => Args::TYPE_NAME,
|
||||
Self::Class(_) => Class::TYPE_NAME,
|
||||
Self::Dyn(v) => v.type_name(),
|
||||
}
|
||||
}
|
||||
@ -151,7 +148,6 @@ impl Debug for Value {
|
||||
Self::Dict(v) => Debug::fmt(v, f),
|
||||
Self::Func(v) => Debug::fmt(v, f),
|
||||
Self::Args(v) => Debug::fmt(v, f),
|
||||
Self::Class(v) => Debug::fmt(v, f),
|
||||
Self::Dyn(v) => Debug::fmt(v, f),
|
||||
}
|
||||
}
|
||||
@ -190,7 +186,6 @@ impl Hash for Value {
|
||||
Self::Dict(v) => v.hash(state),
|
||||
Self::Func(v) => v.hash(state),
|
||||
Self::Args(v) => v.hash(state),
|
||||
Self::Class(v) => v.hash(state),
|
||||
Self::Dyn(v) => v.hash(state),
|
||||
}
|
||||
}
|
||||
@ -444,9 +439,8 @@ primitive! { EcoString: "string", Str }
|
||||
primitive! { Content: "content", Content, None => Content::new() }
|
||||
primitive! { Array: "array", Array }
|
||||
primitive! { Dict: "dictionary", Dict }
|
||||
primitive! { Func: "function", Func, Class(v) => v.constructor() }
|
||||
primitive! { Func: "function", Func }
|
||||
primitive! { Args: "arguments", Args }
|
||||
primitive! { Class: "class", Class }
|
||||
|
||||
impl Cast for Value {
|
||||
fn is(_: &Value) -> bool {
|
||||
@ -570,7 +564,7 @@ mod tests {
|
||||
|
||||
// Functions.
|
||||
test(
|
||||
Func::native("nil", |_, _| Ok(Value::None)),
|
||||
Func::from_fn("nil", |_, _| Ok(Value::None)),
|
||||
"<function nil>",
|
||||
);
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::library::prelude::*;
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct HideNode(pub LayoutNode);
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl HideNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::inline(Self(args.expect("body")?)))
|
||||
|
@ -7,7 +7,7 @@ use crate::library::text::TextNode;
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct ImageNode(pub ImageId);
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl ImageNode {
|
||||
/// How the image should adjust itself to a given area.
|
||||
pub const FIT: ImageFit = ImageFit::Cover;
|
||||
|
@ -19,7 +19,7 @@ pub type CircleNode = ShapeNode<CIRCLE>;
|
||||
/// Place a node into an ellipse.
|
||||
pub type EllipseNode = ShapeNode<ELLIPSE>;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl<const S: ShapeKind> ShapeNode<S> {
|
||||
/// How to fill the shape.
|
||||
pub const FILL: Option<Paint> = None;
|
||||
|
@ -19,7 +19,7 @@ pub type RotateNode = TransformNode<ROTATE>;
|
||||
/// Transform a node by scaling it without affecting layout.
|
||||
pub type ScaleNode = TransformNode<SCALE>;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl<const T: TransformKind> TransformNode<T> {
|
||||
/// The origin of the transformation.
|
||||
pub const ORIGIN: Spec<Option<Align>> = Spec::default();
|
||||
|
@ -10,7 +10,7 @@ pub struct AlignNode {
|
||||
pub child: LayoutNode,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl AlignNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
let aligns: Spec<_> = args.find()?.unwrap_or_default();
|
||||
|
@ -11,7 +11,7 @@ pub struct ColumnsNode {
|
||||
pub child: LayoutNode,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl ColumnsNode {
|
||||
/// The size of the gutter space between each column.
|
||||
pub const GUTTER: Linear = Relative::new(0.04).into();
|
||||
@ -103,7 +103,7 @@ impl Layout for ColumnsNode {
|
||||
/// A column break.
|
||||
pub struct ColbreakNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl ColbreakNode {
|
||||
fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::Colbreak)
|
||||
|
@ -3,7 +3,7 @@ use crate::library::prelude::*;
|
||||
/// An inline-level container that sizes content and places it into a paragraph.
|
||||
pub struct BoxNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl BoxNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
let width = args.named("width")?;
|
||||
@ -16,7 +16,7 @@ impl BoxNode {
|
||||
/// A block-level container that places content into a separate flow.
|
||||
pub struct BlockNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl BlockNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::Block(args.find()?.unwrap_or_default()))
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{AlignNode, PlaceNode, SpacingKind};
|
||||
use super::{AlignNode, PlaceNode, Spacing};
|
||||
use crate::library::prelude::*;
|
||||
use crate::library::text::{ParNode, TextNode};
|
||||
|
||||
@ -19,7 +19,7 @@ pub enum FlowChild {
|
||||
/// A column / region break.
|
||||
Colbreak,
|
||||
/// Vertical spacing between other children.
|
||||
Spacing(SpacingKind),
|
||||
Spacing(Spacing),
|
||||
/// An arbitrary block-level node.
|
||||
Node(LayoutNode),
|
||||
}
|
||||
@ -142,9 +142,9 @@ impl FlowLayouter {
|
||||
}
|
||||
|
||||
/// Layout spacing.
|
||||
pub fn layout_spacing(&mut self, spacing: SpacingKind) {
|
||||
pub fn layout_spacing(&mut self, spacing: Spacing) {
|
||||
match spacing {
|
||||
SpacingKind::Linear(v) => {
|
||||
Spacing::Linear(v) => {
|
||||
// Resolve the linear and limit it to the remaining space.
|
||||
let resolved = v.resolve(self.full.y);
|
||||
let limited = resolved.min(self.regions.first.y);
|
||||
@ -152,7 +152,7 @@ impl FlowLayouter {
|
||||
self.used.y += limited;
|
||||
self.items.push(FlowItem::Absolute(resolved));
|
||||
}
|
||||
SpacingKind::Fractional(v) => {
|
||||
Spacing::Fractional(v) => {
|
||||
self.items.push(FlowItem::Fractional(v));
|
||||
self.fr += v;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ pub struct GridNode {
|
||||
pub children: Vec<LayoutNode>,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl GridNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
let columns = args.named("columns")?.unwrap_or_default();
|
||||
|
@ -9,7 +9,7 @@ pub struct PadNode {
|
||||
pub child: LayoutNode,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl PadNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
let all = args.find()?;
|
||||
|
@ -7,7 +7,7 @@ use crate::library::prelude::*;
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct PageNode(pub LayoutNode);
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl PageNode {
|
||||
/// The unflipped width of the page.
|
||||
pub const WIDTH: Smart<Length> = Smart::Custom(Paper::A4.width());
|
||||
@ -36,7 +36,9 @@ impl PageNode {
|
||||
Ok(Content::Page(Self(args.expect("body")?)))
|
||||
}
|
||||
|
||||
fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
|
||||
fn set(args: &mut Args) -> TypResult<StyleMap> {
|
||||
let mut styles = StyleMap::new();
|
||||
|
||||
if let Some(paper) = args.named_or_find::<Paper>("paper")? {
|
||||
styles.set(Self::WIDTH, Smart::Custom(paper.width()));
|
||||
styles.set(Self::HEIGHT, Smart::Custom(paper.height()));
|
||||
@ -59,7 +61,7 @@ impl PageNode {
|
||||
styles.set_opt(Self::HEADER, args.named("header")?);
|
||||
styles.set_opt(Self::FOOTER, args.named("footer")?);
|
||||
|
||||
Ok(())
|
||||
Ok(styles)
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +155,7 @@ impl Debug for PageNode {
|
||||
/// A page break.
|
||||
pub struct PagebreakNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl PagebreakNode {
|
||||
fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::Pagebreak)
|
||||
|
@ -5,7 +5,7 @@ use crate::library::prelude::*;
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct PlaceNode(pub LayoutNode);
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl PlaceNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
let aligns = args.find()?.unwrap_or(Spec::with_x(Some(Align::Left)));
|
||||
|
@ -3,7 +3,7 @@ use crate::library::prelude::*;
|
||||
/// Horizontal spacing.
|
||||
pub struct HNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl HNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::Horizontal(args.expect("spacing")?))
|
||||
@ -13,7 +13,7 @@ impl HNode {
|
||||
/// Vertical spacing.
|
||||
pub struct VNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl VNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::Vertical(args.expect("spacing")?))
|
||||
@ -22,28 +22,28 @@ impl VNode {
|
||||
|
||||
/// Kinds of spacing.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum SpacingKind {
|
||||
pub enum Spacing {
|
||||
/// A length stated in absolute values and/or relative to the parent's size.
|
||||
Linear(Linear),
|
||||
/// A length that is the fraction of the remaining free space in the parent.
|
||||
Fractional(Fractional),
|
||||
}
|
||||
|
||||
impl SpacingKind {
|
||||
impl Spacing {
|
||||
/// Whether this is fractional spacing.
|
||||
pub fn is_fractional(self) -> bool {
|
||||
matches!(self, Self::Fractional(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Length> for SpacingKind {
|
||||
impl From<Length> for Spacing {
|
||||
fn from(length: Length) -> Self {
|
||||
Self::Linear(length.into())
|
||||
}
|
||||
}
|
||||
|
||||
castable! {
|
||||
SpacingKind,
|
||||
Spacing,
|
||||
Expected: "linear or fractional",
|
||||
Value::Length(v) => Self::Linear(v.into()),
|
||||
Value::Relative(v) => Self::Linear(v.into()),
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{AlignNode, SpacingKind};
|
||||
use super::{AlignNode, Spacing};
|
||||
use crate::library::prelude::*;
|
||||
|
||||
/// Arrange nodes and spacing along an axis.
|
||||
@ -7,12 +7,12 @@ pub struct StackNode {
|
||||
/// The stacking direction.
|
||||
pub dir: Dir,
|
||||
/// The spacing between non-spacing children.
|
||||
pub spacing: Option<SpacingKind>,
|
||||
pub spacing: Option<Spacing>,
|
||||
/// The children to be stacked.
|
||||
pub children: Vec<StackChild>,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl StackNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::block(Self {
|
||||
@ -60,7 +60,7 @@ impl Layout for StackNode {
|
||||
#[derive(Hash)]
|
||||
pub enum StackChild {
|
||||
/// Spacing between other nodes.
|
||||
Spacing(SpacingKind),
|
||||
Spacing(Spacing),
|
||||
/// An arbitrary node.
|
||||
Node(LayoutNode),
|
||||
}
|
||||
@ -77,10 +77,10 @@ impl Debug for StackChild {
|
||||
castable! {
|
||||
StackChild,
|
||||
Expected: "linear, fractional or content",
|
||||
Value::Length(v) => Self::Spacing(SpacingKind::Linear(v.into())),
|
||||
Value::Relative(v) => Self::Spacing(SpacingKind::Linear(v.into())),
|
||||
Value::Linear(v) => Self::Spacing(SpacingKind::Linear(v)),
|
||||
Value::Fractional(v) => Self::Spacing(SpacingKind::Fractional(v)),
|
||||
Value::Length(v) => Self::Spacing(Spacing::Linear(v.into())),
|
||||
Value::Relative(v) => Self::Spacing(Spacing::Linear(v.into())),
|
||||
Value::Linear(v) => Self::Spacing(Spacing::Linear(v)),
|
||||
Value::Fractional(v) => Self::Spacing(Spacing::Fractional(v)),
|
||||
Value::Content(v) => Self::Node(v.pack()),
|
||||
}
|
||||
|
||||
@ -142,9 +142,9 @@ impl StackLayouter {
|
||||
}
|
||||
|
||||
/// Add spacing along the spacing direction.
|
||||
pub fn layout_spacing(&mut self, spacing: SpacingKind) {
|
||||
pub fn layout_spacing(&mut self, spacing: Spacing) {
|
||||
match spacing {
|
||||
SpacingKind::Linear(v) => {
|
||||
Spacing::Linear(v) => {
|
||||
// Resolve the linear and limit it to the remaining space.
|
||||
let resolved = v.resolve(self.regions.base.get(self.axis));
|
||||
let remaining = self.regions.first.get_mut(self.axis);
|
||||
@ -153,7 +153,7 @@ impl StackLayouter {
|
||||
self.used.main += limited;
|
||||
self.items.push(StackItem::Absolute(resolved));
|
||||
}
|
||||
SpacingKind::Fractional(v) => {
|
||||
Spacing::Fractional(v) => {
|
||||
self.fr += v;
|
||||
self.items.push(StackItem::Fractional(v));
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ pub struct MathNode {
|
||||
pub display: bool,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl MathNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::show(Self {
|
||||
|
@ -18,77 +18,77 @@ pub fn new() -> Scope {
|
||||
let mut std = Scope::new();
|
||||
|
||||
// Text.
|
||||
std.def_class::<text::TextNode>("text");
|
||||
std.def_class::<text::ParNode>("par");
|
||||
std.def_class::<text::LinebreakNode>("linebreak");
|
||||
std.def_class::<text::ParbreakNode>("parbreak");
|
||||
std.def_class::<text::StrongNode>("strong");
|
||||
std.def_class::<text::EmphNode>("emph");
|
||||
std.def_class::<text::RawNode>("raw");
|
||||
std.def_class::<text::UnderlineNode>("underline");
|
||||
std.def_class::<text::StrikethroughNode>("strike");
|
||||
std.def_class::<text::OverlineNode>("overline");
|
||||
std.def_class::<text::LinkNode>("link");
|
||||
std.def_node::<text::TextNode>("text");
|
||||
std.def_node::<text::ParNode>("par");
|
||||
std.def_node::<text::LinebreakNode>("linebreak");
|
||||
std.def_node::<text::ParbreakNode>("parbreak");
|
||||
std.def_node::<text::StrongNode>("strong");
|
||||
std.def_node::<text::EmphNode>("emph");
|
||||
std.def_node::<text::RawNode>("raw");
|
||||
std.def_node::<text::UnderlineNode>("underline");
|
||||
std.def_node::<text::StrikethroughNode>("strike");
|
||||
std.def_node::<text::OverlineNode>("overline");
|
||||
std.def_node::<text::LinkNode>("link");
|
||||
|
||||
// Structure.
|
||||
std.def_class::<structure::HeadingNode>("heading");
|
||||
std.def_class::<structure::ListNode>("list");
|
||||
std.def_class::<structure::EnumNode>("enum");
|
||||
std.def_class::<structure::TableNode>("table");
|
||||
std.def_node::<structure::HeadingNode>("heading");
|
||||
std.def_node::<structure::ListNode>("list");
|
||||
std.def_node::<structure::EnumNode>("enum");
|
||||
std.def_node::<structure::TableNode>("table");
|
||||
|
||||
// Layout.
|
||||
std.def_class::<layout::PageNode>("page");
|
||||
std.def_class::<layout::PagebreakNode>("pagebreak");
|
||||
std.def_class::<layout::HNode>("h");
|
||||
std.def_class::<layout::VNode>("v");
|
||||
std.def_class::<layout::BoxNode>("box");
|
||||
std.def_class::<layout::BlockNode>("block");
|
||||
std.def_class::<layout::AlignNode>("align");
|
||||
std.def_class::<layout::PadNode>("pad");
|
||||
std.def_class::<layout::StackNode>("stack");
|
||||
std.def_class::<layout::GridNode>("grid");
|
||||
std.def_class::<layout::ColumnsNode>("columns");
|
||||
std.def_class::<layout::ColbreakNode>("colbreak");
|
||||
std.def_class::<layout::PlaceNode>("place");
|
||||
std.def_node::<layout::PageNode>("page");
|
||||
std.def_node::<layout::PagebreakNode>("pagebreak");
|
||||
std.def_node::<layout::HNode>("h");
|
||||
std.def_node::<layout::VNode>("v");
|
||||
std.def_node::<layout::BoxNode>("box");
|
||||
std.def_node::<layout::BlockNode>("block");
|
||||
std.def_node::<layout::AlignNode>("align");
|
||||
std.def_node::<layout::PadNode>("pad");
|
||||
std.def_node::<layout::StackNode>("stack");
|
||||
std.def_node::<layout::GridNode>("grid");
|
||||
std.def_node::<layout::ColumnsNode>("columns");
|
||||
std.def_node::<layout::ColbreakNode>("colbreak");
|
||||
std.def_node::<layout::PlaceNode>("place");
|
||||
|
||||
// Graphics.
|
||||
std.def_class::<graphics::ImageNode>("image");
|
||||
std.def_class::<graphics::RectNode>("rect");
|
||||
std.def_class::<graphics::SquareNode>("square");
|
||||
std.def_class::<graphics::EllipseNode>("ellipse");
|
||||
std.def_class::<graphics::CircleNode>("circle");
|
||||
std.def_class::<graphics::MoveNode>("move");
|
||||
std.def_class::<graphics::ScaleNode>("scale");
|
||||
std.def_class::<graphics::RotateNode>("rotate");
|
||||
std.def_class::<graphics::HideNode>("hide");
|
||||
std.def_node::<graphics::ImageNode>("image");
|
||||
std.def_node::<graphics::RectNode>("rect");
|
||||
std.def_node::<graphics::SquareNode>("square");
|
||||
std.def_node::<graphics::EllipseNode>("ellipse");
|
||||
std.def_node::<graphics::CircleNode>("circle");
|
||||
std.def_node::<graphics::MoveNode>("move");
|
||||
std.def_node::<graphics::ScaleNode>("scale");
|
||||
std.def_node::<graphics::RotateNode>("rotate");
|
||||
std.def_node::<graphics::HideNode>("hide");
|
||||
|
||||
// Math.
|
||||
std.def_class::<math::MathNode>("math");
|
||||
std.def_node::<math::MathNode>("math");
|
||||
|
||||
// Utility functions.
|
||||
std.def_func("assert", utility::assert);
|
||||
std.def_func("type", utility::type_);
|
||||
std.def_func("repr", utility::repr);
|
||||
std.def_func("join", utility::join);
|
||||
std.def_func("int", utility::int);
|
||||
std.def_func("float", utility::float);
|
||||
std.def_func("str", utility::str);
|
||||
std.def_func("abs", utility::abs);
|
||||
std.def_func("min", utility::min);
|
||||
std.def_func("max", utility::max);
|
||||
std.def_func("even", utility::even);
|
||||
std.def_func("odd", utility::odd);
|
||||
std.def_func("mod", utility::modulo);
|
||||
std.def_func("range", utility::range);
|
||||
std.def_func("rgb", utility::rgb);
|
||||
std.def_func("cmyk", utility::cmyk);
|
||||
std.def_func("lower", utility::lower);
|
||||
std.def_func("upper", utility::upper);
|
||||
std.def_func("letter", utility::letter);
|
||||
std.def_func("roman", utility::roman);
|
||||
std.def_func("symbol", utility::symbol);
|
||||
std.def_func("len", utility::len);
|
||||
std.def_func("sorted", utility::sorted);
|
||||
std.def_fn("assert", utility::assert);
|
||||
std.def_fn("type", utility::type_);
|
||||
std.def_fn("repr", utility::repr);
|
||||
std.def_fn("join", utility::join);
|
||||
std.def_fn("int", utility::int);
|
||||
std.def_fn("float", utility::float);
|
||||
std.def_fn("str", utility::str);
|
||||
std.def_fn("abs", utility::abs);
|
||||
std.def_fn("min", utility::min);
|
||||
std.def_fn("max", utility::max);
|
||||
std.def_fn("even", utility::even);
|
||||
std.def_fn("odd", utility::odd);
|
||||
std.def_fn("mod", utility::modulo);
|
||||
std.def_fn("range", utility::range);
|
||||
std.def_fn("rgb", utility::rgb);
|
||||
std.def_fn("cmyk", utility::cmyk);
|
||||
std.def_fn("lower", utility::lower);
|
||||
std.def_fn("upper", utility::upper);
|
||||
std.def_fn("letter", utility::letter);
|
||||
std.def_fn("roman", utility::roman);
|
||||
std.def_fn("symbol", utility::symbol);
|
||||
std.def_fn("len", utility::len);
|
||||
std.def_fn("sorted", utility::sorted);
|
||||
|
||||
// Predefined colors.
|
||||
std.def_const("black", Color::BLACK);
|
||||
|
@ -5,13 +5,12 @@ pub use std::hash::Hash;
|
||||
pub use std::num::NonZeroUsize;
|
||||
pub use std::sync::Arc;
|
||||
|
||||
pub use typst_macros::class;
|
||||
pub use typst_macros::node;
|
||||
|
||||
pub use crate::diag::{with_alternative, At, StrResult, TypResult};
|
||||
pub use crate::eval::{
|
||||
Arg, Args, Array, Cast, Construct, Content, Dict, Func, Layout, LayoutNode, Merge,
|
||||
Property, Regions, Scope, Set, Show, ShowNode, Smart, StyleChain, StyleMap, StyleVec,
|
||||
Value,
|
||||
Arg, Args, Array, Cast, Content, Dict, Func, Key, Layout, LayoutNode, Merge, Node,
|
||||
Regions, Scope, Show, ShowNode, Smart, StyleChain, StyleMap, StyleVec, Value,
|
||||
};
|
||||
pub use crate::frame::*;
|
||||
pub use crate::geom::*;
|
||||
|
@ -11,7 +11,7 @@ pub struct HeadingNode {
|
||||
pub body: Content,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl HeadingNode {
|
||||
/// The heading's font family. Just the normal text family if `auto`.
|
||||
pub const FAMILY: Leveled<Smart<FontFamily>> = Leveled::Value(Smart::Auto);
|
||||
|
@ -28,7 +28,7 @@ pub struct ListItem {
|
||||
/// An ordered list.
|
||||
pub type EnumNode = ListNode<ORDERED>;
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl<const L: ListKind> ListNode<L> {
|
||||
/// How the list is labelled.
|
||||
pub const LABEL: Label = Label::Default;
|
||||
|
@ -12,7 +12,7 @@ pub struct TableNode {
|
||||
pub children: Vec<Content>,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl TableNode {
|
||||
/// The primary cell fill color.
|
||||
pub const PRIMARY: Option<Paint> = None;
|
||||
@ -41,14 +41,15 @@ impl TableNode {
|
||||
}))
|
||||
}
|
||||
|
||||
fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
|
||||
fn set(args: &mut Args) -> TypResult<StyleMap> {
|
||||
let mut styles = StyleMap::new();
|
||||
let fill = args.named("fill")?;
|
||||
styles.set_opt(Self::PRIMARY, args.named("primary")?.or(fill));
|
||||
styles.set_opt(Self::SECONDARY, args.named("secondary")?.or(fill));
|
||||
styles.set_opt(Self::STROKE, args.named("stroke")?);
|
||||
styles.set_opt(Self::THICKNESS, args.named("thickness")?);
|
||||
styles.set_opt(Self::PADDING, args.named("padding")?);
|
||||
Ok(())
|
||||
Ok(styles)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ pub type StrikethroughNode = DecoNode<STRIKETHROUGH>;
|
||||
/// Typeset overlined text.
|
||||
pub type OverlineNode = DecoNode<OVERLINE>;
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl<const L: DecoLine> DecoNode<L> {
|
||||
/// Stroke color of the line, defaults to the text color if `None`.
|
||||
#[shorthand]
|
||||
|
@ -11,7 +11,7 @@ pub struct LinkNode {
|
||||
pub body: Option<Content>,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl LinkNode {
|
||||
/// The fill color of text in the link. Just the surrounding text color
|
||||
/// if `auto`.
|
||||
|
@ -25,7 +25,7 @@ use crate::util::EcoString;
|
||||
#[derive(Hash)]
|
||||
pub struct TextNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl TextNode {
|
||||
/// A prioritized sequence of font families.
|
||||
#[variadic]
|
||||
@ -122,7 +122,7 @@ impl TextNode {
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct StrongNode(pub Content);
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl StrongNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::show(Self(args.expect("body")?)))
|
||||
@ -141,7 +141,7 @@ impl Show for StrongNode {
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct EmphNode(pub Content);
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl EmphNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::show(Self(args.expect("body")?)))
|
||||
|
@ -6,7 +6,7 @@ use xi_unicode::LineBreakIterator;
|
||||
|
||||
use super::{shape, ShapedText, TextNode};
|
||||
use crate::font::FontStore;
|
||||
use crate::library::layout::SpacingKind;
|
||||
use crate::library::layout::Spacing;
|
||||
use crate::library::prelude::*;
|
||||
use crate::util::{ArcExt, EcoString, RangeExt, SliceExt};
|
||||
|
||||
@ -20,12 +20,12 @@ pub enum ParChild {
|
||||
/// A chunk of text.
|
||||
Text(EcoString),
|
||||
/// Horizontal spacing between other children.
|
||||
Spacing(SpacingKind),
|
||||
Spacing(Spacing),
|
||||
/// An arbitrary inline-level node.
|
||||
Node(LayoutNode),
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl ParNode {
|
||||
/// An ISO 639-1 language code.
|
||||
pub const LANG: Option<EcoString> = None;
|
||||
@ -53,9 +53,10 @@ impl ParNode {
|
||||
Ok(Content::Block(args.expect("body")?))
|
||||
}
|
||||
|
||||
fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
|
||||
let lang = args.named::<Option<EcoString>>("lang")?;
|
||||
fn set(args: &mut Args) -> TypResult<StyleMap> {
|
||||
let mut styles = StyleMap::new();
|
||||
|
||||
let lang = args.named::<Option<EcoString>>("lang")?;
|
||||
let mut dir =
|
||||
lang.clone().flatten().map(|iso| match iso.to_lowercase().as_str() {
|
||||
"ar" | "dv" | "fa" | "he" | "ks" | "pa" | "ps" | "sd" | "ug" | "ur"
|
||||
@ -89,7 +90,7 @@ impl ParNode {
|
||||
styles.set_opt(Self::SPACING, args.named("spacing")?);
|
||||
styles.set_opt(Self::INDENT, args.named("indent")?);
|
||||
|
||||
Ok(())
|
||||
Ok(styles)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +184,7 @@ impl Merge for ParChild {
|
||||
/// A paragraph break.
|
||||
pub struct ParbreakNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl ParbreakNode {
|
||||
fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::Parbreak)
|
||||
@ -193,7 +194,7 @@ impl ParbreakNode {
|
||||
/// A line break.
|
||||
pub struct LinebreakNode;
|
||||
|
||||
#[class]
|
||||
#[node]
|
||||
impl LinebreakNode {
|
||||
fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::Linebreak)
|
||||
@ -256,13 +257,13 @@ impl<'a> ParLayout<'a> {
|
||||
ranges.push(subrange);
|
||||
}
|
||||
}
|
||||
ParChild::Spacing(kind) => match *kind {
|
||||
SpacingKind::Linear(v) => {
|
||||
ParChild::Spacing(spacing) => match *spacing {
|
||||
Spacing::Linear(v) => {
|
||||
let resolved = v.resolve(regions.first.x);
|
||||
items.push(ParItem::Absolute(resolved));
|
||||
ranges.push(range);
|
||||
}
|
||||
SpacingKind::Fractional(v) => {
|
||||
Spacing::Fractional(v) => {
|
||||
items.push(ParItem::Fractional(v));
|
||||
ranges.push(range);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ pub struct RawNode {
|
||||
pub block: bool,
|
||||
}
|
||||
|
||||
#[class]
|
||||
#[node(showable)]
|
||||
impl RawNode {
|
||||
/// The language to syntax-highlight in.
|
||||
pub const LANG: Option<EcoString> = None;
|
||||
|
@ -900,16 +900,14 @@ node! {
|
||||
}
|
||||
|
||||
impl SetExpr {
|
||||
/// The class to set style properties for.
|
||||
pub fn class(&self) -> Ident {
|
||||
self.0.cast_first_child().expect("set expression is missing class")
|
||||
/// The function to set style properties for.
|
||||
pub fn target(&self) -> Ident {
|
||||
self.0.cast_first_child().expect("set rule is missing target")
|
||||
}
|
||||
|
||||
/// The style properties to set.
|
||||
pub fn args(&self) -> CallArgs {
|
||||
self.0
|
||||
.cast_first_child()
|
||||
.expect("set expression is missing argument list")
|
||||
self.0.cast_last_child().expect("set rule is missing argument list")
|
||||
}
|
||||
}
|
||||
|
||||
@ -919,14 +917,14 @@ node! {
|
||||
}
|
||||
|
||||
impl ShowExpr {
|
||||
/// The class to set the show rule for.
|
||||
pub fn class(&self) -> Ident {
|
||||
self.0.cast_first_child().expect("show expression is missing class")
|
||||
/// The function to customize with this show rule.
|
||||
pub fn target(&self) -> Ident {
|
||||
self.0.cast_first_child().expect("show rule is missing target")
|
||||
}
|
||||
|
||||
/// The closure that defines the rule.
|
||||
pub fn closure(&self) -> ClosureExpr {
|
||||
self.0.cast_first_child().expect("show expression is missing closure")
|
||||
pub fn recipe(&self) -> ClosureExpr {
|
||||
self.0.cast_last_child().expect("show rule is missing closure")
|
||||
}
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
@ -34,15 +34,19 @@ Another text.
|
||||
A [= Heading] C
|
||||
|
||||
---
|
||||
// Error: 1-22 unexpected argument
|
||||
// Error: 14-22 unexpected argument
|
||||
#show heading() as []
|
||||
= Heading
|
||||
|
||||
---
|
||||
// Error: 1-28 expected content, found string
|
||||
// Error: 14-28 expected content, found string
|
||||
#show heading(_, _) as "hi"
|
||||
= Heading
|
||||
|
||||
---
|
||||
// Error: 7-12 this function cannot be customized with show
|
||||
#show upper() as {}
|
||||
|
||||
---
|
||||
// Ref: false
|
||||
// // Error: 1-29 show rule is recursive
|
||||
|
@ -73,7 +73,7 @@ fn main() {
|
||||
let mut std = typst::library::new();
|
||||
std.def_const("conifer", RgbaColor::new(0x9f, 0xEB, 0x52, 0xFF));
|
||||
std.def_const("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
|
||||
std.def_func("test", move |_, args| {
|
||||
std.def_fn("test", move |_, args| {
|
||||
let lhs = args.expect::<Value>("left-hand side")?;
|
||||
let rhs = args.expect::<Value>("right-hand side")?;
|
||||
if lhs != rhs {
|
||||
|
Loading…
x
Reference in New Issue
Block a user