From 3150fd56437ecf8b2a5902c18e3f9ace800b768c Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 3 Feb 2020 12:22:02 +0100 Subject: [PATCH] =?UTF-8?q?Better=20Debug/Display=20and=20Derives=20?= =?UTF-8?q?=F0=9F=A7=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/error.rs | 2 +- src/export/pdf.rs | 228 ++++++++++++++++++++++------------------ src/layout/actions.rs | 14 ++- src/layout/line.rs | 10 +- src/layout/mod.rs | 4 +- src/layout/model.rs | 5 +- src/layout/stack.rs | 10 +- src/layout/text.rs | 3 +- src/lib.rs | 27 +++-- src/library/mod.rs | 1 - src/library/spacing.rs | 2 +- src/macros.rs | 74 ------------- src/size.rs | 92 +++++++--------- src/style.rs | 29 +++-- src/syntax/expr.rs | 110 +++++++------------ src/syntax/func/maps.rs | 2 +- src/syntax/mod.rs | 4 +- src/syntax/parsing.rs | 7 +- src/syntax/scope.rs | 7 +- src/syntax/span.rs | 47 +++------ src/syntax/tokens.rs | 3 +- tests/src/layouter.rs | 2 +- 22 files changed, 288 insertions(+), 395 deletions(-) delete mode 100644 src/macros.rs diff --git a/src/error.rs b/src/error.rs index b08d27dd9..1eb48debb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub struct Error { } /// How severe / important an error is. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] pub enum Severity { /// Something in the code is not good. Warning, diff --git a/src/export/pdf.rs b/src/export/pdf.rs index f877d4584..5c38084be 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -1,6 +1,8 @@ //! Exporting of layouts into _PDF_ documents. use std::collections::{HashMap, HashSet}; +use std::error::Error; +use std::fmt::{self, Display, Formatter}; use std::io::{self, Write}; use tide::{PdfWriter, Rect, Ref, Trailer, Version}; @@ -74,8 +76,8 @@ impl<'a, W: Write> PdfExporter<'a, W> { font_loader: &GlobalFontLoader, target: W, ) -> PdfResult> { - let (fonts, font_remap) = Self::subset_fonts(layouts, font_loader)?; - let offsets = Self::calculate_offsets(layouts.len(), fonts.len()); + let (fonts, font_remap) = subset_fonts(layouts, font_loader)?; + let offsets = calculate_offsets(layouts.len(), fonts.len()); Ok(PdfExporter { writer: PdfWriter::new(target), @@ -86,95 +88,6 @@ impl<'a, W: Write> PdfExporter<'a, W> { }) } - /// Subsets all fonts and assign a new PDF-internal index to each one. The - /// returned hash map maps the old indices (used by the layouts) to the new - /// one used in the PDF. The new ones index into the returned vector of - /// owned fonts. - fn subset_fonts( - layouts: &'a MultiLayout, - font_loader: &GlobalFontLoader, - ) -> PdfResult<(Vec, HashMap)> { - let mut fonts = Vec::new(); - let mut font_chars: HashMap> = HashMap::new(); - let mut old_to_new: HashMap = HashMap::new(); - let mut new_to_old: HashMap = HashMap::new(); - let mut active_font = FontIndex::MAX; - - // We want to find out which fonts are used at all and which chars are - // used for those. We use this information to create subsetted fonts. - for layout in layouts { - for action in &layout.actions { - match action { - LayoutAction::WriteText(text) => { - font_chars - .entry(active_font) - .or_insert_with(HashSet::new) - .extend(text.chars()); - }, - - LayoutAction::SetFont(index, _) => { - active_font = *index; - - let next_id = old_to_new.len(); - let new_id = *old_to_new - .entry(active_font) - .or_insert(next_id); - - new_to_old - .entry(new_id) - .or_insert(active_font); - }, - - _ => {} - } - } - } - - let num_fonts = old_to_new.len(); - let mut font_loader = font_loader.borrow_mut(); - - // All tables not listed here are dropped. - let tables: Vec<_> = [ - b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp", - b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf", - ].iter().map(|&s| Tag(*s)).collect(); - - // Do the subsetting. - for index in 0 .. num_fonts { - let old_index = new_to_old[&index]; - let font = font_loader.get_with_index(old_index); - - let chars = font_chars[&old_index].iter().cloned(); - let subsetted = match font.subsetted(chars, tables.iter().copied()) { - Ok(data) => Font::from_bytes(data)?, - Err(_) => font.clone(), - }; - - fonts.push(subsetted); - } - - Ok((fonts, old_to_new)) - } - - /// We need to know in advance which IDs to use for which objects to - /// cross-reference them. Therefore, we calculate the indices in the - /// beginning. - fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets { - let catalog = 1; - let page_tree = catalog + 1; - let pages = (page_tree + 1, page_tree + layout_count as Ref); - let contents = (pages.1 + 1, pages.1 + layout_count as Ref); - let font_offsets = (contents.1 + 1, contents.1 + 5 * font_count as Ref); - - Offsets { - catalog, - page_tree, - pages, - contents, - fonts: font_offsets, - } - } - /// Write everything (writing entry point). fn write(&mut self) -> PdfResult { self.writer.write_header(Version::new(1, 7))?; @@ -395,12 +308,100 @@ impl<'a, W: Write> PdfExporter<'a, W> { } } +/// Subsets all fonts and assign a new PDF-internal index to each one. The +/// returned hash map maps the old indices (used by the layouts) to the new one +/// used in the PDF. The new ones index into the returned vector of owned fonts. +fn subset_fonts( + layouts: &MultiLayout, + font_loader: &GlobalFontLoader, +) -> PdfResult<(Vec, HashMap)> { + let mut fonts = Vec::new(); + let mut font_chars: HashMap> = HashMap::new(); + let mut old_to_new: HashMap = HashMap::new(); + let mut new_to_old: HashMap = HashMap::new(); + let mut active_font = FontIndex::MAX; + + // We want to find out which fonts are used at all and which chars are used + // for those. We use this information to create subsetted fonts. + for layout in layouts { + for action in &layout.actions { + match action { + LayoutAction::WriteText(text) => { + font_chars + .entry(active_font) + .or_insert_with(HashSet::new) + .extend(text.chars()); + }, + + LayoutAction::SetFont(index, _) => { + active_font = *index; + + let next_id = old_to_new.len(); + let new_id = *old_to_new + .entry(active_font) + .or_insert(next_id); + + new_to_old + .entry(new_id) + .or_insert(active_font); + }, + + _ => {} + } + } + } + + let num_fonts = old_to_new.len(); + let mut font_loader = font_loader.borrow_mut(); + + // All tables not listed here are dropped. + let tables: Vec<_> = [ + b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp", + b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf", + ].iter().map(|&s| Tag(*s)).collect(); + + // Do the subsetting. + for index in 0 .. num_fonts { + let old_index = new_to_old[&index]; + let font = font_loader.get_with_index(old_index); + + let chars = font_chars[&old_index].iter().cloned(); + let subsetted = match font.subsetted(chars, tables.iter().copied()) { + Ok(data) => Font::from_bytes(data)?, + Err(_) => font.clone(), + }; + + fonts.push(subsetted); + } + + Ok((fonts, old_to_new)) +} + +/// We need to know in advance which IDs to use for which objects to +/// cross-reference them. Therefore, we calculate the indices in the beginning. +fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets { + let catalog = 1; + let page_tree = catalog + 1; + let pages = (page_tree + 1, page_tree + layout_count as Ref); + let contents = (pages.1 + 1, pages.1 + layout_count as Ref); + let font_offsets = (contents.1 + 1, contents.1 + 5 * font_count as Ref); + + Offsets { + catalog, + page_tree, + pages, + contents, + fonts: font_offsets, + } +} + /// Create an iterator from a reference pair. -fn ids((start, end): (Ref, Ref)) -> impl Iterator { +fn ids((start, end): (Ref, Ref)) -> impl Iterator { start ..= end } /// The error type for _PDF_ exporting. +#[derive(Debug)] pub enum PdfExportError { /// An error occured while subsetting the font for the _PDF_. Font(LoadError), @@ -408,17 +409,34 @@ pub enum PdfExportError { Io(io::Error), } -error_type! { - self: PdfExportError, - res: PdfResult, - show: f => match self { - PdfExportError::Font(err) => err.fmt(f), - PdfExportError::Io(err) => err.fmt(f), - }, - source: match self { - PdfExportError::Font(err) => Some(err), - PdfExportError::Io(err) => Some(err), - }, - from: (err: io::Error, PdfExportError::Io(err)), - from: (err: LoadError, PdfExportError::Font(err)), +type PdfResult = Result; + +impl Error for PdfExportError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + PdfExportError::Font(err) => Some(err), + PdfExportError::Io(err) => Some(err), + } + } +} + +impl Display for PdfExportError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + PdfExportError::Font(err) => err.fmt(f), + PdfExportError::Io(err) => err.fmt(f), + } + } +} + +impl From for PdfExportError { + fn from(err: LoadError) -> PdfExportError { + PdfExportError::Font(err) + } +} + +impl From for PdfExportError { + fn from(err: io::Error) -> PdfExportError { + PdfExportError::Io(err) + } } diff --git a/src/layout/actions.rs b/src/layout/actions.rs index b61f2201f..314084e5f 100644 --- a/src/layout/actions.rs +++ b/src/layout/actions.rs @@ -1,7 +1,7 @@ //! Drawing and configuration actions composing layouts. use std::io::{self, Write}; -use std::fmt::{self, Display, Formatter}; +use std::fmt::{self, Debug, Formatter}; use toddle::query::FontIndex; use crate::size::{Size, Size2D}; @@ -11,7 +11,7 @@ use self::LayoutAction::*; /// A layouting action, which is the basic building block layouts are composed /// of. -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum LayoutAction { /// Move to an absolute position. MoveAbsolute(Size2D), @@ -34,20 +34,18 @@ impl Serialize for LayoutAction { } } -impl Display for LayoutAction { +impl Debug for LayoutAction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { use LayoutAction::*; match self { MoveAbsolute(s) => write!(f, "move {} {}", s.x, s.y), - SetFont(i, s) => write!(f, "font {} {} {}", i.id, i.variant, s), + SetFont(i, s) => write!(f, "font {}_{} {}", i.id, i.variant, s), WriteText(s) => write!(f, "write \"{}\"", s), - DebugBox(s) => write!(f, "box {}", s), + DebugBox(s) => write!(f, "box {} {}", s.x, s.y), } } } -debug_display!(LayoutAction); - /// A sequence of layouting actions. /// /// The sequence of actions is optimized as the actions are added. For example, @@ -60,7 +58,7 @@ debug_display!(LayoutAction); /// `add_layout` method, which allows a layout to be added at a position, /// effectively translating all movement actions inside the layout by the /// position. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct LayoutActions { origin: Size2D, actions: Vec, diff --git a/src/layout/line.rs b/src/layout/line.rs index 2c8e45f28..18ace29fe 100644 --- a/src/layout/line.rs +++ b/src/layout/line.rs @@ -13,7 +13,7 @@ use super::*; /// Performs the line layouting. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct LineLayouter { /// The context for layouting. ctx: LineContext, @@ -46,7 +46,7 @@ pub struct LineContext { /// A line run is a sequence of boxes with the same alignment that are arranged /// in a line. A real line can consist of multiple runs with different /// alignments. -#[derive(Debug, Clone)] +#[derive(Debug)] struct LineRun { /// The so-far accumulated layouts in the line. layouts: Vec<(Size, Layout)>, @@ -102,7 +102,7 @@ impl LineLayouter { } else if layout.alignment.primary > alignment.primary { let mut rest_run = LineRun::new(); - let usable = self.stack.usable().get_primary(axes); + let usable = self.stack.usable().primary(axes); rest_run.usable = Some(match layout.alignment.primary { Alignment::Origin => unreachable!("origin > x"), Alignment::Center => usable - 2 * self.run.size.x, @@ -228,7 +228,7 @@ impl LineLayouter { /// a function how much space it has to layout itself. pub fn remaining(&self) -> LayoutSpaces { let mut spaces = self.stack.remaining(); - *spaces[0].dimensions.get_secondary_mut(self.ctx.axes) + *spaces[0].dimensions.secondary_mut(self.ctx.axes) -= self.run.size.y; spaces } @@ -262,7 +262,7 @@ impl LineLayouter { true => offset, false => self.run.size.x - offset - - layout.dimensions.get_primary(self.ctx.axes), + - layout.dimensions.primary(self.ctx.axes), }; let pos = Size2D::with_x(x); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index bcabf7f30..8c120c6b6 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -33,7 +33,7 @@ pub mod prelude { pub type MultiLayout = Vec; /// A finished box with content at fixed positions. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Layout { /// The size of the box. pub dimensions: Size2D, @@ -91,7 +91,7 @@ impl Serialize for MultiLayout { pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>; /// The space into which content is laid out. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct LayoutSpace { /// The maximum size of the box to layout in. pub dimensions: Size2D, diff --git a/src/layout/model.rs b/src/layout/model.rs index 2eac9a8c9..d23968d8e 100644 --- a/src/layout/model.rs +++ b/src/layout/model.rs @@ -5,7 +5,6 @@ use std::future::Future; use std::pin::Pin; use smallvec::smallvec; -use toddle::query::{SharedFontLoader, FontProvider}; use crate::GlobalFontLoader; use crate::error::Errors; @@ -19,6 +18,7 @@ use super::*; /// Performs the model layouting. +#[derive(Debug)] pub struct ModelLayouter<'a> { ctx: LayoutContext<'a>, layouter: LineLayouter, @@ -52,6 +52,7 @@ pub struct LayoutContext<'a> { } /// The result of layouting: Some layouted things and a list of errors. +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Layouted { /// The result of the layouting process. pub output: T, @@ -63,7 +64,7 @@ pub struct Layouted { pub type Commands<'a> = Vec>; /// Commands issued to the layouting engine by models. -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Command<'a> { /// Layout the given model in the current context (i.e. not nested). The /// content of the model is not laid out into a separate box and then added, diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 8f76ccbf3..891815e97 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -27,7 +27,7 @@ use super::*; /// Performs the stack layouting. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct StackLayouter { /// The context for layouting. ctx: StackContext, @@ -57,7 +57,7 @@ pub struct StackContext { /// A layout space composed of subspaces which can have different axes and /// alignments. -#[derive(Debug, Clone)] +#[derive(Debug)] struct Space { /// The index of this space in the list of spaces. index: usize, @@ -134,7 +134,7 @@ impl StackLayouter { // A hard space is simply an empty box. SpacingKind::Hard => { // Reduce the spacing such that it definitely fits. - spacing.min_eq(self.space.usable.get_secondary(self.ctx.axes)); + spacing.min_eq(self.space.usable.secondary(self.ctx.axes)); let dimensions = Size2D::with_y(spacing); self.update_metrics(dimensions); @@ -179,7 +179,7 @@ impl StackLayouter { self.space.size = size.specialized(axes); self.space.extra = extra.specialized(axes); - *self.space.usable.get_secondary_mut(axes) -= dimensions.y; + *self.space.usable.secondary_mut(axes) -= dimensions.y; } /// Update the rulers to account for the new layout. Returns true if a @@ -328,7 +328,7 @@ impl StackLayouter { // the usable space for following layouts at it's origin by its // extent along the secondary axis. *bound.get_mut(axes.secondary, Origin) - += axes.secondary.factor() * layout.dimensions.get_secondary(*axes); + += axes.secondary.factor() * layout.dimensions.secondary(*axes); } // ------------------------------------------------------------------ // diff --git a/src/layout/text.rs b/src/layout/text.rs index 6b512a07e..c6fa45d19 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -14,6 +14,7 @@ use super::*; /// Performs the text layouting. +#[derive(Debug)] struct TextLayouter<'a> { ctx: TextContext<'a>, text: &'a str, @@ -24,7 +25,7 @@ struct TextLayouter<'a> { } /// The context for text layouting. -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct TextContext<'a> { /// The font loader to retrieve fonts from when typesetting text /// using [`layout_text`]. diff --git a/src/lib.rs b/src/lib.rs index 07a60e356..afb327666 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,14 +16,10 @@ //! format is [_PDF_](crate::export::pdf). Alternatively, the layout can be //! serialized to pass it to a suitable renderer. -#![allow(unused)] - pub use toddle; use std::cell::RefCell; use std::error::Error; -use std::fmt::{self, Debug, Formatter}; -use std::io::Cursor; use async_trait::async_trait; use smallvec::smallvec; @@ -35,8 +31,15 @@ use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::syntax::{SyntaxModel, Scope, ParseContext, Parsed, parse}; use crate::syntax::span::Position; -#[macro_use] -mod macros; + +/// Declare a module and reexport all its contents. +macro_rules! pub_use_mod { + ($name:ident) => { + mod $name; + pub use $name::*; + }; +} + pub mod error; pub mod export; #[macro_use] @@ -51,6 +54,7 @@ pub mod syntax; /// Transforms source code into typesetted layouts. /// /// A typesetter can be configured through various methods. +#[derive(Debug)] pub struct Typesetter { /// The font loader shared by all typesetting processes. loader: GlobalFontLoader, @@ -79,16 +83,16 @@ impl Typesetter { } } - /// Set the base page style. - pub fn set_page_style(&mut self, style: PageStyle) { - self.style.page = style; - } - /// Set the base text style. pub fn set_text_style(&mut self, style: TextStyle) { self.style.text = style; } + /// Set the base page style. + pub fn set_page_style(&mut self, style: PageStyle) { + self.style.page = style; + } + /// A reference to the backing font loader. pub fn loader(&self) -> &GlobalFontLoader { &self.loader @@ -134,6 +138,7 @@ impl Typesetter { /// Wraps a font provider and transforms its errors into boxed trait objects. /// This enables font providers that do not return boxed errors to be used with /// the typesetter. +#[derive(Debug)] pub struct DynErrorProvider

