Prune derives

This commit is contained in:
Laurenz 2021-08-21 16:38:51 +02:00
parent f38eb10c2b
commit 0dd4ae0a7a
48 changed files with 434 additions and 540 deletions

View File

@ -12,14 +12,6 @@ pub enum Color {
Rgba(RgbaColor), Rgba(RgbaColor),
} }
impl Display for Color {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Rgba(c) => Display::fmt(c, f),
}
}
}
impl Debug for Color { impl Debug for Color {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
@ -28,6 +20,14 @@ impl Debug for Color {
} }
} }
impl Display for Color {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Rgba(c) => Display::fmt(c, f),
}
}
}
/// An 8-bit RGBA color. /// An 8-bit RGBA color.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct RgbaColor { pub struct RgbaColor {
@ -97,16 +97,6 @@ impl FromStr for RgbaColor {
} }
} }
impl Display for RgbaColor {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?;
if self.a != 255 {
write!(f, "{:02x}", self.a)?;
}
Ok(())
}
}
impl Debug for RgbaColor { impl Debug for RgbaColor {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if f.alternate() { if f.alternate() {
@ -121,18 +111,28 @@ impl Debug for RgbaColor {
} }
} }
impl Display for RgbaColor {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?;
if self.a != 255 {
write!(f, "{:02x}", self.a)?;
}
Ok(())
}
}
/// The error when parsing an [`RgbaColor`] from a string fails. /// The error when parsing an [`RgbaColor`] from a string fails.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ParseRgbaError; pub struct ParseRgbaError;
impl std::error::Error for ParseRgbaError {} impl Display for ParseRgbaError {
impl fmt::Display for ParseRgbaError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("invalid color") f.pad("invalid color")
} }
} }
impl std::error::Error for ParseRgbaError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -22,7 +22,7 @@ pub type TypResult<T> = Result<T, Box<Vec<Error>>>;
pub type StrResult<T> = Result<T, String>; pub type StrResult<T> = Result<T, String>;
/// An error in a source file. /// An error in a source file.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Error { pub struct Error {
/// The erroneous location in the source code. /// The erroneous location in the source code.
pub span: Span, pub span: Span,

View File

@ -137,18 +137,18 @@ impl AddAssign for Array {
} }
} }
impl FromIterator<Value> for Array {
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
Self(Rc::new(iter.into_iter().collect()))
}
}
impl Extend<Value> for Array { impl Extend<Value> for Array {
fn extend<T: IntoIterator<Item = Value>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = Value>>(&mut self, iter: T) {
Rc::make_mut(&mut self.0).extend(iter); Rc::make_mut(&mut self.0).extend(iter);
} }
} }
impl FromIterator<Value> for Array {
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
Self(Rc::new(iter.into_iter().collect()))
}
}
impl IntoIterator for Array { impl IntoIterator for Array {
type Item = Value; type Item = Value;
type IntoIter = std::vec::IntoIter<Value>; type IntoIter = std::vec::IntoIter<Value>;

View File

@ -5,7 +5,6 @@ use crate::syntax::visit::{immutable::visit_expr, Visit};
use crate::syntax::{Expr, Ident}; use crate::syntax::{Expr, Ident};
/// A visitor that captures variable slots. /// A visitor that captures variable slots.
#[derive(Debug)]
pub struct CapturesVisitor<'a> { pub struct CapturesVisitor<'a> {
external: &'a Scopes<'a>, external: &'a Scopes<'a>,
internal: Scopes<'a>, internal: Scopes<'a>,

View File

@ -82,6 +82,12 @@ fn missing_key(key: &Str) -> String {
format!("dictionary does not contain key: {}", key) format!("dictionary does not contain key: {}", key)
} }
impl Debug for Dict {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_map().entries(self.0.iter()).finish()
}
}
impl Display for Dict { impl Display for Dict {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_char('(')?; f.write_char('(')?;
@ -100,12 +106,6 @@ impl Display for Dict {
} }
} }
impl Debug for Dict {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_map().entries(self.0.iter()).finish()
}
}
impl Add for Dict { impl Add for Dict {
type Output = Self; type Output = Self;
@ -124,18 +124,18 @@ impl AddAssign for Dict {
} }
} }
impl FromIterator<(Str, Value)> for Dict {
fn from_iter<T: IntoIterator<Item = (Str, Value)>>(iter: T) -> Self {
Self(Rc::new(iter.into_iter().collect()))
}
}
impl Extend<(Str, Value)> for Dict { impl Extend<(Str, Value)> for Dict {
fn extend<T: IntoIterator<Item = (Str, Value)>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = (Str, Value)>>(&mut self, iter: T) {
Rc::make_mut(&mut self.0).extend(iter); Rc::make_mut(&mut self.0).extend(iter);
} }
} }
impl FromIterator<(Str, Value)> for Dict {
fn from_iter<T: IntoIterator<Item = (Str, Value)>>(iter: T) -> Self {
Self(Rc::new(iter.into_iter().collect()))
}
}
impl IntoIterator for Dict { impl IntoIterator for Dict {
type Item = (Str, Value); type Item = (Str, Value);
type IntoIter = std::collections::btree_map::IntoIter<Str, Value>; type IntoIter = std::collections::btree_map::IntoIter<Str, Value>;

View File

@ -38,6 +38,12 @@ impl Function {
} }
} }
impl Debug for Function {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Function").field("name", &self.0.name).finish()
}
}
impl Display for Function { impl Display for Function {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("<function")?; f.write_str("<function")?;
@ -49,12 +55,6 @@ impl Display for Function {
} }
} }
impl Debug for Function {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Function").field("name", &self.0.name).finish()
}
}
impl PartialEq for Function { impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
// We cast to thin pointers for comparison. // We cast to thin pointers for comparison.

View File

@ -13,6 +13,7 @@ mod scope;
mod state; mod state;
mod str; mod str;
mod template; mod template;
mod walk;
pub use self::str::*; pub use self::str::*;
pub use array::*; pub use array::*;
@ -23,25 +24,24 @@ pub use scope::*;
pub use state::*; pub use state::*;
pub use template::*; pub use template::*;
pub use value::*; pub use value::*;
pub use walk::*;
use std::cell::RefMut; use std::cell::RefMut;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write;
use std::io; use std::io;
use std::mem; use std::mem;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult};
use crate::geom::{Angle, Fractional, Gen, Length, Relative}; use crate::geom::{Angle, Fractional, Length, Relative};
use crate::image::ImageStore; use crate::image::ImageStore;
use crate::layout::{ParChild, ParNode, StackChild, StackNode};
use crate::loading::Loader; use crate::loading::Loader;
use crate::parse::parse; use crate::parse::parse;
use crate::source::{SourceId, SourceStore}; use crate::source::{SourceId, SourceStore};
use crate::syntax::visit::Visit; use crate::syntax::visit::Visit;
use crate::syntax::*; use crate::syntax::*;
use crate::util::{EcoString, RefMutExt}; use crate::util::RefMutExt;
use crate::Context; use crate::Context;
/// Evaluate a parsed source file into a module. /// Evaluate a parsed source file into a module.
@ -52,7 +52,7 @@ pub fn eval(ctx: &mut Context, source: SourceId, ast: &SyntaxTree) -> TypResult<
} }
/// An evaluated module, ready for importing or instantiation. /// An evaluated module, ready for importing or instantiation.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Default, Clone)]
pub struct Module { pub struct Module {
/// The top-level definitions that were bound in this module. /// The top-level definitions that were bound in this module.
pub scope: Scope, pub scope: Scope,
@ -717,128 +717,3 @@ impl Access for CallExpr {
}) })
} }
} }
/// Walk a syntax node and fill the currently built template.
pub trait Walk {
/// Walk the node.
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()>;
}
impl Walk for SyntaxTree {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
for node in self.iter() {
node.walk(ctx)?;
}
Ok(())
}
}
impl Walk for SyntaxNode {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
match self {
Self::Space => ctx.template.space(),
Self::Linebreak(_) => ctx.template.linebreak(),
Self::Parbreak(_) => ctx.template.parbreak(),
Self::Strong(_) => {
ctx.template.modify(|state| state.font_mut().strong ^= true);
}
Self::Emph(_) => {
ctx.template.modify(|state| state.font_mut().emph ^= true);
}
Self::Text(text) => ctx.template.text(text),
Self::Raw(raw) => raw.walk(ctx)?,
Self::Heading(heading) => heading.walk(ctx)?,
Self::List(list) => list.walk(ctx)?,
Self::Enum(enum_) => enum_.walk(ctx)?,
Self::Expr(expr) => match expr.eval(ctx)? {
Value::None => {}
Value::Int(v) => ctx.template.text(v.to_string()),
Value::Float(v) => ctx.template.text(v.to_string()),
Value::Str(v) => ctx.template.text(v),
Value::Template(v) => ctx.template += v,
// For values which can't be shown "naturally", we print the
// representation in monospace.
other => ctx.template.monospace(other.to_string()),
},
}
Ok(())
}
}
impl Walk for RawNode {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
if self.block {
ctx.template.parbreak();
}
ctx.template.monospace(&self.text);
if self.block {
ctx.template.parbreak();
}
Ok(())
}
}
impl Walk for HeadingNode {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
let level = self.level;
let body = self.body.eval(ctx)?;
ctx.template.parbreak();
ctx.template.save();
ctx.template.modify(move |state| {
let font = state.font_mut();
let upscale = 1.6 - 0.1 * level as f64;
font.size *= upscale;
font.strong = true;
});
ctx.template += body;
ctx.template.restore();
ctx.template.parbreak();
Ok(())
}
}
impl Walk for ListItem {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
let body = self.body.eval(ctx)?;
walk_item(ctx, '•'.into(), body);
Ok(())
}
}
impl Walk for EnumItem {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
let body = self.body.eval(ctx)?;
let mut label = EcoString::new();
write!(&mut label, "{}.", self.number.unwrap_or(1)).unwrap();
walk_item(ctx, label, body);
Ok(())
}
}
/// Walk a list or enum item, converting it into a stack.
fn walk_item(ctx: &mut EvalContext, label: EcoString, body: Template) {
ctx.template += Template::from_block(move |state| {
let label = ParNode {
dir: state.dirs.cross,
line_spacing: state.line_spacing(),
children: vec![ParChild::Text(
label.clone(),
state.aligns.cross,
Rc::clone(&state.font),
)],
};
StackNode {
dirs: Gen::new(state.dirs.main, state.dirs.cross),
children: vec![
StackChild::Any(label.into(), Gen::default()),
StackChild::Spacing((state.font.size / 2.0).into()),
StackChild::Any(body.to_stack(&state).into(), Gen::default()),
],
}
});
}

