Small improvements 🍪

This commit is contained in:
Laurenz 2020-10-04 23:31:35 +02:00
parent 605ab104c5
commit 335fa2d118
21 changed files with 166 additions and 129 deletions

View File

@ -2,7 +2,9 @@
use std::mem; use std::mem;
use super::*; use super::{Convert, RefKey, ValueDict};
use crate::layout::LayoutContext;
use crate::syntax::{SpanWith, Spanned};
/// A wrapper around a dictionary value that simplifies argument parsing in /// A wrapper around a dictionary value that simplifies argument parsing in
/// functions. /// functions.
@ -21,14 +23,44 @@ impl Args {
{ {
self.0.v.remove(key).and_then(|entry| { self.0.v.remove(key).and_then(|entry| {
let span = entry.value.span; let span = entry.value.span;
let (t, diag) = T::convert(entry.value); let (result, diag) = T::convert(entry.value);
if let Some(diag) = diag { if let Some(diag) = diag {
ctx.f.diags.push(diag.span_with(span)) ctx.f.diags.push(diag.span_with(span))
} }
t.ok() result.ok()
}) })
} }
/// This is the same as [`get`], except that it generates an error about a
/// missing argument with the given `name` if the key does not exist.
///
/// [`get`]: #method.get
pub fn need<'a, K, T>(
&mut self,
ctx: &mut LayoutContext,
key: K,
name: &str,
) -> Option<T>
where
K: Into<RefKey<'a>>,
T: Convert,
{
match self.0.v.remove(key) {
Some(entry) => {
let span = entry.value.span;
let (result, diag) = T::convert(entry.value);
if let Some(diag) = diag {
ctx.f.diags.push(diag.span_with(span))
}
result.ok()
}
None => {
ctx.f.diags.push(error!(self.0.span, "missing argument: {}", name));
None
}
}
}
/// Retrieve and remove the first matching positional argument. /// Retrieve and remove the first matching positional argument.
pub fn find<T>(&mut self) -> Option<T> pub fn find<T>(&mut self) -> Option<T>
where where
@ -104,6 +136,7 @@ impl Args {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::{Dict, SpannedEntry, Value};
use super::*; use super::*;
fn entry(value: Value) -> SpannedEntry<Value> { fn entry(value: Value) -> SpannedEntry<Value> {

View File

@ -4,11 +4,12 @@ use std::ops::Deref;
use fontdock::{FontStretch, FontStyle, FontWeight}; use fontdock::{FontStretch, FontStyle, FontWeight};
use super::*; use super::{Value, ValueDict, ValueFunc};
use crate::diag::Diag; use crate::diag::Diag;
use crate::geom::Linear; use crate::geom::Linear;
use crate::layout::{Dir, SpecAlign}; use crate::layout::{Dir, SpecAlign};
use crate::paper::Paper; use crate::paper::Paper;
use crate::syntax::{Ident, SpanWith, Spanned, SynTree};
/// Types that values can be converted into. /// Types that values can be converted into.
pub trait Convert: Sized { pub trait Convert: Sized {
@ -96,9 +97,9 @@ macro_rules! impl_match {
} }
impl_match!(Value, "value", v => v); impl_match!(Value, "value", v => v);
impl_match!(Ident, "ident", Value::Ident(v) => v); impl_match!(Ident, "identifier", Value::Ident(v) => v);
impl_match!(bool, "bool", Value::Bool(v) => v); impl_match!(bool, "bool", Value::Bool(v) => v);
impl_match!(i64, "int", Value::Int(v) => v); impl_match!(i64, "integer", Value::Int(v) => v);
impl_match!(f64, "float", impl_match!(f64, "float",
Value::Int(v) => v as f64, Value::Int(v) => v as f64,
Value::Float(v) => v, Value::Float(v) => v,
@ -112,9 +113,9 @@ impl_match!(Linear, "linear",
); );
impl_match!(String, "string", Value::Str(v) => v); impl_match!(String, "string", Value::Str(v) => v);
impl_match!(SynTree, "tree", Value::Content(v) => v); impl_match!(SynTree, "tree", Value::Content(v) => v);
impl_match!(ValueDict, "dict", Value::Dict(v) => v); impl_match!(ValueDict, "dictionary", Value::Dict(v) => v);
impl_match!(ValueFunc, "function", Value::Func(v) => v); impl_match!(ValueFunc, "function", Value::Func(v) => v);
impl_match!(StringLike, "ident or string", impl_match!(StringLike, "identifier or string",
Value::Ident(Ident(v)) => StringLike(v), Value::Ident(Ident(v)) => StringLike(v),
Value::Str(v) => StringLike(v), Value::Str(v) => StringLike(v),
); );

View File

@ -1,4 +1,4 @@
//! State. //! Evaluation state.
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight}; use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};

View File

@ -1,4 +1,4 @@
//! Computational values: Syntactical expressions can be evaluated into these. //! Computational values.
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::ops::Deref; use std::ops::Deref;
@ -57,9 +57,9 @@ impl Value {
pub fn ty(&self) -> &'static str { pub fn ty(&self) -> &'static str {
match self { match self {
Self::None => "none", Self::None => "none",
Self::Ident(_) => "ident", Self::Ident(_) => "identifier",
Self::Bool(_) => "bool", Self::Bool(_) => "bool",
Self::Int(_) => "int", Self::Int(_) => "integer",
Self::Float(_) => "float", Self::Float(_) => "float",
Self::Relative(_) => "relative", Self::Relative(_) => "relative",
Self::Length(_) => "length", Self::Length(_) => "length",
@ -88,6 +88,9 @@ impl Spanned<Value> {
/// the value is represented as layoutable content in a reasonable way. /// the value is represented as layoutable content in a reasonable way.
pub fn into_commands(self) -> Vec<Command> { pub fn into_commands(self) -> Vec<Command> {
match self.v { match self.v {
// Don't print out none values.
Value::None => vec![],
// Pass-through. // Pass-through.
Value::Commands(commands) => commands, Value::Commands(commands) => commands,
Value::Content(tree) => vec![Command::LayoutSyntaxTree(tree)], Value::Content(tree) => vec![Command::LayoutSyntaxTree(tree)],
@ -109,9 +112,6 @@ impl Spanned<Value> {
commands commands
} }
// Don't print out none values.
Value::None => vec![],
// Format with debug. // Format with debug.
val => { val => {
let fmt = format!("{:?}", val); let fmt = format!("{:?}", val);
@ -144,6 +144,14 @@ impl Debug for Value {
} }
} }
/// A dictionary of values.
///
/// # Example
/// ```typst
/// (false, 12cm, greeting="hi")
/// ```
pub type ValueDict = Dict<SpannedEntry<Value>>;
/// An wrapper around a reference-counted executable function value. /// An wrapper around a reference-counted executable function value.
/// ///
/// The dynamic function object is wrapped in an `Rc` to keep [`Value`] /// The dynamic function object is wrapped in an `Rc` to keep [`Value`]
@ -192,11 +200,3 @@ impl Debug for ValueFunc {
f.pad("<function>") f.pad("<function>")
} }
} }
/// A dictionary of values.
///
/// # Example
/// ```typst
/// (false, 12cm, greeting="hi")
/// ```
pub type ValueDict = Dict<SpannedEntry<Value>>;

View File

@ -26,11 +26,11 @@ use crate::length::Length;
/// The raw _PDF_ is written into the `target` writable, returning the number of /// The raw _PDF_ is written into the `target` writable, returning the number of
/// bytes written. /// bytes written.
pub fn export<W: Write>( pub fn export<W: Write>(
layout: &[BoxLayout], layouts: &[BoxLayout],
loader: &FontLoader, loader: &FontLoader,
target: W, target: W,
) -> io::Result<usize> { ) -> io::Result<usize> {
PdfExporter::new(layout, loader, target)?.write() PdfExporter::new(layouts, loader, target)?.write()
} }
struct PdfExporter<'a, W: Write> { struct PdfExporter<'a, W: Write> {
@ -42,7 +42,7 @@ struct PdfExporter<'a, W: Write> {
/// tree and so on. These offsets are computed in the beginning and stored /// tree and so on. These offsets are computed in the beginning and stored
/// here. /// here.
offsets: Offsets, offsets: Offsets,
// Font remapping, see below at `remap_fonts`. // Font remapping, for more information see `remap_fonts`.
to_pdf: HashMap<FaceId, usize>, to_pdf: HashMap<FaceId, usize>,
to_layout: Vec<FaceId>, to_layout: Vec<FaceId>,
} }

View File

@ -12,7 +12,9 @@ use super::*;
/// Performs the line layouting. /// Performs the line layouting.
pub struct LineLayouter { pub struct LineLayouter {
/// The context used for line layouting.
ctx: LineContext, ctx: LineContext,
/// The underlying layouter that stacks the finished lines.
stack: StackLayouter, stack: StackLayouter,
/// The in-progress line. /// The in-progress line.
run: LineRun, run: LineRun,
@ -36,27 +38,6 @@ pub struct LineContext {
pub line_spacing: f64, pub line_spacing: f64,
} }
/// A sequence of boxes with the same alignment. A real line can consist of
/// multiple runs with different alignments.
struct LineRun {
/// The so-far accumulated items of the run.
layouts: Vec<(f64, BoxLayout)>,
/// The summed width and maximal height of the run.
size: Size,
/// The alignment of all layouts in the line.
///
/// When a new run is created the alignment is yet to be determined and
/// `None` as such. Once a layout is added, its alignment decides the
/// alignment for the whole run.
align: Option<LayoutAlign>,
/// The amount of space left by another run on the same line or `None` if
/// this is the only run so far.
usable: Option<f64>,
/// The spacing state. This influences how new spacing is handled, e.g. hard
/// spacing may override soft spacing.
last_spacing: LastSpacing,
}
impl LineLayouter { impl LineLayouter {
/// Create a new line layouter. /// Create a new line layouter.
pub fn new(ctx: LineContext) -> Self { pub fn new(ctx: LineContext) -> Self {
@ -259,6 +240,27 @@ impl LineLayouter {
} }
} }
/// A sequence of boxes with the same alignment. A real line can consist of
/// multiple runs with different alignments.
struct LineRun {
/// The so-far accumulated items of the run.
layouts: Vec<(f64, BoxLayout)>,
/// The summed width and maximal height of the run.
size: Size,
/// The alignment of all layouts in the line.
///
/// When a new run is created the alignment is yet to be determined and
/// `None` as such. Once a layout is added, its alignment decides the
/// alignment for the whole run.
align: Option<LayoutAlign>,
/// The amount of space left by another run on the same line or `None` if
/// this is the only run so far.
usable: Option<f64>,
/// The spacing state. This influences how new spacing is handled, e.g. hard
/// spacing may override soft spacing.
last_spacing: LastSpacing,
}
impl LineRun { impl LineRun {
fn new() -> Self { fn new() -> Self {
Self { Self {

View File

@ -46,7 +46,6 @@ pub async fn layout(
}; };
let layouts = layout_tree(&tree, &mut ctx).await; let layouts = layout_tree(&tree, &mut ctx).await;
Pass::new(layouts, ctx.f) Pass::new(layouts, ctx.f)
} }
@ -177,12 +176,11 @@ pub enum Command {
SetTextState(TextState), SetTextState(TextState),
/// Update the page style. /// Update the page style.
SetPageState(PageState), SetPageState(PageState),
/// Update the alignment for future boxes added to this layouting process.
SetAlignment(LayoutAlign),
/// Update the layouting system along which future boxes will be laid /// Update the layouting system along which future boxes will be laid
/// out. This ends the current line. /// out. This ends the current line.
SetSystem(LayoutSystem), SetSystem(LayoutSystem),
/// Update the alignment for future boxes added to this layouting process.
SetAlignment(LayoutAlign),
} }
/// Defines how spacing interacts with surrounding spacing. /// Defines how spacing interacts with surrounding spacing.
@ -211,26 +209,3 @@ impl SpacingKind {
/// The standard spacing kind used for word spacing. /// The standard spacing kind used for word spacing.
pub const WORD: Self = Self::Soft(1); pub const WORD: Self = Self::Soft(1);
} }
/// The spacing kind of the most recently inserted item in a layouting process.
///
/// Since the last inserted item may not be spacing at all, this can be `None`.
#[derive(Debug, Copy, Clone, PartialEq)]
enum LastSpacing {
/// The last item was hard spacing.
Hard,
/// The last item was soft spacing with the given width and level.
Soft(f64, u32),
/// The last item wasn't spacing.
None,
}
impl LastSpacing {
/// The width of the soft space if this is a soft space or zero otherwise.
fn soft_or_zero(self) -> f64 {
match self {
LastSpacing::Soft(space, _) => space,
_ => 0.0,
}
}
}

View File

@ -23,7 +23,9 @@ use super::*;
/// Performs the stack layouting. /// Performs the stack layouting.
pub struct StackLayouter { pub struct StackLayouter {
/// The context used for stack layouting.
ctx: StackContext, ctx: StackContext,
/// The finished layouts.
layouts: Vec<BoxLayout>, layouts: Vec<BoxLayout>,
/// The in-progress space. /// The in-progress space.
space: Space, space: Space,
@ -45,30 +47,6 @@ pub struct StackContext {
pub repeat: bool, pub repeat: bool,
} }
/// A layout space composed of subspaces which can have different systems and
/// alignments.
struct Space {
/// The index of this space in `ctx.spaces`.
index: usize,
/// Whether to include a layout for this space even if it would be empty.
hard: bool,
/// The so-far accumulated layouts.
layouts: Vec<(LayoutSystem, BoxLayout)>,
/// The specialized size of this space.
size: Size,
/// The specialized remaining space.
usable: Size,
/// The specialized extra-needed size to affect the size at all.
extra: Size,
/// Dictate which alignments for new boxes are still allowed and which
/// require a new space to be started. For example, after an `End`-aligned
/// item, no `Start`-aligned one can follow.
rulers: Sides<GenAlign>,
/// The spacing state. This influences how new spacing is handled, e.g. hard
/// spacing may override soft spacing.
last_spacing: LastSpacing,
}
impl StackLayouter { impl StackLayouter {
/// Create a new stack layouter. /// Create a new stack layouter.
pub fn new(ctx: StackContext) -> Self { pub fn new(ctx: StackContext) -> Self {
@ -387,6 +365,30 @@ impl StackLayouter {
} }
} }
/// A layout space composed of subspaces which can have different systems and
/// alignments.
struct Space {
/// The index of this space in `ctx.spaces`.
index: usize,
/// Whether to include a layout for this space even if it would be empty.
hard: bool,
/// The so-far accumulated layouts.
layouts: Vec<(LayoutSystem, BoxLayout)>,
/// The specialized size of this space.
size: Size,
/// The specialized remaining space.
usable: Size,
/// The specialized extra-needed size to affect the size at all.
extra: Size,
/// Dictate which alignments for new boxes are still allowed and which
/// require a new space to be started. For example, after an `End`-aligned
/// item, no `Start`-aligned one can follow.
rulers: Sides<GenAlign>,
/// The spacing state. This influences how new spacing is handled, e.g. hard
/// spacing may override soft spacing.
last_spacing: LastSpacing,
}
impl Space { impl Space {
fn new(index: usize, hard: bool, usable: Size) -> Self { fn new(index: usize, hard: bool, usable: Size) -> Self {
Self { Self {
@ -401,3 +403,26 @@ impl Space {
} }
} }
} }
/// The spacing kind of the most recently inserted item in a layouting process.
///
/// Since the last inserted item may not be spacing at all, this can be `None`.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum LastSpacing {
/// The last item was hard spacing.
Hard,
/// The last item was soft spacing with the given width and level.
Soft(f64, u32),
/// The last item wasn't spacing.
None,
}
impl LastSpacing {
/// The width of the soft space if this is a soft space or zero otherwise.
fn soft_or_zero(self) -> f64 {
match self {
LastSpacing::Soft(space, _) => space,
_ => 0.0,
}
}
}

View File

@ -3,9 +3,7 @@
use super::*; use super::*;
use crate::eval::Eval; use crate::eval::Eval;
use crate::shaping; use crate::shaping;
use crate::syntax::{ use crate::syntax::*;
Deco, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree,
};
use crate::DynFuture; use crate::DynFuture;
/// Layout a syntax tree in a given context. /// Layout a syntax tree in a given context.

View File

@ -2,24 +2,25 @@
//! //!
//! # Steps //! # Steps
//! - **Parsing:** The parsing step first transforms a plain string into an //! - **Parsing:** The parsing step first transforms a plain string into an
//! [iterator of tokens][tokens]. Then, a [parser] constructs a syntax tree //! [iterator of tokens][tokens]. This token stream is [parsed] into a [syntax
//! from the token stream. The structures describing the tree can be found in //! tree]. The structures describing the tree can be found in the [ast]
//! the [syntax] module. //! module.
//! - **Layouting:** The next step is to transform the syntax tree into a //! - **Layouting:** The next step is to transform the syntax tree into a
//! portable representation of the typesetted document. Types for these can be //! portable representation of the typesetted document. The final output
//! found in the [layout] module. A finished layout ready for exporting is a //! consists of a vector of [`BoxLayouts`] (corresponding to pages), ready for
//! [`MultiLayout`] consisting of multiple boxes (or pages). //! exporting.
//! - **Exporting:** The finished layout can then be exported into a supported //! - **Exporting:** The finished layout can then be exported into a supported
//! format. Submodules for these formats are located in the [export] module. //! format. Submodules for these formats are located in the [export] module.
//! Currently, the only supported output format is [_PDF_]. //! Currently, the only supported output format is [_PDF_].
//! //!
//! [tokens]: parse/struct.Tokens.html //! [tokens]: parsing/struct.Tokens.html
//! [parser]: parse/fn.parse.html //! [parsed]: parsing/fn.parse.html
//! [syntax]: syntax/index.html //! [syntax tree]: syntax/ast/type.SynTree.html
//! [ast]: syntax/ast/index.html
//! [layout]: layout/index.html //! [layout]: layout/index.html
//! [`BoxLayouts`]: layout/struct.BoxLayout.html
//! [export]: export/index.html //! [export]: export/index.html
//! [_PDF_]: export/pdf/index.html //! [_PDF_]: export/pdf/index.html
//! [`MultiLayout`]: layout/type.MultiLayout.html
#[macro_use] #[macro_use]
pub mod diag; pub mod diag;
@ -56,7 +57,7 @@ pub async fn typeset(
) -> Pass<Vec<BoxLayout>> { ) -> Pass<Vec<BoxLayout>> {
let parsed = parse::parse(src); let parsed = parse::parse(src);
let layouted = layout::layout(&parsed.output, state, loader).await; let layouted = layout::layout(&parsed.output, state, loader).await;
let feedback = Feedback::merge(parsed.feedback, layouted.feedback); let feedback = Feedback::join(parsed.feedback, layouted.feedback);
Pass::new(layouted.output, feedback) Pass::new(layouted.output, feedback)
} }
@ -108,8 +109,8 @@ impl Feedback {
Self { diags: vec![], decos: vec![] } Self { diags: vec![], decos: vec![] }
} }
/// Merged two feedbacks into one. /// Merge two feedbacks into one.
pub fn merge(mut a: Self, b: Self) -> Self { pub fn join(mut a: Self, b: Self) -> Self {
a.extend(b); a.extend(b);
a a
} }
@ -120,7 +121,7 @@ impl Feedback {
self.decos.extend(more.decos); self.decos.extend(more.decos);
} }
/// Add more feedback whose spans are local and need to be offset by an /// Add more feedback whose spans are local and need to be translated by an
/// `offset` to be correct in this feedback's context. /// `offset` to be correct in this feedback's context.
pub fn extend_offset(&mut self, more: Self, offset: Pos) { pub fn extend_offset(&mut self, more: Self, offset: Pos) {
self.diags.extend(more.diags.offset(offset)); self.diags.extend(more.diags.offset(offset));

View File

@ -1,4 +1,4 @@
use super::*; use crate::prelude::*;
/// `align`: Align content along the layouting axes. /// `align`: Align content along the layouting axes.
/// ///
@ -16,7 +16,6 @@ use super::*;
/// There may not be two alignment specifications for the same axis. /// There may not be two alignment specifications for the same axis.
pub async fn align(mut args: Args, ctx: &mut LayoutContext) -> Value { pub async fn align(mut args: Args, ctx: &mut LayoutContext) -> Value {
let body = args.find::<SynTree>(); let body = args.find::<SynTree>();
let h = args.get::<_, Spanned<SpecAlign>>(ctx, "horizontal"); let h = args.get::<_, Spanned<SpecAlign>>(ctx, "horizontal");
let v = args.get::<_, Spanned<SpecAlign>>(ctx, "vertical"); let v = args.get::<_, Spanned<SpecAlign>>(ctx, "vertical");
let pos = args.find_all::<Spanned<SpecAlign>>(); let pos = args.find_all::<Spanned<SpecAlign>>();

View File

@ -1,5 +1,5 @@
use super::*;
use crate::geom::Linear; use crate::geom::Linear;
use crate::prelude::*;
/// `box`: Layouts its contents into a box. /// `box`: Layouts its contents into a box.
/// ///

View File

@ -1,11 +1,11 @@
use super::*;
use crate::color::RgbaColor; use crate::color::RgbaColor;
use crate::prelude::*;
/// `rgb`: Create an RGB(A) color. /// `rgb`: Create an RGB(A) color.
pub async fn rgb(mut args: Args, ctx: &mut LayoutContext) -> Value { pub async fn rgb(mut args: Args, ctx: &mut LayoutContext) -> Value {
let r = args.get::<_, Spanned<i64>>(ctx, 0); let r = args.need::<_, Spanned<i64>>(ctx, 0, "red value");
let g = args.get::<_, Spanned<i64>>(ctx, 1); let g = args.need::<_, Spanned<i64>>(ctx, 1, "green value");
let b = args.get::<_, Spanned<i64>>(ctx, 2); let b = args.need::<_, Spanned<i64>>(ctx, 2, "blue value");
let a = args.get::<_, Spanned<i64>>(ctx, 3); let a = args.get::<_, Spanned<i64>>(ctx, 3);
args.done(ctx); args.done(ctx);

View File

@ -1,8 +1,8 @@
use fontdock::{FontStretch, FontStyle, FontWeight}; use fontdock::{FontStretch, FontStyle, FontWeight};
use super::*;
use crate::eval::StringLike; use crate::eval::StringLike;
use crate::geom::Linear; use crate::geom::Linear;
use crate::prelude::*;
/// `font`: Configure the font. /// `font`: Configure the font.
/// ///

View File

@ -15,7 +15,6 @@ pub use page::*;
pub use spacing::*; pub use spacing::*;
use crate::eval::{Scope, ValueFunc}; use crate::eval::{Scope, ValueFunc};
use crate::prelude::*;
macro_rules! std { macro_rules! std {
($($name:literal => $func:expr),* $(,)?) => { ($($name:literal => $func:expr),* $(,)?) => {

View File

@ -1,9 +1,9 @@
use std::mem; use std::mem;
use super::*;
use crate::eval::Absolute; use crate::eval::Absolute;
use crate::geom::{Linear, Sides}; use crate::geom::{Linear, Sides};
use crate::paper::{Paper, PaperClass}; use crate::paper::{Paper, PaperClass};
use crate::prelude::*;
/// `page`: Configure pages. /// `page`: Configure pages.
/// ///

View File

@ -1,6 +1,6 @@
use super::*;
use crate::geom::Linear; use crate::geom::Linear;
use crate::layout::SpacingKind; use crate::layout::SpacingKind;
use crate::prelude::*;
/// `h`: Add horizontal spacing. /// `h`: Add horizontal spacing.
/// ///
@ -19,7 +19,7 @@ pub async fn v(args: Args, ctx: &mut LayoutContext) -> Value {
} }
fn spacing(mut args: Args, ctx: &mut LayoutContext, axis: SpecAxis) -> Value { fn spacing(mut args: Args, ctx: &mut LayoutContext, axis: SpecAxis) -> Value {
let spacing = args.get::<_, Linear>(ctx, 0); let spacing = args.need::<_, Linear>(ctx, 0, "spacing");
args.done(ctx); args.done(ctx);
Value::Commands(if let Some(spacing) = spacing { Value::Commands(if let Some(spacing) = spacing {

View File

@ -37,7 +37,7 @@ pub enum PaperClass {
} }
impl PaperClass { impl PaperClass {
/// The default margin ratios for this page class. /// The default margins for this page class.
pub fn default_margins(self) -> Sides<Linear> { pub fn default_margins(self) -> Sides<Linear> {
let f = Linear::rel; let f = Linear::rel;
let s = |l, r, t, b| Sides::new(f(l), f(r), f(t), f(b)); let s = |l, r, t, b| Sides::new(f(l), f(r), f(t), f(b));

View File

@ -89,6 +89,7 @@ macro_rules! Call {
}}; }};
($($tts:tt)*) => { Expr::Call(Call![@$($tts)*]) }; ($($tts:tt)*) => { Expr::Call(Call![@$($tts)*]) };
} }
fn Unary(op: impl Into<Spanned<UnOp>>, expr: impl Into<Spanned<Expr>>) -> Expr { fn Unary(op: impl Into<Spanned<UnOp>>, expr: impl Into<Spanned<Expr>>) -> Expr {
Expr::Unary(ExprUnary { Expr::Unary(ExprUnary {
op: op.into(), op: op.into(),

View File

@ -21,6 +21,9 @@ pub struct ExprCall {
/// The name of the function. /// The name of the function.
pub name: Spanned<Ident>, pub name: Spanned<Ident>,
/// The arguments to the function. /// The arguments to the function.
///
/// In case of a bracketed invocation with a body, the body is _not_
/// included in the span for the sake of clearer error messages.
pub args: Spanned<LitDict>, pub args: Spanned<LitDict>,
} }

View File

@ -8,4 +8,4 @@ pub use expr::*;
pub use lit::*; pub use lit::*;
pub use tree::*; pub use tree::*;
use super::*; use super::{Ident, SpanVec, Spanned};