{ provider: P, } diff --git a/src/library/mod.rs b/src/library/mod.rs index 1a36fcc72..f6666174a 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -1,6 +1,5 @@ //! The _Typst_ standard library. -use toddle::query::FontProvider; use crate::syntax::Scope; use crate::func::prelude::*; diff --git a/src/library/spacing.rs b/src/library/spacing.rs index b948153d0..d77285c5d 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -67,7 +67,7 @@ function! { /// The different kinds of content that can be spaced. Used as a metadata type /// for the [`ContentSpacingFunc`]. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[allow(missing_docs)] pub enum ContentKind { Word, diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 7476de4b9..000000000 --- a/src/macros.rs +++ /dev/null @@ -1,74 +0,0 @@ -//! Auxiliary macros. - - -/// Create trait implementations for an error type. -macro_rules! error_type { - ( - $this:ident: $type:ident, - $(res: $res:ident,)* - show: $f:ident => $show:expr, - $(source: $source:expr,)* - $(from: ($err:ident: $from:path, $conv:expr),)* - ) => { - // Possibly create a result type. - $(type $res = std::result::Result;)* - - impl std::fmt::Display for $type { - fn fmt(&$this, $f: &mut std::fmt::Formatter) -> std::fmt::Result { - $show - } - } - - debug_display!($type); - - impl std::error::Error for $type { - // The source method is only generated if an implementation was given. - $(fn source(&$this) -> Option<&(dyn std::error::Error + 'static)> { - $source - })* - } - - // Create any number of from implementations. - $(impl From<$from> for $type { - fn from($err: $from) -> $type { - $conv - } - })* - }; -} - -/// Create a `Debug` implementation from a `Display` implementation. -macro_rules! debug_display { - ($type:ident) => ( - impl std::fmt::Debug for $type { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::Display::fmt(self, f) - } - } - ); - ($type:ident; $generics:tt where $($bounds:tt)*) => ( - impl<$generics> std::fmt::Debug for $type<$generics> where $($bounds)* { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::Display::fmt(self, f) - } - } - ); -} - -/// Declare a module and reexport all its contents. -macro_rules! pub_use_mod { - ($name:ident) => { - mod $name; - pub use $name::*; - }; -} - -/// Whether an expression matches a set of patterns. -macro_rules! matches { - ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => { - match $expression { - $( $pattern )|+ $( if $guard )? => true, - _ => false - } - } -} diff --git a/src/size.rs b/src/size.rs index 402910179..79b987746 100644 --- a/src/size.rs +++ b/src/size.rs @@ -76,7 +76,11 @@ impl Display for Size { } } -debug_display!(Size); +impl Debug for Size { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(self, f) + } +} impl Neg for Size { type Output = Size; @@ -115,12 +119,16 @@ impl Display for ScaleSize { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { ScaleSize::Absolute(size) => write!(f, "{}", size), - ScaleSize::Scaled(scale) => write!(f, "x{}", scale), + ScaleSize::Scaled(scale) => write!(f, "{}%", scale * 100.0), } } } -debug_display!(ScaleSize); +impl Debug for ScaleSize { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(self, f) + } +} /// A scale size that is scaled by the font size. pub type FSize = ScaleSize; @@ -129,7 +137,7 @@ pub type FSize = ScaleSize; pub type PSize = ScaleSize; /// A value in two dimensions. -#[derive(Default, Copy, Clone, PartialEq)] +#[derive(Default, Copy, Clone, Eq, PartialEq)] pub struct Value2D { /// The horizontal component. pub x: T, @@ -137,7 +145,7 @@ pub struct Value2D { pub y: T, } -impl Value2D { +impl Value2D { /// Create a new 2D-value from two values. pub fn new(x: T, y: T) -> Value2D { Value2D { x, y } } @@ -164,7 +172,7 @@ impl Value2D { } /// Create a 2D-value with `x` and `y` set to the same value `s`. - pub fn with_all(s: T) -> Value2D { Value2D { x: s, y: s } } + pub fn with_all(s: T) -> Value2D { Value2D { x: s.clone(), y: s } } /// Get the specificed component. pub fn get(self, axis: SpecificAxis) -> T { @@ -183,22 +191,22 @@ impl Value2D { } /// Return the primary value of this specialized 2D-value. - pub fn get_primary(self, axes: LayoutAxes) -> T { + pub fn primary(self, axes: LayoutAxes) -> T { if axes.primary.axis() == Horizontal { self.x } else { self.y } } /// Borrow the primary value of this specialized 2D-value mutably. - pub fn get_primary_mut(&mut self, axes: LayoutAxes) -> &mut T { + pub fn primary_mut(&mut self, axes: LayoutAxes) -> &mut T { if axes.primary.axis() == Horizontal { &mut self.x } else { &mut self.y } } /// Return the secondary value of this specialized 2D-value. - pub fn get_secondary(self, axes: LayoutAxes) -> T { + pub fn secondary(self, axes: LayoutAxes) -> T { if axes.primary.axis() == Horizontal { self.y } else { self.x } } /// Borrow the secondary value of this specialized 2D-value mutably. - pub fn get_secondary_mut(&mut self, axes: LayoutAxes) -> &mut T { + pub fn secondary_mut(&mut self, axes: LayoutAxes) -> &mut T { if axes.primary.axis() == Horizontal { &mut self.y } else { &mut self.x } } @@ -227,15 +235,12 @@ impl Value2D { } } -impl Display for Value2D where T: Display { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "[{}, {}]", self.x, self.y) - } -} - impl Debug for Value2D where T: Debug { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "[{:?}, {:?}]", self.x, self.y) + f.debug_list() + .entry(&self.x) + .entry(&self.y) + .finish() } } @@ -294,6 +299,7 @@ impl Neg for Size2D { /// A value that is stretchable in an interval from a minimal through an optimal /// to a maximal value. +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] pub struct StretchValue { /// The minimum this value can be stretched to. pub min: T, @@ -310,23 +316,11 @@ impl StretchValue { } } -impl Display for StretchValue where T: Display { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({}, {}, {})", self.min, self.opt, self.max) - } -} - -impl Debug for StretchValue where T: Debug { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({:?}, {:?}, {:?})", self.min, self.opt, self.max) - } -} - /// A size that is stretchable. pub type StretchSize = StretchValue; /// A value in four dimensions. -#[derive(Default, Copy, Clone, PartialEq)] +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] pub struct ValueBox { /// The left extent. pub left: T, @@ -389,20 +383,6 @@ impl ValueBox { } } -impl Display for ValueBox where T: Display { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "[left: {}, top: {}, right: {}, bottom: {}]", - self.left, self.top, self.right, self.bottom) - } -} - -impl Debug for ValueBox where T: Debug { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "[left: {:?}, top: {:?}, right: {:?}, bottom: {:?}]", - self.left, self.top, self.right, self.bottom) - } -} - /// A size in four dimensions. pub type SizeBox = ValueBox; @@ -416,14 +396,6 @@ impl SizeBox { }; } -/// An error which can be returned when parsing a size. -pub struct ParseSizeError; - -error_type! { - self: ParseSizeError, - show: f => write!(f, "failed to parse size"), -} - impl FromStr for Size { type Err = ParseSizeError; @@ -433,16 +405,28 @@ impl FromStr for Size { _ if src.ends_with("mm") => Size::mm, _ if src.ends_with("cm") => Size::cm, _ if src.ends_with("in") => Size::inches, - _ => return Err(ParseSizeError), + _ => return Err(ParseSizeError(())), }; Ok(func(src[..src.len() - 2] .parse::() - .map_err(|_| ParseSizeError)?)) + .map_err(|_| ParseSizeError(()))?)) } } +/// An error which can be returned when parsing a size. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ParseSizeError(()); + +impl std::error::Error for ParseSizeError {} + +impl Display for ParseSizeError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str("invalid string for size") + } +} + macro_rules! implement_traits { ($ty:ident, $t:ident, $o:ident reflexive {$( diff --git a/src/style.rs b/src/style.rs index e49e9680c..075baa5ac 100644 --- a/src/style.rs +++ b/src/style.rs @@ -6,16 +6,16 @@ use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize}; /// Defines properties of pages and text. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct LayoutStyle { - /// The style for pages. - pub page: PageStyle, /// The style for text. pub text: TextStyle, + /// The style for pages. + pub page: PageStyle, } /// Defines which fonts to use and how to space text. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct TextStyle { /// A tree of font names and generic family names. pub fallback: FallbackTree, @@ -87,7 +87,7 @@ impl Default for TextStyle { } /// Defines the size and margins of a page. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct PageStyle { /// The class of this page. pub class: PaperClass, @@ -159,12 +159,19 @@ impl PaperClass { /// The default margins for this page class. pub fn default_margins(self) -> ValueBox { use PaperClass::*; + let values = |l, t, r, b| ValueBox::new( + PSize::Scaled(l), + PSize::Scaled(t), + PSize::Scaled(r), + PSize::Scaled(b), + ); + match self { - Custom => ValueBox::with_all(PSize::Scaled(0.1)), - Base => ValueBox::new(PSize::Scaled(0.119), PSize::Scaled(0.0569), PSize::Scaled(0.0476), PSize::Scaled(0.0569)), - US => ValueBox::new(PSize::Scaled(0.176), PSize::Scaled(0.1092), PSize::Scaled(0.176), PSize::Scaled(0.091)), - Newspaper => ValueBox::new(PSize::Scaled(0.0455), PSize::Scaled(0.0587), PSize::Scaled(0.0455), PSize::Scaled(0.0294)), - Book => ValueBox::new(PSize::Scaled(0.12), PSize::Scaled(0.0852), PSize::Scaled(0.15), PSize::Scaled(0.0965)), + Custom => values(0.1190, 0.0842, 0.1190, 0.0842), + Base => values(0.1190, 0.0842, 0.1190, 0.0842), + US => values(0.1760, 0.1092, 0.1760, 0.0910), + Newspaper => values(0.0455, 0.0587, 0.0455, 0.0294), + Book => values(0.1200, 0.0852, 0.1500, 0.0965), } } } @@ -182,7 +189,7 @@ macro_rules! papers { }; (@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => { - #[doc = "Paper with the names `"] + #[doc = "Paper with name `"] #[doc = $names] #[doc = "`."] pub const $var: Paper = Paper { diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 879e5faec..e5c9489e4 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -1,6 +1,6 @@ //! Expressions in function headers. -use std::fmt::{self, Display, Formatter}; +use std::fmt::{self, Debug, Formatter}; use crate::error::Errors; use crate::size::Size; @@ -44,6 +44,21 @@ impl Expr { } } +impl Debug for Expr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use Expr::*; + match self { + Ident(i) => i.fmt(f), + Str(s) => s.fmt(f), + Number(n) => n.fmt(f), + Size(s) => s.fmt(f), + Bool(b) => b.fmt(f), + Tuple(t) => t.fmt(f), + Object(o) => o.fmt(f), + } + } +} + /// A unicode identifier. /// /// The identifier must be valid! This is checked in [`Ident::new`] or @@ -73,13 +88,19 @@ impl Ident { } } +impl Debug for Ident { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(&self.0) + } +} + /// An untyped sequence of expressions. /// /// # Example /// ```typst /// (false, 12cm, "hi") /// ``` -#[derive(Clone, PartialEq)] +#[derive(Default, Clone, PartialEq)] pub struct Tuple { /// The elements of the tuple. pub items: Vec>, @@ -124,6 +145,16 @@ impl Tuple { } } +impl Debug for Tuple { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let mut tuple = f.debug_tuple(""); + for item in &self.items { + tuple.field(item); + } + tuple.finish() + } +} + /// A key-value collection of identifiers and associated expressions. /// /// The pairs themselves are not spanned, but the combined spans can easily be @@ -134,14 +165,14 @@ impl Tuple { /// ```typst /// { fit: false, size: 12cm, items: (1, 2, 3) } /// ``` -#[derive(Clone, PartialEq)] +#[derive(Default, Clone, PartialEq)] pub struct Object { /// The key-value pairs of the object. pub pairs: Vec, } /// A key-value pair in an object. -#[derive(Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct Pair { /// The key part. /// ```typst @@ -247,73 +278,10 @@ impl Object { } } -impl Display for Expr { +impl Debug for Object { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - use Expr::*; - match self { - Ident(i) => write!(f, "{}", i), - Str(s) => write!(f, "{:?}", s), - Number(n) => write!(f, "{}", n), - Size(s) => write!(f, "{}", s), - Bool(b) => write!(f, "{}", b), - Tuple(t) => write!(f, "{}", t), - Object(o) => write!(f, "{}", o), - } + f.debug_map() + .entries(self.pairs.iter().map(|p| (&p.key.v, &p.value.v))) + .finish() } } - -impl Display for Ident { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Display for Tuple { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "(")?; - - let mut first = true; - for item in &self.items { - if !first { - write!(f, ", ")?; - } - write!(f, "{}", item.v)?; - first = false; - } - - write!(f, ")") - } -} - -impl Display for Object { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if self.pairs.len() == 0 { - return write!(f, "{{}}"); - } - - write!(f, "{{ ")?; - - let mut first = true; - for pair in &self.pairs { - if !first { - write!(f, ", ")?; - } - write!(f, "{}", pair)?; - first = false; - } - - write!(f, " }}") - } -} - -impl Display for Pair { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}: {}", self.key.v, self.value.v) - } -} - -debug_display!(Expr); -debug_display!(Ident); -debug_display!(Tuple); -debug_display!(Object); -debug_display!(Pair); diff --git a/src/syntax/func/maps.rs b/src/syntax/func/maps.rs index eb4c8394e..691b3d36a 100644 --- a/src/syntax/func/maps.rs +++ b/src/syntax/func/maps.rs @@ -16,7 +16,7 @@ use super::*; /// list that needs to be passed to those functions. /// /// All entries need to have span information to enable the error reporting. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct DedupMap where K: Eq { map: Vec>, } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 9d83e5468..7f4052ab1 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -30,7 +30,7 @@ pub trait Model: Debug + ModelBounds { } /// A tree representation of source code. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct SyntaxModel { /// The syntactical elements making up this model. pub nodes: SpanVec, @@ -97,7 +97,7 @@ impl PartialEq for Node { } /// Decorations for semantic syntax highlighting. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)] #[serde(rename_all = "camelCase")] pub enum Decoration { /// A valid function name: diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs index 1e8cc74f0..a7f39640d 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -18,6 +18,7 @@ pub struct ParseContext<'a> { /// The result of parsing: Some parsed thing, errors and decorations for syntax /// highlighting. +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Parsed { /// The result of the parsing process. pub output: T, @@ -321,9 +322,11 @@ impl<'s> FuncParser<'s> { /// Skip all whitespace/comment tokens. fn skip_whitespace(&mut self) { - self.eat_until(|t| !matches!(t, + self.eat_until(|t| match t { Token::Space(_) | Token::LineComment(_) | - Token::BlockComment(_)), false) + Token::BlockComment(_) => false, + _ => true, + }, false) } /// Add an error about an expected `thing` which was not found, showing diff --git a/src/syntax/scope.rs b/src/syntax/scope.rs index a6d27c1ec..551d06846 100644 --- a/src/syntax/scope.rs +++ b/src/syntax/scope.rs @@ -13,7 +13,7 @@ use super::Model; /// A map from identifiers to function parsers. pub struct Scope { parsers: HashMap>, - fallback: Box + fallback: Box, } impl Scope { @@ -63,8 +63,9 @@ impl Scope { impl Debug for Scope { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Scope ")?; - write!(f, "{:?}", self.parsers.keys()) + f.debug_set() + .entries(self.parsers.keys()) + .finish() } } diff --git a/src/syntax/span.rs b/src/syntax/span.rs index ad1358cfd..7a051d999 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -1,6 +1,6 @@ //! Spans map elements to the part of source code they originate from. -use std::fmt::{self, Debug, Display, Formatter}; +use std::fmt::{self, Debug, Formatter}; use std::ops::{Add, Sub}; use serde::Serialize; @@ -60,6 +60,12 @@ impl Sub for Position { } } +impl Debug for Position { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}:{}", self.line, self.column) + } +} + /// Locates a slice of source code. #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] pub struct Span { @@ -103,8 +109,14 @@ impl Span { } } +impl Debug for Span { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "({:?} -> {:?})", self.start, self.end) + } +} + /// A value with the span it corresponds to in the source code. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)] pub struct Spanned { /// The value. pub v: T, @@ -143,34 +155,3 @@ pub type SpanVec = Vec>; pub fn offset_spans(vec: SpanVec, start: Position) -> impl Iterator> { vec.into_iter().map(move |s| s.map_span(|span| span.offset(start))) } - -impl Display for Position { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}:{}", self.line, self.column) - } -} - -impl Display for Span { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({}, {})", self.start, self.end) - } -} - -impl Display for Spanned where T: std::fmt::Display { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({}, {}, ", self.span.start, self.span.end)?; - self.v.fmt(f)?; - write!(f, ")") - } -} - -impl Debug for Spanned where T: std::fmt::Debug { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({}, {}, ", self.span.start, self.span.end)?; - self.v.fmt(f)?; - write!(f, ")") - } -} - -debug_display!(Position); -debug_display!(Span); diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 40d2a5268..747b6b933 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -122,6 +122,7 @@ impl<'s> Token<'s> { } /// An iterator over the tokens of a string of source code. +#[derive(Debug)] pub struct Tokens<'s> { src: &'s str, mode: TokenizationMode, @@ -133,7 +134,7 @@ pub struct Tokens<'s> { /// Whether to tokenize in header mode which yields expression, comma and /// similar tokens or in body mode which yields text and star, underscore, /// backtick tokens. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[allow(missing_docs)] pub enum TokenizationMode { Header, diff --git a/tests/src/layouter.rs b/tests/src/layouter.rs index ab95224cf..23110fe7b 100644 --- a/tests/src/layouter.rs +++ b/tests/src/layouter.rs @@ -52,7 +52,7 @@ fn main() -> DynResult<()> { for (name, src) in filtered { panic::catch_unwind(|| { if let Err(e) = test(&name, &src) { - println!("error: {}", e); + println!("error: {:?}", e); } }).ok(); }