View File

@ -12,7 +12,7 @@ use crate::util::EcoString;
pub type Slot = Rc<RefCell<Value>>; pub type Slot = Rc<RefCell<Value>>;
/// A stack of scopes. /// A stack of scopes.
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone)]
pub struct Scopes<'a> { pub struct Scopes<'a> {
/// The active scope. /// The active scope.
pub top: Scope, pub top: Scope,
@ -65,7 +65,7 @@ impl<'a> Scopes<'a> {
} }
/// A map from variable names to variable slots. /// A map from variable names to variable slots.
#[derive(Default, Clone, PartialEq)] #[derive(Default, Clone)]
pub struct Scope { pub struct Scope {
/// The mapping from names to slots. /// The mapping from names to slots.
values: HashMap<EcoString, Slot>, values: HashMap<EcoString, Slot>,

View File

@ -63,7 +63,7 @@ impl Default for State {
} }
/// Defines page properties. /// Defines page properties.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct PageState { pub struct PageState {
/// The class of this page. /// The class of this page.
pub class: PaperClass, pub class: PaperClass,
@ -220,7 +220,7 @@ impl Default for FontState {
} }
/// Font family definitions. /// Font family definitions.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct FamilyState { pub struct FamilyState {
/// The user-defined list of font families. /// The user-defined list of font families.
pub list: Rc<Vec<FontFamily>>, pub list: Rc<Vec<FontFamily>>,
@ -250,7 +250,7 @@ impl Default for FamilyState {
} }
/// Defines a line that is positioned over, under or on top of text. /// Defines a line that is positioned over, under or on top of text.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct LineState { pub struct LineState {
/// Stroke color of the line, defaults to the text color if `None`. /// Stroke color of the line, defaults to the text color if `None`.
pub stroke: Option<Paint>, pub stroke: Option<Paint>,

View File

@ -6,7 +6,7 @@ use crate::diag::StrResult;
use crate::util::EcoString; use crate::util::EcoString;
/// A string value with inline storage and clone-on-write semantics. /// A string value with inline storage and clone-on-write semantics.
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Str(EcoString); pub struct Str(EcoString);
impl Str { impl Str {
@ -46,6 +46,20 @@ impl Str {
} }
} }
impl Deref for Str {
type Target = str;
fn deref(&self) -> &str {
self.0.deref()
}
}
impl Debug for Str {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl Display for Str { impl Display for Str {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_char('"')?; f.write_char('"')?;
@ -63,20 +77,6 @@ impl Display for Str {
} }
} }
impl Debug for Str {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl Deref for Str {
type Target = str;
fn deref(&self) -> &str {
self.0.deref()
}
}
impl Add for Str { impl Add for Str {
type Output = Self; type Output = Self;

View File

@ -160,18 +160,18 @@ impl Template {
} }
} }
impl Display for Template {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("<template>")
}
}
impl Debug for Template { impl Debug for Template {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("Template { .. }") f.pad("Template { .. }")
} }
} }
impl Display for Template {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("<template>")
}
}
impl PartialEq for Template { impl PartialEq for Template {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0) Rc::ptr_eq(&self.0, &other.0)
@ -465,11 +465,11 @@ impl ParBuilder {
} }
fn push_inner(&mut self, child: ParChild) { fn push_inner(&mut self, child: ParChild) {
if let ParChild::Text(curr_text, curr_props, curr_align) = &child { if let ParChild::Text(curr_text, curr_align, curr_props) = &child {
if let Some(ParChild::Text(prev_text, prev_props, prev_align)) = if let Some(ParChild::Text(prev_text, prev_align, prev_props)) =
self.children.last_mut() self.children.last_mut()
{ {
if prev_align == curr_align && prev_props == curr_props { if prev_align == curr_align && Rc::ptr_eq(prev_props, curr_props) {
prev_text.push_str(&curr_text); prev_text.push_str(&curr_text);
return; return;
} }

View File

@ -144,6 +144,12 @@ impl From<usize> for Value {
} }
} }
impl From<RgbaColor> for Value {
fn from(v: RgbaColor) -> Self {
Self::Color(Color::Rgba(v))
}
}
impl From<&str> for Value { impl From<&str> for Value {
fn from(v: &str) -> Self { fn from(v: &str) -> Self {
Self::Str(v.into()) Self::Str(v.into())
@ -162,12 +168,6 @@ impl From<EcoString> for Value {
} }
} }
impl From<RgbaColor> for Value {
fn from(v: RgbaColor) -> Self {
Self::Color(Color::Rgba(v))
}
}
impl From<Dynamic> for Value { impl From<Dynamic> for Value {
fn from(v: Dynamic) -> Self { fn from(v: Dynamic) -> Self {
Self::Dyn(v) Self::Dyn(v)
@ -181,7 +181,7 @@ impl Dynamic {
/// Create a new instance from any value that satisifies the required bounds. /// Create a new instance from any value that satisifies the required bounds.
pub fn new<T>(any: T) -> Self pub fn new<T>(any: T) -> Self
where where
T: Type + Debug + Display + Clone + PartialEq + 'static, T: Type + Debug + Display + PartialEq + 'static,
{ {
Self(Rc::new(any)) Self(Rc::new(any))
} }
@ -202,18 +202,18 @@ impl Dynamic {
} }
} }
impl Display for Dynamic {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Debug for Dynamic { impl Debug for Dynamic {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(&self.0, f) Debug::fmt(&self.0, f)
} }
} }
impl Display for Dynamic {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl PartialEq for Dynamic { impl PartialEq for Dynamic {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.0.dyn_eq(other) self.0.dyn_eq(other)
@ -228,7 +228,7 @@ trait Bounds: Debug + Display + 'static {
impl<T> Bounds for T impl<T> Bounds for T
where where
T: Type + Debug + Display + Clone + PartialEq + 'static, T: Type + Debug + Display + PartialEq + 'static,
{ {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
@ -309,12 +309,6 @@ macro_rules! primitive {
const TYPE_NAME: &'static str = $name; const TYPE_NAME: &'static str = $name;
} }
impl From<$type> for Value {
fn from(v: $type) -> Self {
Value::$variant(v)
}
}
impl Cast<Value> for $type { impl Cast<Value> for $type {
fn is(value: &Value) -> bool { fn is(value: &Value) -> bool {
matches!(value, Value::$variant(_) $(| Value::$other(_))*) matches!(value, Value::$variant(_) $(| Value::$other(_))*)
@ -332,6 +326,12 @@ macro_rules! primitive {
} }
} }
} }
impl From<$type> for Value {
fn from(v: $type) -> Self {
Value::$variant(v)
}
}
}; };
} }
@ -342,17 +342,17 @@ macro_rules! dynamic {
const TYPE_NAME: &'static str = $name; const TYPE_NAME: &'static str = $name;
} }
impl From<$type> for $crate::eval::Value {
fn from(v: $type) -> Self {
$crate::eval::Value::Dyn($crate::eval::Dynamic::new(v))
}
}
castable! { castable! {
$type: <Self as $crate::eval::Type>::TYPE_NAME, $type: <Self as $crate::eval::Type>::TYPE_NAME,
$($tts)* $($tts)*
@this: Self => this.clone(), @this: Self => this.clone(),
} }
impl From<$type> for $crate::eval::Value {
fn from(v: $type) -> Self {
$crate::eval::Value::Dyn($crate::eval::Dynamic::new(v))
}
}
}; };
} }

129
src/eval/walk.rs Normal file
View File

@ -0,0 +1,129 @@
use std::fmt::Write;
use std::rc::Rc;
use super::{Eval, EvalContext, Template, Value};
use crate::diag::TypResult;
use crate::geom::Gen;
use crate::layout::{ParChild, ParNode, StackChild, StackNode};
use crate::syntax::*;
use crate::util::EcoString;
/// Walk a syntax node and fill the currently built template.
pub trait Walk {
/// Walk the node.
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()>;
}
impl Walk for SyntaxTree {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
for node in self.iter() {
node.walk(ctx)?;
}
Ok(())
}
}
impl Walk for SyntaxNode {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
match self {
Self::Space => ctx.template.space(),
Self::Linebreak(_) => ctx.template.linebreak(),
Self::Parbreak(_) => ctx.template.parbreak(),
Self::Strong(_) => ctx.template.modify(|s| s.font_mut().strong ^= true),
Self::Emph(_) => ctx.template.modify(|s| s.font_mut().emph ^= true),
Self::Text(text) => ctx.template.text(text),
Self::Raw(raw) => raw.walk(ctx)?,
Self::Heading(heading) => heading.walk(ctx)?,
Self::List(list) => list.walk(ctx)?,
Self::Enum(enum_) => enum_.walk(ctx)?,
Self::Expr(expr) => match expr.eval(ctx)? {
Value::None => {}
Value::Int(v) => ctx.template.text(v.to_string()),
Value::Float(v) => ctx.template.text(v.to_string()),
Value::Str(v) => ctx.template.text(v),
Value::Template(v) => ctx.template += v,
// For values which can't be shown "naturally", we print the
// representation in monospace.
other => ctx.template.monospace(other.to_string()),
},
}
Ok(())
}
}
impl Walk for RawNode {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
if self.block {
ctx.template.parbreak();
}
ctx.template.monospace(&self.text);
if self.block {
ctx.template.parbreak();
}
Ok(())
}
}
impl Walk for HeadingNode {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
let level = self.level;
let body = self.body.eval(ctx)?;
ctx.template.parbreak();
ctx.template.save();
ctx.template.modify(move |state| {
let font = state.font_mut();
let upscale = 1.6 - 0.1 * level as f64;
font.size *= upscale;
font.strong = true;
});
ctx.template += body;
ctx.template.restore();
ctx.template.parbreak();
Ok(())
}
}
impl Walk for ListNode {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
let body = self.body.eval(ctx)?;
walk_item(ctx, '•'.into(), body);
Ok(())
}
}
impl Walk for EnumNode {
fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
let body = self.body.eval(ctx)?;
let mut label = EcoString::new();
write!(&mut label, "{}.", self.number.unwrap_or(1)).unwrap();
walk_item(ctx, label, body);
Ok(())
}
}
fn walk_item(ctx: &mut EvalContext, label: EcoString, body: Template) {
ctx.template += Template::from_block(move |state| {
let label = ParNode {
dir: state.dirs.cross,
line_spacing: state.line_spacing(),
children: vec![ParChild::Text(
label.clone(),
state.aligns.cross,
Rc::clone(&state.font),
)],
};
StackNode {
dirs: Gen::new(state.dirs.main, state.dirs.cross),
children: vec![
StackChild::Any(label.into(), Gen::default()),
StackChild::Spacing((state.font.size / 2.0).into()),
StackChild::Any(body.to_stack(&state).into(), Gen::default()),
],
}
});
}

View File

@ -14,8 +14,7 @@ use crate::geom::Length;
use crate::loading::{FileHash, Loader}; use crate::loading::{FileHash, Loader};
/// A unique identifier for a loaded font face. /// A unique identifier for a loaded font face.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct FaceId(u32); pub struct FaceId(u32);
impl FaceId { impl FaceId {
@ -271,7 +270,7 @@ impl Face {
/// A length in em units. /// A length in em units.
/// ///
/// `1em` is the same as the font size. /// `1em` is the same as the font size.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd)] #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Em(N64); pub struct Em(N64);
impl Em { impl Em {
@ -343,11 +342,15 @@ impl Display for VerticalFontMetric {
} }
/// A generic or named font family. /// A generic or named font family.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum FontFamily { pub enum FontFamily {
/// A family that has "serifs", small strokes attached to letters.
Serif, Serif,
/// A family in which glyphs do not have "serifs", small attached strokes.
SansSerif, SansSerif,
/// A family in which (almost) all glyphs are of equal width.
Monospace, Monospace,
/// A specific family with a name.
Named(String), Named(String),
} }
@ -575,15 +578,6 @@ impl Default for FontWeight {
} }
} }
impl Display for FontWeight {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.to_str() {
Some(name) => f.pad(name),
None => write!(f, "{}", self.0),
}
}
}
impl Debug for FontWeight { impl Debug for FontWeight {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.pad(match *self { f.pad(match *self {
@ -601,6 +595,15 @@ impl Debug for FontWeight {
} }
} }
impl Display for FontWeight {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.to_str() {
Some(name) => f.pad(name),
None => write!(f, "{}", self.0),
}
}
}
/// The width of a font face. /// The width of a font face.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -707,15 +710,6 @@ impl Default for FontStretch {
} }
} }
impl Display for FontStretch {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.to_str() {
Some(name) => f.pad(name),
None => write!(f, "{}", self.to_ratio()),
}
}
}
impl Debug for FontStretch { impl Debug for FontStretch {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match *self { f.pad(match *self {
@ -733,6 +727,15 @@ impl Debug for FontStretch {
} }
} }
impl Display for FontStretch {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.to_str() {
Some(name) => f.pad(name),
None => write!(f, "{}", self.to_ratio()),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,8 +1,11 @@
use super::*;
use decorum::N64; use decorum::N64;
use serde::{Deserialize, Serialize};
use super::*;
/// An angle. /// An angle.
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)]
pub struct Angle(N64); pub struct Angle(N64);
impl Angle { impl Angle {
@ -52,6 +55,13 @@ impl Angle {
} }
} }
impl Debug for Angle {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let unit = AngularUnit::Deg;
write!(f, "{}{}", self.to_unit(unit), unit)
}
}
impl Display for Angle { impl Display for Angle {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
// Format with the unit that yields the shortest output, preferring // Format with the unit that yields the shortest output, preferring
@ -66,13 +76,6 @@ impl Display for Angle {
} }
} }
impl Debug for Angle {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let unit = AngularUnit::Deg;
write!(f, "{}{}", self.to_unit(unit), unit)
}
}
impl Neg for Angle { impl Neg for Angle {
type Output = Self; type Output = Self;
@ -134,7 +137,7 @@ impl Sum for Angle {
} }
} }
/// Different units of angular measurement. /// Different units of angular measurement.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum AngularUnit { pub enum AngularUnit {
/// Radians. /// Radians.
Rad, Rad,

View File

@ -33,18 +33,18 @@ impl Fractional {
} }
} }
impl Display for Fractional {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}fr", self.get())
}
}
impl Debug for Fractional { impl Debug for Fractional {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f) Display::fmt(self, f)
} }
} }
impl Display for Fractional {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}fr", self.get())
}
}
impl Neg for Fractional { impl Neg for Fractional {
type Output = Self; type Output = Self;

View File

@ -126,6 +126,13 @@ impl Length {
} }
} }
impl Debug for Length {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let unit = LengthUnit::Pt;
write!(f, "{}{}", self.to_unit(unit), unit)
}
}
impl Display for Length { impl Display for Length {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use LengthUnit::*; use LengthUnit::*;
@ -142,13 +149,6 @@ impl Display for Length {
} }
} }
impl Debug for Length {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let unit = LengthUnit::Pt;
write!(f, "{}{}", self.to_unit(unit), unit)
}
}
impl Neg for Length { impl Neg for Length {
type Output = Self; type Output = Self;
@ -217,7 +217,7 @@ impl<'a> Sum<&'a Length> for Length {
} }
/// Different units of length measurement. /// Different units of length measurement.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum LengthUnit { pub enum LengthUnit {
/// Points. /// Points.
Pt, Pt,

View File

@ -47,18 +47,18 @@ impl Linear {
} }
} }
impl Display for Linear {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{} + {}", self.rel, self.abs)
}
}
impl Debug for Linear { impl Debug for Linear {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:?} + {:?}", self.rel, self.abs) write!(f, "{:?} + {:?}", self.rel, self.abs)
} }
} }
impl Display for Linear {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{} + {}", self.rel, self.abs)
}
}
impl From<Length> for Linear { impl From<Length> for Linear {
fn from(abs: Length) -> Self { fn from(abs: Length) -> Self {
Self { rel: Relative::zero(), abs } Self { rel: Relative::zero(), abs }

View File

@ -46,18 +46,18 @@ impl Relative {
} }
} }
impl Display for Relative {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}%", 100.0 * self.get())
}
}
impl Debug for Relative { impl Debug for Relative {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f) Display::fmt(self, f)
} }
} }
impl Display for Relative {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}%", 100.0 * self.get())
}
}
impl Neg for Relative { impl Neg for Relative {
type Output = Self; type Output = Self;

View File

@ -33,6 +33,14 @@ impl<T> Sides<T> {
} }
} }
impl Sides<Length> {
/// A size with `left` and `right` summed into `width`, and `top` and
/// `bottom` summed into `height`.
pub fn size(self) -> Size {
Size::new(self.left + self.right, self.top + self.bottom)
}
}
impl Sides<Linear> { impl Sides<Linear> {
/// Resolve the linear sides relative to the given `size`. /// Resolve the linear sides relative to the given `size`.
pub fn resolve(self, size: Size) -> Sides<Length> { pub fn resolve(self, size: Size) -> Sides<Length> {
@ -45,14 +53,6 @@ impl Sides<Linear> {
} }
} }
impl Sides<Length> {
/// A size with `left` and `right` summed into `width`, and `top` and
/// `bottom` summed into `height`.
pub fn size(self) -> Size {
Size::new(self.left + self.right, self.top + self.bottom)
}
}
impl<T> Get<Side> for Sides<T> { impl<T> Get<Side> for Sides<T> {
type Component = T; type Component = T;

View File

@ -13,8 +13,7 @@ use serde::{Deserialize, Serialize};
use crate::loading::{FileHash, Loader}; use crate::loading::{FileHash, Loader};
/// A unique identifier for a loaded image. /// A unique identifier for a loaded image.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct ImageId(u32); pub struct ImageId(u32);
impl ImageId { impl ImageId {

View File

@ -1,7 +1,6 @@
use super::*; use super::*;
/// A node that places a rectangular filled background behind its child. /// A node that places a rectangular filled background behind its child.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct BackgroundNode { pub struct BackgroundNode {
/// The kind of shape to use as a background. /// The kind of shape to use as a background.

View File

@ -1,5 +1,3 @@
use std::ops::Deref;
use super::*; use super::*;
/// Carries an item that is only valid in certain regions and the constraints /// Carries an item that is only valid in certain regions and the constraints
@ -12,14 +10,6 @@ pub struct Constrained<T> {
pub constraints: Constraints, pub constraints: Constraints,
} }
impl<T> Deref for Constrained<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.item
}
}
/// Describe regions that match them. /// Describe regions that match them.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Constraints { pub struct Constraints {

View File

@ -3,7 +3,6 @@ use decorum::N64;
use super::*; use super::*;
/// A node that can fix its child's width and height. /// A node that can fix its child's width and height.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct FixedNode { pub struct FixedNode {
/// The fixed width, if any. /// The fixed width, if any.
@ -78,7 +77,7 @@ impl Layout for FixedNode {
// relayout with expansion. // relayout with expansion.
if let Some(aspect) = aspect { if let Some(aspect) = aspect {
if width.is_none() && height.is_none() { if width.is_none() && height.is_none() {
let needed = frames[0].size.cap(size); let needed = frames[0].item.size.cap(size);
let width = needed.width.max(aspect * needed.height); let width = needed.width.max(aspect * needed.height);
regions.current = Size::new(width, width / aspect); regions.current = Size::new(width, width / aspect);
regions.expand = Spec::splat(true); regions.expand = Spec::splat(true);

View File

@ -1,7 +1,6 @@
use super::*; use super::*;
/// A node that arranges its children in a grid. /// A node that arranges its children in a grid.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct GridNode { pub struct GridNode {
/// The `main` and `cross` directions of this grid. /// The `main` and `cross` directions of this grid.
@ -256,7 +255,7 @@ impl<'a> GridLayouter<'a> {
for node in (0 .. self.rows.len()).filter_map(|y| self.cell(x, y)) { for node in (0 .. self.rows.len()).filter_map(|y| self.cell(x, y)) {
let size = Gen::new(available, Length::inf()).to_size(self.main); let size = Gen::new(available, Length::inf()).to_size(self.main);
let regions = Regions::one(size, size, Spec::splat(false)); let regions = Regions::one(size, size, Spec::splat(false));
let frame = node.layout(ctx, &regions).remove(0); let frame = node.layout(ctx, &regions).remove(0).item;
resolved.set_max(frame.size.get(self.cross)); resolved.set_max(frame.size.get(self.cross));
} }
@ -353,7 +352,7 @@ impl<'a> GridLayouter<'a> {
let mut sizes = node let mut sizes = node
.layout(ctx, &self.regions) .layout(ctx, &self.regions)
.into_iter() .into_iter()
.map(|frame| frame.size.get(self.main)); .map(|frame| frame.item.size.get(self.main));
if let Some(size) = sizes.next() { if let Some(size) = sizes.next() {
first.set_max(size); first.set_max(size);

View File

@ -4,7 +4,6 @@ use crate::image::ImageId;
use ::image::GenericImageView; use ::image::GenericImageView;
/// An image node. /// An image node.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct ImageNode { pub struct ImageNode {
/// The id of the image file. /// The id of the image file.

View File

@ -1,7 +1,6 @@
use super::*; use super::*;
/// A node that adds padding to its child. /// A node that adds padding to its child.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct PadNode { pub struct PadNode {
/// The amount of padding. /// The amount of padding.

View File

@ -1,4 +1,3 @@
use std::fmt::{self, Debug, Formatter};
use std::rc::Rc; use std::rc::Rc;
use unicode_bidi::{BidiInfo, Level}; use unicode_bidi::{BidiInfo, Level};
@ -11,7 +10,6 @@ use crate::util::{EcoString, RangeExt, SliceExt};
type Range = std::ops::Range<usize>; type Range = std::ops::Range<usize>;
/// A node that arranges its children into a paragraph. /// A node that arranges its children into a paragraph.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct ParNode { pub struct ParNode {
/// The inline direction of this paragraph. /// The inline direction of this paragraph.
@ -23,7 +21,6 @@ pub struct ParNode {
} }
/// A child of a paragraph node. /// A child of a paragraph node.
#[derive(Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub enum ParChild { pub enum ParChild {
/// Spacing between other nodes. /// Spacing between other nodes.
@ -94,18 +91,6 @@ impl From<ParNode> for LayoutNode {
} }
} }
impl Debug for ParChild {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Spacing(amount) => write!(f, "Spacing({:?})", amount),
Self::Text(text, align, _) => write!(f, "Text({:?}, {:?})", text, align),
Self::Any(any, align) => {
f.debug_tuple("Any").field(any).field(align).finish()
}
}
}
}
/// A paragraph representation in which children are already layouted and text /// A paragraph representation in which children are already layouted and text
/// is separated into shapable runs. /// is separated into shapable runs.
struct ParLayouter<'a> { struct ParLayouter<'a> {

View File

@ -1,5 +1,4 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter};
use std::ops::Range; use std::ops::Range;
use rustybuzz::UnicodeBuffer; use rustybuzz::UnicodeBuffer;
@ -11,12 +10,45 @@ use crate::geom::{Dir, Length, Point, Size};
use crate::layout::Geometry; use crate::layout::Geometry;
use crate::util::SliceExt; use crate::util::SliceExt;
/// Shape text into [`ShapedText`].
pub fn shape<'a>(
ctx: &mut LayoutContext,
text: &'a str,
dir: Dir,
state: &'a FontState,
) -> ShapedText<'a> {
let mut glyphs = vec![];
if !text.is_empty() {
shape_segment(
ctx,
&mut glyphs,
0,
text,
dir,
state.size,
state.variant(),
state.families(),
None,
);
}
let (size, baseline) = measure(ctx, &glyphs, state);
ShapedText {
text,
dir,
state,
size,
baseline,
glyphs: Cow::Owned(glyphs),
}
}
/// The result of shaping text. /// The result of shaping text.
/// ///
/// This type contains owned or borrowed shaped text runs, which can be /// This type contains owned or borrowed shaped text runs, which can be
/// measured, used to reshape substrings more quickly and converted into a /// measured, used to reshape substrings more quickly and converted into a
/// frame. /// frame.
#[derive(Clone)]
pub struct ShapedText<'a> { pub struct ShapedText<'a> {
/// The text that was shaped. /// The text that was shaped.
pub text: &'a str, pub text: &'a str,
@ -33,7 +65,7 @@ pub struct ShapedText<'a> {
} }
/// A single glyph resulting from shaping. /// A single glyph resulting from shaping.
#[derive(Debug, Copy, Clone)] #[derive(Copy, Clone)]
pub struct ShapedGlyph { pub struct ShapedGlyph {
/// The font face the glyph is contained in. /// The font face the glyph is contained in.
pub face_id: FaceId, pub face_id: FaceId,
@ -51,13 +83,6 @@ pub struct ShapedGlyph {
pub safe_to_break: bool, pub safe_to_break: bool,
} }
/// A visual side.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum Side {
Left,
Right,
}
impl<'a> ShapedText<'a> { impl<'a> ShapedText<'a> {
/// Build the shaped text's frame. /// Build the shaped text's frame.
pub fn build(&self, ctx: &LayoutContext) -> Frame { pub fn build(&self, ctx: &LayoutContext) -> Frame {
@ -174,44 +199,10 @@ impl<'a> ShapedText<'a> {
} }
} }
impl Debug for ShapedText<'_> { /// A visual side.
fn fmt(&self, f: &mut Formatter) -> fmt::Result { enum Side {
write!(f, "Shaped({:?})", self.text) Left,
} Right,
}
/// Shape text into [`ShapedText`].
pub fn shape<'a>(
ctx: &mut LayoutContext,
text: &'a str,
dir: Dir,
state: &'a FontState,
) -> ShapedText<'a> {
let mut glyphs = vec![];
if !text.is_empty() {
shape_segment(
ctx,
&mut glyphs,
0,
text,
dir,
state.size,
state.variant(),
state.families(),
None,
);
}
let (size, baseline) = measure(ctx, &glyphs, state);
ShapedText {
text,
dir,
state,
size,
baseline,
glyphs: Cow::Owned(glyphs),
}
} }
/// Shape text with font fallback using the `families` iterator. /// Shape text with font fallback using the `families` iterator.

View File

@ -1,7 +1,6 @@
use super::*; use super::*;
/// A node that stacks its children. /// A node that stacks its children.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct StackNode { pub struct StackNode {
/// The `main` and `cross` directions of this stack. /// The `main` and `cross` directions of this stack.
@ -14,7 +13,6 @@ pub struct StackNode {
} }
/// A child of a stack node. /// A child of a stack node.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "layout-cache", derive(Hash))] #[cfg_attr(feature = "layout-cache", derive(Hash))]
pub enum StackChild { pub enum StackChild {
/// Spacing between other nodes. /// Spacing between other nodes.

View File

@ -1,7 +1,6 @@
use super::*; use super::*;
use std::any::Any; use std::any::Any;
use std::fmt::{self, Debug, Formatter};
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -10,7 +9,6 @@ use std::hash::{Hash, Hasher};
use fxhash::FxHasher64; use fxhash::FxHasher64;
/// A tree of layout nodes. /// A tree of layout nodes.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct LayoutTree { pub struct LayoutTree {
/// Runs of pages with the same properties. /// Runs of pages with the same properties.
pub runs: Vec<PageRun>, pub runs: Vec<PageRun>,
@ -24,7 +22,6 @@ impl LayoutTree {
} }
/// A run of pages that all have the same properties. /// A run of pages that all have the same properties.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PageRun { pub struct PageRun {
/// The size of each page. /// The size of each page.
pub size: Size, pub size: Size,
@ -47,7 +44,7 @@ impl PageRun {
/// A dynamic layouting node. /// A dynamic layouting node.
pub struct LayoutNode { pub struct LayoutNode {
node: Box<dyn Bounds>, node: Box<dyn Layout>,
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
hash: u64, hash: u64,
} }
@ -57,7 +54,7 @@ impl LayoutNode {
#[cfg(not(feature = "layout-cache"))] #[cfg(not(feature = "layout-cache"))]
pub fn new<T>(node: T) -> Self pub fn new<T>(node: T) -> Self
where where
T: Layout + Debug + Clone + Eq + PartialEq + 'static, T: Layout + 'static,
{ {
Self { node: Box::new(node) } Self { node: Box::new(node) }
} }
@ -66,7 +63,7 @@ impl LayoutNode {
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
pub fn new<T>(node: T) -> Self pub fn new<T>(node: T) -> Self
where where
T: Layout + Debug + Clone + Eq + PartialEq + Hash + 'static, T: Layout + Hash + 'static,
{ {
let hash = { let hash = {
let mut state = FxHasher64::default(); let mut state = FxHasher64::default();
@ -99,60 +96,9 @@ impl Layout for LayoutNode {
} }
} }
impl Debug for LayoutNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.node.fmt(f)
}
}
impl Clone for LayoutNode {
fn clone(&self) -> Self {
Self {
node: self.node.dyn_clone(),
#[cfg(feature = "layout-cache")]
hash: self.hash,
}
}
}
impl Eq for LayoutNode {}
impl PartialEq for LayoutNode {
fn eq(&self, other: &Self) -> bool {
self.node.dyn_eq(other.node.as_ref())
}
}
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
impl Hash for LayoutNode { impl Hash for LayoutNode {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.hash); state.write_u64(self.hash);
} }
} }
trait Bounds: Layout + Debug + 'static {
fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &dyn Bounds) -> bool;
fn dyn_clone(&self) -> Box<dyn Bounds>;
}
impl<T> Bounds for T
where
T: Layout + Debug + Eq + PartialEq + Clone + 'static,
{
fn as_any(&self) -> &dyn Any {
self
}
fn dyn_eq(&self, other: &dyn Bounds) -> bool {
if let Some(other) = other.as_any().downcast_ref::<Self>() {
self == other
} else {
false
}
}
fn dyn_clone(&self) -> Box<dyn Bounds> {
Box::new(self.clone())
}
}

View File

@ -13,7 +13,7 @@ use crate::font::FaceInfo;
/// Loads fonts and files from the local file system. /// Loads fonts and files from the local file system.
/// ///
/// _This is only available when the `fs` feature is enabled._ /// _This is only available when the `fs` feature is enabled._
#[derive(Debug, Default, Clone)] #[derive(Default)]
pub struct FsLoader { pub struct FsLoader {
faces: Vec<FaceInfo>, faces: Vec<FaceInfo>,
} }

View File

@ -9,7 +9,7 @@ use crate::font::FaceInfo;
use crate::util::PathExt; use crate::util::PathExt;
/// Loads fonts and files from an in-memory storage. /// Loads fonts and files from an in-memory storage.
#[derive(Debug, Default, Clone)] #[derive(Default)]
pub struct MemLoader { pub struct MemLoader {
faces: Vec<FaceInfo>, faces: Vec<FaceInfo>,
files: HashMap<PathBuf, Cow<'static, [u8]>>, files: HashMap<PathBuf, Cow<'static, [u8]>>,

View File

@ -11,15 +11,12 @@ pub use mem::*;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
use serde::{Deserialize, Serialize};
use crate::font::FaceInfo; use crate::font::FaceInfo;
/// A hash that identifies a file. /// A hash that identifies a file.
/// ///
/// Such a hash can be [resolved](Loader::resolve) from a path. /// Such a hash can be [resolved](Loader::resolve) from a path.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Serialize, Deserialize)]
pub struct FileHash(pub u64); pub struct FileHash(pub u64);
/// Loads resources from a local or remote source. /// Loads resources from a local or remote source.

View File

@ -3,7 +3,7 @@
use crate::geom::{Length, Linear, Relative, Sides, Size}; use crate::geom::{Length, Linear, Relative, Sides, Size};
/// Specification of a paper. /// Specification of a paper.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone)]
pub struct Paper { pub struct Paper {
/// The broad class this paper belongs to. /// The broad class this paper belongs to.
class: PaperClass, class: PaperClass,

View File

@ -201,7 +201,7 @@ fn list_item(p: &mut Parser) -> SyntaxNode {
let column = p.column(start); let column = p.column(start);
p.eat_assert(Token::Hyph); p.eat_assert(Token::Hyph);
let body = tree_indented(p, column); let body = tree_indented(p, column);
SyntaxNode::List(Box::new(ListItem { span: p.span_from(start), body })) SyntaxNode::List(Box::new(ListNode { span: p.span_from(start), body }))
} }
/// Parse a single enum item. /// Parse a single enum item.
@ -210,7 +210,7 @@ fn enum_item(p: &mut Parser, number: Option<usize>) -> SyntaxNode {
let column = p.column(start); let column = p.column(start);
p.eat_assert(Token::Numbering(number)); p.eat_assert(Token::Numbering(number));
let body = tree_indented(p, column); let body = tree_indented(p, column);
SyntaxNode::Enum(Box::new(EnumItem { SyntaxNode::Enum(Box::new(EnumNode {
span: p.span_from(start), span: p.span_from(start),
number, number,
body, body,

View File

@ -1,4 +1,3 @@
use std::fmt::{self, Debug, Formatter};
use std::ops::Range; use std::ops::Range;
use super::{TokenMode, Tokens}; use super::{TokenMode, Tokens};
@ -28,7 +27,6 @@ pub struct Parser<'s> {
} }
/// A logical group of tokens, e.g. `[...]`. /// A logical group of tokens, e.g. `[...]`.
#[derive(Debug, Copy, Clone)]
struct GroupEntry { struct GroupEntry {
/// The start index of the group. Used by `Parser::end_group` to return the /// The start index of the group. Used by `Parser::end_group` to return the
/// group's full span. /// group's full span.
@ -391,11 +389,3 @@ impl<'s> Parser<'s> {
self.groups.iter().any(|g| g.kind == kind) self.groups.iter().any(|g| g.kind == kind)
} }
} }
impl Debug for Parser<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut s = self.tokens.scanner();
s.jump(self.next_start());
write!(f, "Parser({}|{})", s.eaten(), s.rest())
}
}

View File

@ -1,4 +1,3 @@
use std::fmt::{self, Debug, Formatter};
use std::slice::SliceIndex; use std::slice::SliceIndex;
/// A featureful char-based scanner. /// A featureful char-based scanner.
@ -153,12 +152,6 @@ impl<'s> Scanner<'s> {
} }
} }
impl Debug for Scanner<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Scanner({}|{})", self.eaten(), self.rest())
}
}
/// Whether this character denotes a newline. /// Whether this character denotes a newline.
#[inline] #[inline]
pub fn is_newline(character: char) -> bool { pub fn is_newline(character: char) -> bool {

View File

@ -1,11 +1,8 @@
use std::fmt::{self, Debug, Formatter};
use super::{is_newline, Scanner}; use super::{is_newline, Scanner};
use crate::geom::{AngularUnit, LengthUnit}; use crate::geom::{AngularUnit, LengthUnit};
use crate::syntax::*; use crate::syntax::*;
/// An iterator over the tokens of a string of source code. /// An iterator over the tokens of a string of source code.
#[derive(Clone)]
pub struct Tokens<'s> { pub struct Tokens<'s> {
s: Scanner<'s>, s: Scanner<'s>,
mode: TokenMode, mode: TokenMode,
@ -481,12 +478,6 @@ impl<'s> Tokens<'s> {
} }
} }
impl Debug for Tokens<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Tokens({}|{})", self.s.eaten(), self.s.rest())
}
}
fn keyword(ident: &str) -> Option<Token<'static>> { fn keyword(ident: &str) -> Option<Token<'static>> {
Some(match ident { Some(match ident {
"not" => Token::Not, "not" => Token::Not,
@ -512,6 +503,8 @@ fn keyword(ident: &str) -> Option<Token<'static>> {
#[cfg(test)] #[cfg(test)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
mod tests { mod tests {
use std::fmt::Debug;
use super::*; use super::*;
use Option::None; use Option::None;

View File

@ -15,8 +15,7 @@ use crate::parse::{is_newline, Scanner};
use crate::util::PathExt; use crate::util::PathExt;
/// A unique identifier for a loaded source file. /// A unique identifier for a loaded source file.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct SourceId(u32); pub struct SourceId(u32);
impl SourceId { impl SourceId {
@ -252,12 +251,6 @@ impl SourceFile {
} }
} }
impl AsRef<str> for SourceFile {
fn as_ref(&self) -> &str {
&self.src
}
}
/// The indices at which lines start (right behind newlines). /// The indices at which lines start (right behind newlines).
/// ///
/// The beginning of the string (index 0) is not returned. /// The beginning of the string (index 0) is not returned.
@ -276,6 +269,12 @@ fn newlines(string: &str) -> impl Iterator<Item = usize> + '_ {
}) })
} }
impl AsRef<str> for SourceFile {
fn as_ref(&self) -> &str {
&self.src
}
}
#[cfg(feature = "codespan-reporting")] #[cfg(feature = "codespan-reporting")]
impl<'a> Files<'a> for SourceStore { impl<'a> Files<'a> for SourceStore {
type FileId = SourceId; type FileId = SourceId;

View File

@ -12,7 +12,7 @@ use crate::util::EcoString;
/// - `_` and `-` as continuing characters. /// - `_` and `-` as continuing characters.
/// ///
/// [uax31]: http://www.unicode.org/reports/tr31/ /// [uax31]: http://www.unicode.org/reports/tr31/
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Clone, PartialEq)]
pub struct Ident { pub struct Ident {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,

View File

@ -20,9 +20,9 @@ pub enum SyntaxNode {
/// A section heading: `= Introduction`. /// A section heading: `= Introduction`.
Heading(Box<HeadingNode>), Heading(Box<HeadingNode>),
/// An item in an unordered list: `- ...`. /// An item in an unordered list: `- ...`.
List(Box<ListItem>), List(Box<ListNode>),
/// An item in an enumeration (ordered list): `1. ...`. /// An item in an enumeration (ordered list): `1. ...`.
Enum(Box<EnumItem>), Enum(Box<EnumNode>),
/// An expression. /// An expression.
Expr(Expr), Expr(Expr),
} }
@ -55,7 +55,7 @@ pub struct HeadingNode {
/// An item in an unordered list: `- ...`. /// An item in an unordered list: `- ...`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ListItem { pub struct ListNode {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The contents of the list item. /// The contents of the list item.
@ -64,7 +64,7 @@ pub struct ListItem {
/// An item in an enumeration (ordered list): `1. ...`. /// An item in an enumeration (ordered list): `1. ...`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct EnumItem { pub struct EnumNode {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The number, if any. /// The number, if any.

View File

@ -94,14 +94,14 @@ impl Pretty for SyntaxNode {
Self::Emph(_) => p.push('_'), Self::Emph(_) => p.push('_'),
Self::Text(text) => p.push_str(text), Self::Text(text) => p.push_str(text),
Self::Raw(raw) => raw.pretty(p), Self::Raw(raw) => raw.pretty(p),
Self::Heading(n) => n.pretty(p), Self::Heading(heading) => heading.pretty(p),
Self::List(n) => n.pretty(p), Self::List(list) => list.pretty(p),
Self::Enum(n) => n.pretty(p), Self::Enum(enum_) => enum_.pretty(p),
Self::Expr(n) => { Self::Expr(expr) => {
if n.has_short_form() { if expr.has_short_form() {
p.push('#'); p.push('#');
} }
n.pretty(p); expr.pretty(p);
} }
} }
} }
@ -173,14 +173,14 @@ impl Pretty for HeadingNode {
} }
} }
impl Pretty for ListItem { impl Pretty for ListNode {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
p.push_str("- "); p.push_str("- ");
self.body.pretty(p); self.body.pretty(p);
} }
} }
impl Pretty for EnumItem { impl Pretty for EnumNode {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
if let Some(number) = self.number { if let Some(number) = self.number {
write!(p, "{}", number).unwrap(); write!(p, "{}", number).unwrap();

View File

@ -1,3 +1,4 @@
use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, Range}; use std::ops::{Add, Range};
@ -6,8 +7,7 @@ use serde::{Deserialize, Serialize};
use crate::source::SourceId; use crate::source::SourceId;
/// A value with the span it corresponds to in the source code. /// A value with the span it corresponds to in the source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct Spanned<T> { pub struct Spanned<T> {
/// The spanned value. /// The spanned value.
pub v: T, pub v: T,
@ -48,8 +48,7 @@ impl<T: Debug> Debug for Spanned<T> {
} }
/// Bounds of a slice of source code. /// Bounds of a slice of source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct Span { pub struct Span {
/// The id of the source file. /// The id of the source file.
pub source: SourceId, pub source: SourceId,
@ -127,9 +126,18 @@ impl Debug for Span {
} }
} }
impl PartialOrd for Span {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.source == other.source {
Some(self.start.cmp(&other.start).then(self.end.cmp(&other.end)))
} else {
None
}
}
}
/// A byte position in source code. /// A byte position in source code.
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct Pos(pub u32); pub struct Pos(pub u32);
impl Pos { impl Pos {
@ -148,17 +156,6 @@ impl Debug for Pos {
} }
} }
impl<T> Add<T> for Pos
where
T: Into<Pos>,
{
type Output = Self;
fn add(self, rhs: T) -> Self {
Pos(self.0 + rhs.into().0)
}
}
impl From<u32> for Pos { impl From<u32> for Pos {
fn from(index: u32) -> Self { fn from(index: u32) -> Self {
Self(index) Self(index)
@ -171,6 +168,17 @@ impl From<usize> for Pos {
} }
} }
impl<T> Add<T> for Pos
where
T: Into<Pos>,
{
type Output = Self;
fn add(self, rhs: T) -> Self {
Pos(self.0 + rhs.into().0)
}
}
/// Convert a position or range into a span. /// Convert a position or range into a span.
pub trait IntoSpan { pub trait IntoSpan {
/// Convert into a span by providing the source id. /// Convert into a span by providing the source id.

View File

@ -157,7 +157,7 @@ pub enum Token<'s> {
} }
/// A quoted string token: `"..."`. /// A quoted string token: `"..."`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct StrToken<'s> { pub struct StrToken<'s> {
/// The string inside the quotes. /// The string inside the quotes.
/// ///
@ -170,7 +170,7 @@ pub struct StrToken<'s> {
} }
/// A raw block token: `` `...` ``. /// A raw block token: `` `...` ``.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct RawToken<'s> { pub struct RawToken<'s> {
/// The raw text between the backticks. /// The raw text between the backticks.
pub text: &'s str, pub text: &'s str,
@ -181,7 +181,7 @@ pub struct RawToken<'s> {
} }
/// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`. /// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct MathToken<'s> { pub struct MathToken<'s> {
/// The formula between the dollars. /// The formula between the dollars.
pub formula: &'s str, pub formula: &'s str,
@ -193,7 +193,7 @@ pub struct MathToken<'s> {
} }
/// A unicode escape sequence token: `\u{1F5FA}`. /// A unicode escape sequence token: `\u{1F5FA}`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct UnicodeEscapeToken<'s> { pub struct UnicodeEscapeToken<'s> {
/// The escape sequence between the braces. /// The escape sequence between the braces.
pub sequence: &'s str, pub sequence: &'s str,

View File

@ -104,12 +104,12 @@ impl_visitors! {
v.visit_tree(r!(heading.body)); v.visit_tree(r!(heading.body));
} }
visit_list(v, item: ListItem) { visit_list(v, list: ListNode) {
v.visit_tree(r!(item.body)); v.visit_tree(r!(list.body));
} }
visit_enum(v, item: EnumItem) { visit_enum(v, enum_: EnumNode) {
v.visit_tree(r!(item.body)); v.visit_tree(r!(enum_.body));
} }
visit_expr(v, expr: Expr) { visit_expr(v, expr: Expr) {

View File

@ -163,24 +163,6 @@ impl EcoString {
} }
} }
impl Default for EcoString {
fn default() -> Self {
Self::new()
}
}
impl Display for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self.as_str(), f)
}
}
impl Debug for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl Deref for EcoString { impl Deref for EcoString {
type Target = str; type Target = str;
@ -201,6 +183,24 @@ impl Deref for EcoString {
} }
} }
impl Default for EcoString {
fn default() -> Self {
Self::new()
}
}
impl Debug for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl Display for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self.as_str(), f)
}
}
impl Eq for EcoString {} impl Eq for EcoString {}
impl PartialEq for EcoString { impl PartialEq for EcoString {

View File

@ -248,8 +248,9 @@ fn test_part(
error.trace.clear(); error.trace.clear();
} }
ref_errors.sort(); // The comparison never fails since all spans are from the same source file.
errors.sort(); ref_errors.sort_by(|a, b| a.span.partial_cmp(&b.span).unwrap());
errors.sort_by(|a, b| a.span.partial_cmp(&b.span).unwrap());
if errors != ref_errors { if errors != ref_errors {
println!(" Subtest {} does not match expected errors. ❌", i); println!(" Subtest {} does not match expected errors. ❌", i);