Add Repr trait (#2269)

This commit is contained in:
MALO 2023-10-04 12:08:56 +02:00 committed by GitHub
parent 57bc614cf4
commit 333e4037fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 708 additions and 634 deletions

View File

@ -21,7 +21,7 @@ use serde::Deserialize;
use serde_yaml as yaml;
use typst::diag::{bail, StrResult};
use typst::doc::Frame;
use typst::eval::{CastInfo, Func, Library, Module, ParamInfo, Scope, Type, Value};
use typst::eval::{CastInfo, Func, Library, Module, ParamInfo, Repr, Scope, Type, Value};
use typst::font::{Font, FontBook};
use typst::geom::{Abs, Smart};
use typst_library::layout::{Margin, PageElem};

View File

@ -6,7 +6,7 @@ use if_chain::if_chain;
use serde::{Deserialize, Serialize};
use typst::doc::Frame;
use typst::eval::{
format_str, AutoValue, CastInfo, Func, Library, NoneValue, Scope, Type, Value,
format_str, AutoValue, CastInfo, Func, Library, NoneValue, Repr, Scope, Type, Value,
};
use typst::geom::Color;
use typst::syntax::{
@ -1089,14 +1089,14 @@ impl<'a> CompletionContext<'a> {
docs: Option<&str>,
) {
let at = label.as_deref().map_or(false, |field| !is_ident(field));
let label = label.unwrap_or_else(|| value.repr().into());
let label = label.unwrap_or_else(|| value.repr());
let detail = docs.map(Into::into).or_else(|| match value {
Value::Symbol(_) => None,
Value::Func(func) => func.docs().map(plain_docs_sentence),
v => {
let repr = v.repr();
(repr.as_str() != label).then(|| repr.into())
(repr.as_str() != label).then_some(repr)
}
});

View File

@ -3,7 +3,7 @@ use std::fmt::Write;
use ecow::{eco_format, EcoString};
use if_chain::if_chain;
use typst::doc::Frame;
use typst::eval::{CapturesVisitor, CastInfo, Tracer, Value};
use typst::eval::{CapturesVisitor, CastInfo, Repr, Tracer, Value};
use typst::geom::{round_2, Length, Numeric};
use typst::syntax::ast::{self, AstNode};
use typst::syntax::{LinkedNode, Source, SyntaxKind};
@ -83,7 +83,7 @@ fn expr_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
write!(pieces.last_mut().unwrap(), " (x{count})").unwrap();
}
}
pieces.push(value.repr().into());
pieces.push(value.repr());
last = Some((value, 1));
}

View File

@ -1,5 +1,5 @@
use typst::eval::{
Datetime, Duration, EvalMode, Module, Never, NoneValue, Plugin, Regex, Version,
Datetime, Duration, EvalMode, Module, Never, NoneValue, Plugin, Regex, Repr, Version,
};
use crate::prelude::*;
@ -51,7 +51,7 @@ pub fn repr(
/// The value whose string representation to produce.
value: Value,
) -> Str {
value.repr()
value.repr().into()
}
/// Fails with an error.
@ -135,7 +135,11 @@ impl assert {
if let Some(message) = message {
bail!("equality assertion failed: {message}");
} else {
bail!("equality assertion failed: value {left:?} was not equal to {right:?}");
bail!(
"equality assertion failed: value {} was not equal to {}",
left.repr(),
right.repr()
);
}
}
Ok(NoneValue)
@ -165,7 +169,9 @@ impl assert {
bail!("inequality assertion failed: {message}");
} else {
bail!(
"inequality assertion failed: value {left:?} was equal to {right:?}"
"inequality assertion failed: value {} was equal to {}",
left.repr(),
right.repr()
);
}
}

View File

@ -1,9 +1,8 @@
use std::fmt::{self, Debug, Formatter, Write};
use std::str::FromStr;
use ecow::{eco_vec, EcoVec};
use smallvec::{smallvec, SmallVec};
use typst::eval::Tracer;
use typst::eval::{Repr, Tracer};
use typst::model::DelayedErrors;
use super::{FigureElem, HeadingElem, Numbering, NumberingPattern};
@ -199,7 +198,7 @@ use crate::prelude::*;
/// documentation for more details on state management in Typst and why it
/// doesn't just use normal variables for counters.
#[ty(scope)]
#[derive(Clone, PartialEq, Hash)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Counter(CounterKey);
impl Counter {
@ -452,11 +451,9 @@ impl Counter {
}
}
impl Debug for Counter {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("counter(")?;
self.0.fmt(f)?;
f.write_char(')')
impl Repr for Counter {
fn repr(&self) -> EcoString {
eco_format!("counter({})", self.0.repr())
}
}
@ -465,7 +462,7 @@ cast! {
}
/// Identifies a counter.
#[derive(Clone, PartialEq, Hash)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum CounterKey {
/// The page counter.
Page,
@ -495,19 +492,19 @@ cast! {
v: LocatableSelector => Self::Selector(v.0),
}
impl Debug for CounterKey {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for CounterKey {
fn repr(&self) -> EcoString {
match self {
Self::Page => f.pad("page"),
Self::Selector(selector) => selector.fmt(f),
Self::Str(str) => str.fmt(f),
Self::Page => "page".into(),
Self::Selector(selector) => selector.repr(),
Self::Str(str) => str.repr(),
}
}
}
/// An update to perform on a counter.
#[ty]
#[derive(Clone, PartialEq, Hash)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum CounterUpdate {
/// Set the counter to the specified state.
Set(CounterState),
@ -517,9 +514,9 @@ pub enum CounterUpdate {
Func(Func),
}
impl Debug for CounterUpdate {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("..")
impl Repr for CounterUpdate {
fn repr(&self) -> EcoString {
"..".into()
}
}

View File

@ -1,7 +1,7 @@
use std::fmt::{self, Debug, Formatter, Write};
use std::fmt::Debug;
use ecow::{eco_vec, EcoVec};
use typst::eval::Tracer;
use typst::eval::{Repr, Tracer};
use typst::model::DelayedErrors;
use crate::prelude::*;
@ -181,7 +181,7 @@ use crate::prelude::*;
/// function to `update` that determines the value of the state based on its
/// previous value.
#[ty(scope)]
#[derive(Clone, PartialEq, Hash)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct State {
/// The key that identifies the state.
key: Str,
@ -335,13 +335,9 @@ impl State {
}
}
impl Debug for State {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("state(")?;
self.key.fmt(f)?;
f.write_str(", ")?;
self.init.fmt(f)?;
f.write_char(')')
impl Repr for State {
fn repr(&self) -> EcoString {
eco_format!("state({}, {})", self.key.repr(), self.init.repr())
}
}
@ -351,7 +347,7 @@ cast! {
/// An update to perform on a state.
#[ty]
#[derive(Clone, PartialEq, Hash)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum StateUpdate {
/// Set the state to the specified value.
Set(Value),
@ -359,9 +355,9 @@ pub enum StateUpdate {
Func(Func),
}
impl Debug for StateUpdate {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("..")
impl Repr for StateUpdate {
fn repr(&self) -> EcoString {
"..".into()
}
}

View File

@ -16,7 +16,7 @@ pub use typst::doc::*;
#[doc(no_inline)]
pub use typst::eval::{
array, cast, dict, format_str, func, scope, ty, Args, Array, Bytes, Cast, Dict,
FromValue, Func, IntoValue, Scope, Str, Symbol, Type, Value, Vm,
FromValue, Func, IntoValue, Repr, Scope, Str, Symbol, Type, Value, Vm,
};
#[doc(no_inline)]
pub use typst::geom::*;

View File

@ -346,6 +346,12 @@ impl Fold for Decoration {
}
}
impl Repr for Decoration {
fn repr(&self) -> EcoString {
eco_format!("{self:?}")
}
}
cast! {
type Decoration,
}

View File

@ -6,9 +6,9 @@ use std::ops::Range;
use std::str::FromStr;
use std::sync::Arc;
use ecow::EcoString;
use ecow::{eco_format, EcoString};
use crate::eval::{cast, dict, ty, Dict, Value};
use crate::eval::{cast, dict, ty, Dict, Repr, Value};
use crate::export::PdfPageLabel;
use crate::font::Font;
use crate::geom::{
@ -762,6 +762,12 @@ impl Debug for Meta {
}
}
impl Repr for Meta {
fn repr(&self) -> EcoString {
eco_format!("{self:?}")
}
}
/// A link destination.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Destination {
@ -773,6 +779,12 @@ pub enum Destination {
Location(Location),
}
impl Repr for Destination {
fn repr(&self) -> EcoString {
eco_format!("{self:?}")
}
}
cast! {
Destination,
self => match self {

View File

@ -1,8 +1,8 @@
use std::fmt::{self, Debug, Formatter};
use ecow::{eco_format, EcoVec};
use ecow::{eco_format, EcoString, EcoVec};
use super::{func, scope, ty, Array, Dict, FromValue, IntoValue, Str, Value};
use super::{func, scope, ty, Array, Dict, FromValue, IntoValue, Repr, Str, Value};
use crate::diag::{bail, At, SourceDiagnostic, SourceResult};
use crate::syntax::{Span, Spanned};
use crate::util::pretty_array_like;
@ -47,7 +47,7 @@ pub struct Args {
}
/// An argument to a function call: `12` or `draw: false`.
#[derive(Clone, PartialEq, Hash)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Arg {
/// The span of the whole argument.
pub span: Span,
@ -252,18 +252,23 @@ impl Args {
impl Debug for Args {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let pieces: Vec<_> =
self.items.iter().map(|arg| eco_format!("{arg:?}")).collect();
f.write_str(&pretty_array_like(&pieces, false))
f.debug_list().entries(&self.items).finish()
}
}
impl Debug for Arg {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if let Some(name) = &self.name {
f.write_str(name)?;
f.write_str(": ")?;
}
Debug::fmt(&self.value.v, f)
impl Repr for Args {
fn repr(&self) -> EcoString {
let pieces = self.items.iter().map(Arg::repr).collect::<Vec<_>>();
pretty_array_like(&pieces, false).into()
}
}
impl Repr for Arg {
fn repr(&self) -> EcoString {
if let Some(name) = &self.name {
eco_format!("{}: {}", name, self.value.v.repr())
} else {
self.value.v.repr()
}
}
}

View File

@ -1,5 +1,5 @@
use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter};
use std::fmt::{Debug, Formatter};
use std::num::NonZeroI64;
use std::ops::{Add, AddAssign};
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use super::{
cast, func, ops, scope, ty, Args, Bytes, CastInfo, FromValue, Func, IntoValue,
Reflect, Value, Version, Vm,
Reflect, Repr, Value, Version, Vm,
};
use crate::diag::{At, SourceResult, StrResult};
use crate::eval::ops::{add, mul};
@ -808,14 +808,23 @@ cast! {
}
impl Debug for Array {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.debug_list().entries(&self.0).finish()
}
}
impl Repr for Array {
fn repr(&self) -> EcoString {
let max = 40;
let mut pieces: Vec<_> =
self.iter().take(max).map(|value| eco_format!("{value:?}")).collect();
let mut pieces: Vec<_> = self
.iter()
.take(max)
.map(|value| eco_format!("{}", value.repr()))
.collect();
if self.len() > max {
pieces.push(eco_format!(".. ({} items omitted)", self.len() - max));
}
f.write_str(&pretty_array_like(&pieces, self.len() == 1))
pretty_array_like(&pieces, self.len() == 1).into()
}
}

View File

@ -1,6 +1,7 @@
use std::fmt::{self, Debug, Formatter};
use ecow::EcoString;
use std::fmt::Debug;
use super::{ty, CastInfo, FromValue, IntoValue, Reflect, Type, Value};
use super::{ty, CastInfo, FromValue, IntoValue, Reflect, Repr, Type, Value};
use crate::diag::StrResult;
/// A value that indicates a smart default.
@ -12,7 +13,7 @@ use crate::diag::StrResult;
/// parameter. Setting it to `{auto}` lets Typst automatically determine the
/// direction from the [text language]($text.lang).
#[ty(name = "auto")]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct AutoValue;
impl IntoValue for AutoValue {
@ -44,8 +45,8 @@ impl Reflect for AutoValue {
}
}
impl Debug for AutoValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("auto")
impl Repr for AutoValue {
fn repr(&self) -> EcoString {
"auto".into()
}
}

View File

@ -1,4 +1,6 @@
use super::ty;
use ecow::EcoString;
use super::{ty, Repr};
/// A type with two states.
///
@ -13,3 +15,12 @@ use super::ty;
/// ```
#[ty(title = "Boolean")]
type bool;
impl Repr for bool {
fn repr(&self) -> EcoString {
match self {
true => "true".into(),
false => "false".into(),
}
}
}

View File

@ -1,5 +1,5 @@
use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter};
use std::fmt::Debug;
use std::ops::{Add, AddAssign, Deref};
use std::sync::Arc;
@ -7,7 +7,7 @@ use comemo::Prehashed;
use ecow::{eco_format, EcoString};
use serde::{Serialize, Serializer};
use super::{cast, func, scope, ty, Array, Reflect, Str, Value};
use super::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value};
use crate::diag::{bail, StrResult};
/// A sequence of bytes.
@ -38,7 +38,7 @@ use crate::diag::{bail, StrResult};
/// #str(data.slice(1, 4))
/// ```
#[ty(scope)]
#[derive(Clone, Hash, Eq, PartialEq)]
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct Bytes(Arc<Prehashed<Cow<'static, [u8]>>>);
impl Bytes {
@ -179,9 +179,9 @@ impl AsRef<[u8]> for Bytes {
}
}
impl Debug for Bytes {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "bytes({})", self.len())
impl Repr for Bytes {
fn repr(&self) -> EcoString {
eco_format!("bytes({})", self.len())
}
}

View File

@ -6,7 +6,7 @@ use std::ops::Add;
use ecow::{eco_format, EcoString};
use super::{Type, Value};
use super::{Repr, Type, Value};
use crate::diag::{At, SourceResult, StrResult};
use crate::syntax::{Span, Spanned};
use crate::util::separated_list;
@ -239,7 +239,7 @@ impl CastInfo {
self.walk(|info| match info {
CastInfo::Any => parts.push("anything".into()),
CastInfo::Value(value, _) => {
parts.push(value.repr().into());
parts.push(value.repr());
if value.ty() == found.ty() {
matching_type = true;
}

View File

@ -1,6 +1,6 @@
use std::cmp::Ordering;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{Add, Sub};
@ -9,7 +9,7 @@ use time::error::{Format, InvalidFormatDescription};
use time::macros::format_description;
use time::{format_description, Month, PrimitiveDateTime};
use super::{cast, func, scope, ty, Dict, Duration, Str, Value, Vm};
use super::{cast, func, scope, ty, Dict, Duration, Repr, Str, Value, Vm};
use crate::diag::{bail, StrResult};
use crate::geom::Smart;
use crate::util::pretty_array_like;
@ -113,7 +113,7 @@ use crate::World;
/// components such as `hour` or `minute`, which would only work on datetimes
/// that have a specified time.
#[ty(scope)]
#[derive(Clone, Copy, PartialEq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub enum Datetime {
/// Representation as a date.
Date(time::Date),
@ -426,20 +426,20 @@ impl Datetime {
}
}
impl Debug for Datetime {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let year = self.year().map(|y| eco_format!("year: {y}"));
let month = self.month().map(|m| eco_format!("month: {m}"));
let day = self.day().map(|d| eco_format!("day: {d}"));
let hour = self.hour().map(|h| eco_format!("hour: {h}"));
let minute = self.minute().map(|m| eco_format!("minute: {m}"));
let second = self.second().map(|s| eco_format!("second: {s}"));
impl Repr for Datetime {
fn repr(&self) -> EcoString {
let year = self.year().map(|y| eco_format!("year: {}", (y as i64).repr()));
let month = self.month().map(|m| eco_format!("month: {}", (m as i64).repr()));
let day = self.day().map(|d| eco_format!("day: {}", (d as i64).repr()));
let hour = self.hour().map(|h| eco_format!("hour: {}", (h as i64).repr()));
let minute = self.minute().map(|m| eco_format!("minute: {}", (m as i64).repr()));
let second = self.second().map(|s| eco_format!("second: {}", (s as i64).repr()));
let filtered = [year, month, day, hour, minute, second]
.into_iter()
.flatten()
.collect::<EcoVec<_>>();
write!(f, "datetime{}", &pretty_array_like(&filtered, false))
eco_format!("datetime{}", &pretty_array_like(&filtered, false))
}
}

View File

@ -1,4 +1,4 @@
use std::fmt::{self, Debug, Formatter};
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::{Add, AddAssign};
use std::sync::Arc;
@ -7,7 +7,7 @@ use ecow::{eco_format, EcoString};
use indexmap::IndexMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{array, func, scope, ty, Array, Str, Value};
use super::{array, func, scope, ty, Array, Repr, Str, Value};
use crate::diag::StrResult;
use crate::syntax::is_ident;
use crate::util::{pretty_array_like, separated_list, ArcExt};
@ -117,7 +117,7 @@ impl Dict {
pub fn finish(&self, expected: &[&str]) -> StrResult<()> {
if let Some((key, _)) = self.iter().next() {
let parts: Vec<_> = expected.iter().map(|s| eco_format!("\"{s}\"")).collect();
let mut msg = format!("unexpected key {key:?}, valid keys are ");
let mut msg = format!("unexpected key {}, valid keys are ", key.repr());
msg.push_str(&separated_list(&parts, "and"));
return Err(msg.into());
}
@ -200,9 +200,15 @@ impl Dict {
}
impl Debug for Dict {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_map().entries(self.0.iter()).finish()
}
}
impl Repr for Dict {
fn repr(&self) -> EcoString {
if self.is_empty() {
return f.write_str("(:)");
return "(:)".into();
}
let max = 40;
@ -211,9 +217,9 @@ impl Debug for Dict {
.take(max)
.map(|(key, value)| {
if is_ident(key) {
eco_format!("{key}: {value:?}")
eco_format!("{key}: {}", value.repr())
} else {
eco_format!("{key:?}: {value:?}")
eco_format!("{}: {}", key.repr(), value.repr())
}
})
.collect();
@ -222,7 +228,7 @@ impl Debug for Dict {
pieces.push(eco_format!(".. ({} pairs omitted)", self.len() - max));
}
f.write_str(&pretty_array_like(&pieces, false))
pretty_array_like(&pieces, false).into()
}
}
@ -310,15 +316,15 @@ impl From<IndexMap<Str, Value>> for Dict {
/// The missing key access error message.
#[cold]
fn missing_key(key: &str) -> EcoString {
eco_format!("dictionary does not contain key {:?}", Str::from(key))
eco_format!("dictionary does not contain key {}", key.repr())
}
/// The missing key access error message when no default was fiven.
#[cold]
fn missing_key_no_default(key: &str) -> EcoString {
eco_format!(
"dictionary does not contain key {:?} \
"dictionary does not contain key {} \
and no default value was specified",
Str::from(key)
key.repr()
)
}

View File

@ -1,15 +1,15 @@
use ecow::eco_format;
use std::fmt;
use std::fmt::{Debug, Formatter};
use ecow::{eco_format, EcoString};
use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Neg, Sub};
use time::ext::NumericalDuration;
use super::{func, scope, ty};
use super::{func, scope, ty, Repr};
use crate::util::pretty_array_like;
/// Represents a positive or negative span of time.
#[ty(scope)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Duration(time::Duration);
impl Duration {
@ -111,41 +111,41 @@ impl Duration {
}
}
impl Debug for Duration {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for Duration {
fn repr(&self) -> EcoString {
let mut tmp = self.0;
let mut vec = Vec::with_capacity(5);
let weeks = tmp.whole_seconds() / 604_800.0 as i64;
if weeks != 0 {
vec.push(eco_format!("weeks: {weeks}"));
vec.push(eco_format!("weeks: {}", weeks.repr()));
}
tmp -= weeks.weeks();
let days = tmp.whole_days();
if days != 0 {
vec.push(eco_format!("days: {days}"));
vec.push(eco_format!("days: {}", days.repr()));
}
tmp -= days.days();
let hours = tmp.whole_hours();
if hours != 0 {
vec.push(eco_format!("hours: {hours}"));
vec.push(eco_format!("hours: {}", hours.repr()));
}
tmp -= hours.hours();
let minutes = tmp.whole_minutes();
if minutes != 0 {
vec.push(eco_format!("minutes: {minutes}"));
vec.push(eco_format!("minutes: {}", minutes.repr()));
}
tmp -= minutes.minutes();
let seconds = tmp.whole_seconds();
if seconds != 0 {
vec.push(eco_format!("seconds: {seconds}"));
vec.push(eco_format!("seconds: {}", seconds.repr()));
}
write!(f, "duration{}", &pretty_array_like(&vec, false))
eco_format!("duration{}", &pretty_array_like(&vec, false))
}
}

View File

@ -1,7 +1,8 @@
use std::fmt::{self, Debug, Formatter};
use std::fmt::Debug;
use std::sync::Arc;
use comemo::{Prehashed, Tracked, TrackedMut};
use ecow::EcoString;
use once_cell::sync::Lazy;
use super::{
@ -118,7 +119,7 @@ pub use typst_macros::func;
/// [`array.push(value)`]($array.push). These can modify the values they are
/// called on.
#[ty(scope, name = "function")]
#[derive(Clone, Hash)]
#[derive(Debug, Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Func {
/// The internal representation.
@ -128,7 +129,7 @@ pub struct Func {
}
/// The different kinds of function representations.
#[derive(Clone, PartialEq, Hash)]
#[derive(Debug, Clone, PartialEq, Hash)]
enum Repr {
/// A native Rust function.
Native(Static<NativeFuncData>),
@ -363,11 +364,11 @@ impl Func {
}
}
impl Debug for Func {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl super::Repr for Func {
fn repr(&self) -> EcoString {
match self.name() {
Some(name) => write!(f, "{name}"),
None => f.write_str("(..) => .."),
Some(name) => name.into(),
None => "(..) => ..".into(),
}
}
}
@ -412,6 +413,7 @@ pub trait NativeFunc {
}
/// Defines a native function.
#[derive(Debug)]
pub struct NativeFuncData {
pub function: fn(&mut Vm, &mut Args) -> SourceResult<Value>,
pub name: &'static str,
@ -461,7 +463,7 @@ pub struct ParamInfo {
}
/// A user-defined closure.
#[derive(Hash)]
#[derive(Debug, Hash)]
pub(super) struct Closure {
/// The closure's syntax node. Must be castable to `ast::Closure`.
pub node: SyntaxNode,

View File

@ -1,8 +1,8 @@
use std::num::{NonZeroI64, NonZeroIsize, NonZeroU64, NonZeroUsize};
use ecow::eco_format;
use ecow::{eco_format, EcoString};
use super::{cast, func, scope, ty, Str, Value};
use super::{cast, func, scope, ty, Repr, Str, Value};
/// A whole number.
///
@ -51,6 +51,18 @@ impl i64 {
}
}
impl Repr for i64 {
fn repr(&self) -> EcoString {
eco_format!("{self}")
}
}
impl Repr for f64 {
fn repr(&self) -> EcoString {
eco_format!("{self}")
}
}
/// A value that can be cast to an integer.
pub struct ToInt(i64);

View File

@ -65,7 +65,7 @@ pub use self::str::{format_str, Regex, Str};
pub use self::symbol::{symbols, Symbol};
pub use self::tracer::Tracer;
pub use self::ty::{scope, ty, NativeType, NativeTypeData, Type};
pub use self::value::{Dynamic, Value};
pub use self::value::{Dynamic, Repr, Value};
pub use self::version::Version;
use std::collections::HashSet;

View File

@ -1,4 +1,4 @@
use std::fmt::{self, Debug, Formatter};
use std::fmt::Debug;
use std::sync::Arc;
use ecow::{eco_format, EcoString};
@ -24,7 +24,7 @@ use crate::diag::StrResult;
/// >>> #(-3)
/// ```
#[ty]
#[derive(Clone, Hash)]
#[derive(Debug, Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Module {
/// The module's name.
@ -34,7 +34,7 @@ pub struct Module {
}
/// The internal representation.
#[derive(Clone, Hash)]
#[derive(Debug, Clone, Hash)]
struct Repr {
/// The top-level definitions that were bound in this module.
scope: Scope,
@ -100,9 +100,9 @@ impl Module {
}
}
impl Debug for Module {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "<module {}>", self.name())
impl super::Repr for Module {
fn repr(&self) -> EcoString {
eco_format!("<module {}>", self.name())
}
}

View File

@ -1,8 +1,9 @@
use std::fmt::{self, Debug, Formatter};
use ecow::EcoString;
use std::fmt::Debug;
use serde::{Serialize, Serializer};
use super::{cast, ty, CastInfo, FromValue, IntoValue, Reflect, Type, Value};
use super::{cast, ty, CastInfo, FromValue, IntoValue, Reflect, Repr, Type, Value};
use crate::diag::StrResult;
/// A value that indicates the absence of any other value.
@ -18,7 +19,7 @@ use crate::diag::StrResult;
/// Not visible: #none
/// ```
#[ty(name = "none")]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NoneValue;
impl Reflect for NoneValue {
@ -50,9 +51,9 @@ impl FromValue for NoneValue {
}
}
impl Debug for NoneValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("none")
impl Repr for NoneValue {
fn repr(&self) -> EcoString {
"none".into()
}
}

View File

@ -1,11 +1,10 @@
//! Operations on values.
use std::cmp::Ordering;
use std::fmt::Debug;
use ecow::eco_format;
use super::{format_str, IntoValue, Regex, Value};
use super::{format_str, IntoValue, Regex, Repr, Value};
use crate::diag::{bail, StrResult};
use crate::geom::{Align, Length, Numeric, Rel, Smart, Stroke};
use Value::*;
@ -436,9 +435,9 @@ pub fn compare(lhs: &Value, rhs: &Value) -> StrResult<Ordering> {
}
/// Try to compare two values.
fn try_cmp_values<T: PartialOrd + Debug>(a: &T, b: &T) -> StrResult<Ordering> {
fn try_cmp_values<T: PartialOrd + Repr>(a: &T, b: &T) -> StrResult<Ordering> {
a.partial_cmp(b)
.ok_or_else(|| eco_format!("cannot compare {:?} with {:?}", a, b))
.ok_or_else(|| eco_format!("cannot compare {} with {}", a.repr(), b.repr()))
}
/// Try to compare two datetimes.

View File

@ -288,7 +288,13 @@ impl Plugin {
impl Debug for Plugin {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("plugin(..)")
f.pad("Plugin(..)")
}
}
impl super::Repr for Plugin {
fn repr(&self) -> EcoString {
"plugin(..)".into()
}
}

View File

@ -1,5 +1,5 @@
use std::borrow::{Borrow, Cow};
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::{Add, AddAssign, Deref, Range};
@ -8,13 +8,14 @@ use serde::{Deserialize, Serialize};
use unicode_segmentation::UnicodeSegmentation;
use super::{
cast, dict, func, scope, ty, Args, Array, Bytes, Dict, Func, IntoValue, Type, Value,
Version, Vm,
cast, dict, func, scope, ty, Args, Array, Bytes, Dict, Func, IntoValue, Repr, Type,
Value, Version, Vm,
};
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::geom::Align;
use crate::model::Label;
use crate::syntax::{Span, Spanned};
use crate::util::fmt::format_int_with_base;
/// Create a new [`Str`] from a format string.
#[macro_export]
@ -68,7 +69,8 @@ pub use ecow::eco_format;
/// - `[\t]` for a tab
/// - `[\u{1f600}]` for a hexadecimal Unicode escape sequence
#[ty(scope, title = "String")]
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct Str(EcoString);
@ -616,45 +618,6 @@ cast! {
v: Str => Self::Str(v),
}
/// Format an integer in a base.
fn format_int_with_base(mut n: i64, base: i64) -> EcoString {
if n == 0 {
return "0".into();
}
// In Rust, `format!("{:x}", -14i64)` is not `-e` but `fffffffffffffff2`.
// So we can only use the built-in for decimal, not bin/oct/hex.
if base == 10 {
return eco_format!("{n}");
}
// The largest output is `to_base(i64::MIN, 2)`, which is 65 chars long.
const SIZE: usize = 65;
let mut digits = [b'\0'; SIZE];
let mut i = SIZE;
// It's tempting to take the absolute value, but this will fail for i64::MIN.
// Instead, we turn n negative, as -i64::MAX is perfectly representable.
let negative = n < 0;
if n > 0 {
n = -n;
}
while n != 0 {
let digit = char::from_digit(-(n % base) as u32, base as u32);
i -= 1;
digits[i] = digit.unwrap_or('?') as u8;
n /= base;
}
if negative {
i -= 1;
digits[i] = b'-';
}
std::str::from_utf8(&digits[i..]).unwrap_or_default().into()
}
/// The out of bounds access error message.
#[cold]
fn out_of_bounds(index: i64, len: usize) -> EcoString {
@ -717,18 +680,9 @@ impl Display for Str {
}
}
impl Debug for Str {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_char('"')?;
for c in self.chars() {
match c {
'\0' => f.write_str("\\u{0}")?,
'\'' => f.write_str("'")?,
'"' => f.write_str(r#"\""#)?,
_ => Display::fmt(&c.escape_debug(), f)?,
}
}
f.write_char('"')
impl Repr for Str {
fn repr(&self) -> EcoString {
self.as_ref().repr()
}
}
@ -836,6 +790,29 @@ cast! {
v: Str => v.into(),
}
impl Repr for &str {
fn repr(&self) -> EcoString {
let mut r = EcoString::with_capacity(self.len() + 2);
r.push('"');
for c in self.chars() {
match c {
'\0' => r.push_str(r"\u{0}"),
'\'' => r.push('\''),
'"' => r.push_str(r#"\""#),
_ => c.escape_debug().for_each(|c| r.push(c)),
}
}
r.push('"');
r
}
}
impl Repr for EcoString {
fn repr(&self) -> EcoString {
self.as_ref().repr()
}
}
/// A regular expression.
///
/// Can be used as a [show rule selector]($styling/#show-rules) and with
@ -856,7 +833,7 @@ cast! {
/// .split(regex("[,;]")))
/// ```
#[ty(scope)]
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct Regex(regex::Regex);
impl Regex {
@ -895,9 +872,9 @@ impl Deref for Regex {
}
}
impl Debug for Regex {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "regex({:?})", self.0.as_str())
impl Repr for Regex {
fn repr(&self) -> EcoString {
eco_format!("regex({})", self.0.as_str().repr())
}
}

View File

@ -3,7 +3,7 @@ use std::collections::BTreeSet;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::sync::Arc;
use ecow::EcoString;
use ecow::{eco_format, EcoString};
use serde::{Serialize, Serializer};
use super::{cast, func, scope, ty, Array};
@ -43,11 +43,11 @@ pub use typst_macros::symbols;
/// $arrow.t.quad$
/// ```
#[ty(scope)]
#[derive(Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Symbol(Repr);
/// The internal representation.
#[derive(Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum Repr {
Single(char),
Const(&'static [(&'static str, char)]),
@ -55,7 +55,7 @@ enum Repr {
}
/// A collection of symbols.
#[derive(Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum List {
Static(&'static [(&'static str, char)]),
Runtime(Box<[(EcoString, char)]>),
@ -208,15 +208,15 @@ impl Symbol {
}
}
impl Debug for Symbol {
impl Display for Symbol {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_char(self.get())
}
}
impl Display for Symbol {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_char(self.get())
impl super::Repr for Symbol {
fn repr(&self) -> EcoString {
eco_format!("\"{}\"", self.get())
}
}

View File

@ -1,10 +1,10 @@
use std::cmp::Ordering;
use std::fmt::{self, Debug, Display, Formatter};
use ecow::eco_format;
use ecow::{eco_format, EcoString};
use once_cell::sync::Lazy;
use super::{cast, func, Func, NativeFuncData, Scope, Value};
use super::{cast, func, Func, NativeFuncData, Repr, Scope, Value};
use crate::diag::StrResult;
use crate::util::Static;
@ -53,7 +53,7 @@ pub use typst_macros::{scope, ty};
/// - The `{in}` operator on a type and a dictionary will evaluate to `{true}`
/// if the dictionary has a string key matching the type's name
#[ty(scope)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Type(Static<NativeTypeData>);
impl Type {
@ -139,9 +139,9 @@ impl Type {
}
}
impl Debug for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.long_name())
impl Repr for Type {
fn repr(&self) -> EcoString {
self.long_name().into()
}
}
@ -180,6 +180,7 @@ pub trait NativeType {
}
/// Defines a native type.
#[derive(Debug)]
pub struct NativeTypeData {
pub name: &'static str,
pub long_name: &'static str,

View File

@ -1,10 +1,10 @@
use std::any::Any;
use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter};
use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use ecow::eco_format;
use ecow::{eco_format, EcoString};
use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer};
use serde::de::{Error, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -12,9 +12,9 @@ use siphasher::sip128::{Hasher128, SipHasher13};
use typst::eval::Duration;
use super::{
fields, format_str, ops, Args, Array, AutoValue, Bytes, CastInfo, Content, Dict,
FromValue, Func, IntoValue, Module, NativeType, NoneValue, Plugin, Reflect, Scope,
Str, Symbol, Type, Version,
fields, ops, Args, Array, AutoValue, Bytes, CastInfo, Content, Dict, FromValue, Func,
IntoValue, Module, NativeType, NoneValue, Plugin, Reflect, Scope, Str, Symbol, Type,
Version,
};
use crate::diag::StrResult;
use crate::eval::Datetime;
@ -23,7 +23,7 @@ use crate::model::{Label, Styles};
use crate::syntax::{ast, Span};
/// A computational value.
#[derive(Default, Clone)]
#[derive(Debug, Default, Clone)]
pub enum Value {
/// The value that indicates the absence of a meaningful value.
#[default]
@ -90,7 +90,7 @@ impl Value {
/// Create a new dynamic value.
pub fn dynamic<T>(any: T) -> Self
where
T: Debug + NativeType + PartialEq + Hash + Sync + Send + 'static,
T: Debug + Repr + NativeType + PartialEq + Hash + Sync + Send + 'static,
{
Self::Dyn(Dynamic::new(any))
}
@ -194,11 +194,6 @@ impl Value {
}
}
/// Return the debug representation of the value.
pub fn repr(&self) -> Str {
format_str!("{self:?}")
}
/// Return the display representation of the value.
pub fn display(self) -> Content {
match self {
@ -210,7 +205,7 @@ impl Value {
Self::Symbol(v) => item!(text)(v.get().into()),
Self::Content(v) => v,
Self::Module(module) => module.content(),
_ => item!(raw)(self.repr().into(), Some("typc".into()), false),
_ => item!(raw)(self.repr(), Some("typc".into()), false),
}
}
@ -224,38 +219,38 @@ impl Value {
}
}
impl Debug for Value {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for Value {
fn repr(&self) -> EcoString {
match self {
Self::None => Debug::fmt(&NoneValue, f),
Self::Auto => Debug::fmt(&AutoValue, f),
Self::Bool(v) => Debug::fmt(v, f),
Self::Int(v) => Debug::fmt(v, f),
Self::Float(v) => Debug::fmt(v, f),
Self::Length(v) => Debug::fmt(v, f),
Self::Angle(v) => Debug::fmt(v, f),
Self::Ratio(v) => Debug::fmt(v, f),
Self::Relative(v) => Debug::fmt(v, f),
Self::Fraction(v) => Debug::fmt(v, f),
Self::Color(v) => Debug::fmt(v, f),
Self::Gradient(v) => Debug::fmt(v, f),
Self::Symbol(v) => Debug::fmt(v, f),
Self::Version(v) => Debug::fmt(v, f),
Self::Str(v) => Debug::fmt(v, f),
Self::Bytes(v) => Debug::fmt(v, f),
Self::Label(v) => Debug::fmt(v, f),
Self::Datetime(v) => Debug::fmt(v, f),
Self::Duration(v) => Debug::fmt(v, f),
Self::Content(v) => Debug::fmt(v, f),
Self::Styles(v) => Debug::fmt(v, f),
Self::Array(v) => Debug::fmt(v, f),
Self::Dict(v) => Debug::fmt(v, f),
Self::Func(v) => Debug::fmt(v, f),
Self::Args(v) => Debug::fmt(v, f),
Self::Type(v) => Debug::fmt(v, f),
Self::Module(v) => Debug::fmt(v, f),
Self::Plugin(v) => Debug::fmt(v, f),
Self::Dyn(v) => Debug::fmt(v, f),
Self::None => NoneValue.repr(),
Self::Auto => AutoValue.repr(),
Self::Bool(v) => v.repr(),
Self::Int(v) => v.repr(),
Self::Float(v) => v.repr(),
Self::Length(v) => v.repr(),
Self::Angle(v) => v.repr(),
Self::Ratio(v) => v.repr(),
Self::Relative(v) => v.repr(),
Self::Fraction(v) => v.repr(),
Self::Color(v) => v.repr(),
Self::Gradient(v) => v.repr(),
Self::Symbol(v) => v.repr(),
Self::Version(v) => v.repr(),
Self::Str(v) => v.repr(),
Self::Bytes(v) => v.repr(),
Self::Label(v) => v.repr(),
Self::Datetime(v) => v.repr(),
Self::Duration(v) => v.repr(),
Self::Content(v) => v.repr(),
Self::Styles(v) => v.repr(),
Self::Array(v) => v.repr(),
Self::Dict(v) => v.repr(),
Self::Func(v) => v.repr(),
Self::Args(v) => v.repr(),
Self::Type(v) => v.repr(),
Self::Module(v) => v.repr(),
Self::Plugin(v) => v.repr(),
Self::Dyn(v) => v.repr(),
}
}
}
@ -452,7 +447,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
}
/// A value that is not part of the built-in enum.
#[derive(Clone, Hash)]
#[derive(Debug, Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Dynamic(Arc<dyn Bounds>);
@ -460,7 +455,7 @@ impl Dynamic {
/// Create a new instance from any value that satisfies the required bounds.
pub fn new<T>(any: T) -> Self
where
T: Debug + NativeType + PartialEq + Hash + Sync + Send + 'static,
T: Debug + Repr + NativeType + PartialEq + Hash + Sync + Send + 'static,
{
Self(Arc::new(any))
}
@ -481,9 +476,9 @@ impl Dynamic {
}
}
impl Debug for Dynamic {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(&self.0, f)
impl Repr for Dynamic {
fn repr(&self) -> EcoString {
self.0.repr()
}
}
@ -493,7 +488,13 @@ impl PartialEq for Dynamic {
}
}
trait Bounds: Debug + Sync + Send + 'static {
/// A trait that defines the `repr` of a Typst value.
pub trait Repr {
/// Return the debug representation of the value.
fn repr(&self) -> EcoString;
}
trait Bounds: Debug + Repr + Sync + Send + 'static {
fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &Dynamic) -> bool;
fn dyn_ty(&self) -> Type;
@ -502,7 +503,7 @@ trait Bounds: Debug + Sync + Send + 'static {
impl<T> Bounds for T
where
T: Debug + NativeType + PartialEq + Hash + Sync + Send + 'static,
T: Debug + Repr + NativeType + PartialEq + Hash + Sync + Send + 'static,
{
fn as_any(&self) -> &dyn Any {
self
@ -631,7 +632,7 @@ mod tests {
#[track_caller]
fn test(value: impl IntoValue, exp: &str) {
assert_eq!(format!("{:?}", value.into_value()), exp);
assert_eq!(value.into_value().repr(), exp);
}
#[test]

View File

@ -1,11 +1,11 @@
use std::cmp::Ordering;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::fmt::{self, Display, Formatter, Write};
use std::hash::Hash;
use std::iter::repeat;
use ecow::{eco_format, EcoVec};
use ecow::{eco_format, EcoString, EcoVec};
use super::{cast, func, scope, ty};
use super::{cast, func, scope, ty, Repr};
use crate::diag::{bail, error, StrResult};
use crate::util::pretty_array_like;
@ -19,7 +19,7 @@ use crate::util::pretty_array_like;
/// The first three components have names: `major`, `minor`, `patch`. All
/// components after that do not have names.
#[ty(scope)]
#[derive(Default, Clone, Hash)]
#[derive(Debug, Default, Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Version(EcoVec<u32>);
@ -165,7 +165,7 @@ impl PartialEq for Version {
}
impl Display for Version {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut first = true;
for &v in &self.0 {
if !first {
@ -178,11 +178,10 @@ impl Display for Version {
}
}
impl Debug for Version {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("version")?;
impl Repr for Version {
fn repr(&self) -> EcoString {
let parts: Vec<_> = self.0.iter().map(|v| eco_format!("{v}")).collect();
f.write_str(&pretty_array_like(&parts, false))
eco_format!("version{}", &pretty_array_like(&parts, false))
}
}

View File

@ -11,6 +11,7 @@ use super::color::PaintEncode;
use super::extg::ExternalGraphicsState;
use super::{deflate, AbsExt, EmExt, PdfContext, RefExt};
use crate::doc::{Destination, Frame, FrameItem, GroupItem, Meta, TextItem};
use crate::eval::Repr;
use crate::font::Font;
use crate::geom::{
self, Abs, Em, FixedStroke, Geometry, LineCap, LineJoin, Numeric, Paint, Point,
@ -710,6 +711,12 @@ pub struct PdfPageLabel {
pub offset: Option<NonZeroUsize>,
}
impl Repr for PdfPageLabel {
fn repr(&self) -> EcoString {
eco_format!("{self:?}")
}
}
/// A PDF page label number style.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum PdfPageLabelStyle {

View File

@ -133,7 +133,7 @@ impl Hash for Font {
impl Debug for Font {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Font({},{:?})", self.info().family, self.info().variant)
write!(f, "Font({}, {:?})", self.info().family, self.info().variant)
}
}

View File

@ -1,8 +1,9 @@
use ecow::EcoString;
use std::fmt::{self, Debug, Formatter};
use serde::{Deserialize, Serialize};
use crate::eval::{cast, Cast, IntoValue};
use crate::eval::{cast, Cast, IntoValue, Repr};
use crate::geom::Ratio;
/// Properties that distinguish a font from other fonts in the same family.
@ -176,7 +177,7 @@ cast! {
}
/// The width of a font.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct FontStretch(u16);
@ -247,10 +248,9 @@ impl Default for FontStretch {
Self::NORMAL
}
}
impl Debug for FontStretch {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.to_ratio().fmt(f)
impl Repr for FontStretch {
fn repr(&self) -> EcoString {
self.to_ratio().repr()
}
}
@ -291,6 +291,6 @@ mod tests {
#[test]
fn test_font_stretch_debug() {
assert_eq!(format!("{:?}", FontStretch::EXPANDED), "125%")
assert_eq!(FontStretch::EXPANDED.repr(), "125%")
}
}

View File

@ -1,7 +1,7 @@
use super::*;
/// An absolute length.
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Abs(Scalar);
impl Abs {
@ -133,9 +133,9 @@ impl Numeric for Abs {
}
}
impl Debug for Abs {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}pt", round_2(self.to_pt()))
impl Repr for Abs {
fn repr(&self) -> EcoString {
format_float(self.to_pt(), Some(2), "pt")
}
}
@ -220,7 +220,7 @@ cast! {
}
/// Different units of absolute measurement.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum AbsUnit {
/// Points.
Pt,
@ -244,17 +244,6 @@ impl AbsUnit {
}
}
impl Debug for AbsUnit {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
AbsUnit::Mm => "mm",
AbsUnit::Pt => "pt",
AbsUnit::Cm => "cm",
AbsUnit::In => "in",
})
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -42,7 +42,7 @@ use super::*;
/// #left.y (none)
/// ```
#[ty(scope, name = "alignment")]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Align {
H(HAlign),
V(VAlign),
@ -149,12 +149,12 @@ impl Add for Align {
}
}
impl Debug for Align {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for Align {
fn repr(&self) -> EcoString {
match self {
Self::H(x) => x.fmt(f),
Self::V(y) => y.fmt(f),
Self::Both(x, y) => write!(f, "{x:?} + {y:?}"),
Self::H(x) => x.repr(),
Self::V(y) => y.repr(),
Self::Both(x, y) => eco_format!("{} + {}", x.repr(), y.repr()),
}
}
}
@ -195,7 +195,7 @@ cast! {
}
/// Where to align something horizontally.
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub enum HAlign {
#[default]
Start,
@ -229,15 +229,15 @@ impl HAlign {
}
}
impl Debug for HAlign {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Start => "start",
Self::Left => "left",
Self::Center => "center",
Self::Right => "right",
Self::End => "end",
})
impl Repr for HAlign {
fn repr(&self) -> EcoString {
match self {
Self::Start => "start".into(),
Self::Left => "left".into(),
Self::Center => "center".into(),
Self::Right => "right".into(),
Self::End => "end".into(),
}
}
}
@ -268,12 +268,12 @@ cast! {
self => Align::H(self).into_value(),
align: Align => match align {
Align::H(v) => v,
v => bail!("expected `start`, `left`, `center`, `right`, or `end`, found {v:?}"),
v => bail!("expected `start`, `left`, `center`, `right`, or `end`, found {}", v.repr()),
}
}
/// Where to align something vertically.
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum VAlign {
#[default]
Top,
@ -301,13 +301,13 @@ impl VAlign {
}
}
impl Debug for VAlign {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Top => "top",
Self::Horizon => "horizon",
Self::Bottom => "bottom",
})
impl Repr for VAlign {
fn repr(&self) -> EcoString {
match self {
Self::Top => "top".into(),
Self::Horizon => "horizon".into(),
Self::Bottom => "bottom".into(),
}
}
}
@ -330,7 +330,7 @@ cast! {
self => Align::V(self).into_value(),
align: Align => match align {
Align::V(v) => v,
v => bail!("expected `top`, `horizon`, or `bottom`, found {v:?}"),
v => bail!("expected `top`, `horizon`, or `bottom`, found {}", v.repr()),
}
}
@ -338,7 +338,7 @@ cast! {
///
/// For horizontal alignment, start is globally left and for vertical alignment
/// it is globally top.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum FixedAlign {
Start,
Center,
@ -357,16 +357,6 @@ impl FixedAlign {
}
}
impl Debug for FixedAlign {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Start => "start",
Self::Center => "center",
Self::End => "end",
})
}
}
impl From<Side> for FixedAlign {
fn from(side: Side) -> Self {
match side {

View File

@ -12,7 +12,7 @@ use super::*;
/// #rotate(10deg)[Hello there!]
/// ```
#[ty(scope)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Angle(Scalar);
impl Angle {
@ -119,9 +119,9 @@ impl Numeric for Angle {
}
}
impl Debug for Angle {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}deg", round_2(self.to_deg()))
impl Repr for Angle {
fn repr(&self) -> EcoString {
format_float(self.to_deg(), Some(2), "deg")
}
}
@ -187,7 +187,7 @@ impl Sum for Angle {
}
/// Different units of angular measurement.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum AngleUnit {
/// Radians.
Rad,
@ -205,15 +205,6 @@ impl AngleUnit {
}
}
impl Debug for AngleUnit {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Rad => "rad",
Self::Deg => "deg",
})
}
}
/// A quadrant of the Cartesian plane.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Quadrant {

View File

@ -156,7 +156,7 @@ where
}
/// The two layouting axes.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Axis {
/// The horizontal axis.
X,
@ -182,27 +182,16 @@ impl Axis {
Self::Y => Self::X,
}
}
/// A description of this axis' direction.
pub fn description(self) -> &'static str {
match self {
Self::X => "horizontal",
Self::Y => "vertical",
}
}
}
impl Debug for Axis {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.description())
}
}
cast! {
Axis,
self => self.description().into_value(),
self => match self {
Self::X => "horizontal".into_value(),
Self::Y => "vertical".into_value(),
},
"horizontal" => Self::X,
"vertical" => Self::X,
"vertical" => Self::Y,
}
impl<T> Axes<Option<T>> {

View File

@ -1,6 +1,6 @@
use std::str::FromStr;
use ecow::{eco_format, EcoString, EcoVec};
use ecow::EcoVec;
use once_cell::sync::Lazy;
use palette::encoding::{self, Linear};
use palette::{Darken, Desaturate, FromColor, Lighten, RgbHue, Saturate, ShiftHue};
@ -185,7 +185,7 @@ const ANGLE_EPSILON: f32 = 1e-5;
///
/// Feel free to use or create a package with other presets useful to you!
#[ty(scope)]
#[derive(Copy, Clone)]
#[derive(Debug, Copy, Clone)]
pub enum Color {
/// A 32-bit luma color.
Luma(Luma),
@ -1223,106 +1223,101 @@ impl Color {
}
}
impl Debug for Color {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for Color {
fn repr(&self) -> EcoString {
match self {
Self::Luma(c) => write!(f, "luma({:?})", Ratio::new(c.luma as _)),
Self::Rgba(_) => write!(f, "rgb({:?})", self.to_hex()),
Self::Luma(c) => eco_format!("luma({})", Ratio::new(c.luma as _).repr()),
Self::Rgba(_) => eco_format!("rgb({})", self.to_hex().repr()),
Self::LinearRgb(c) => {
if c.alpha == 1.0 {
write!(
f,
"color.linear-rgb({:?}, {:?}, {:?})",
Ratio::new(c.red as _),
Ratio::new(c.green as _),
Ratio::new(c.blue as _),
eco_format!(
"color.linear-rgb({}, {}, {})",
Ratio::new(c.red as _).repr(),
Ratio::new(c.green as _).repr(),
Ratio::new(c.blue as _).repr(),
)
} else {
write!(
f,
"color.linear-rgb({:?}, {:?}, {:?}, {:?})",
Ratio::new(c.red as _),
Ratio::new(c.green as _),
Ratio::new(c.blue as _),
Ratio::new(c.alpha as _),
eco_format!(
"color.linear-rgb({}, {}, {}, {})",
Ratio::new(c.red as _).repr(),
Ratio::new(c.green as _).repr(),
Ratio::new(c.blue as _).repr(),
Ratio::new(c.alpha as _).repr(),
)
}
}
Self::Cmyk(c) => {
write!(
f,
"rgb({:?}, {:?}, {:?}, {:?})",
Ratio::new(c.c as _),
Ratio::new(c.m as _),
Ratio::new(c.y as _),
Ratio::new(c.k as _),
eco_format!(
"rgb({}, {}, {}, {})",
Ratio::new(c.c as _).repr(),
Ratio::new(c.m as _).repr(),
Ratio::new(c.y as _).repr(),
Ratio::new(c.k as _).repr(),
)
}
Self::Oklab(c) => {
if c.alpha == 1.0 {
write!(
f,
"oklab({:?}, {:.3}, {:.3})",
Ratio::new(c.l as _),
(c.a * 1000.0).round() / 1000.0,
(c.b * 1000.0).round() / 1000.0,
eco_format!(
"oklab({}, {}, {})",
Ratio::new(c.l as _).repr(),
format_float(c.a as _, Some(3), ""),
format_float(c.b as _, Some(3), ""),
)
} else {
write!(
f,
"oklab({:?}, {:?}, {:?}, {:?})",
Ratio::new(c.l as _),
(c.a * 1000.0).round() / 1000.0,
(c.b * 1000.0).round() / 1000.0,
Ratio::new(c.alpha as _),
eco_format!(
"oklab({}, {}, {}, {})",
Ratio::new(c.l as _).repr(),
format_float(c.a as _, Some(3), ""),
format_float(c.b as _, Some(3), ""),
Ratio::new(c.alpha as _).repr(),
)
}
}
Self::Hsl(c) => {
if c.alpha == 1.0 {
write!(
f,
"color.hsl({:?}, {:?}, {:?})",
eco_format!(
"color.hsl({}, {}, {})",
Angle::deg(
c.hue.into_degrees().rem_euclid(360.0 + ANGLE_EPSILON) as _
),
Ratio::new(c.saturation as _),
Ratio::new(c.lightness as _),
)
.repr(),
Ratio::new(c.saturation as _).repr(),
Ratio::new(c.lightness as _).repr(),
)
} else {
write!(
f,
"color.hsl({:?}, {:?}, {:?}, {:?})",
eco_format!(
"color.hsl({}, {}, {}, {})",
Angle::deg(
c.hue.into_degrees().rem_euclid(360.0 + ANGLE_EPSILON) as _
),
Ratio::new(c.saturation as _),
Ratio::new(c.lightness as _),
Ratio::new(c.alpha as _),
)
.repr(),
Ratio::new(c.saturation as _).repr(),
Ratio::new(c.lightness as _).repr(),
Ratio::new(c.alpha as _).repr(),
)
}
}
Self::Hsv(c) => {
if c.alpha == 1.0 {
write!(
f,
"color.hsv({:?}, {:?}, {:?})",
eco_format!(
"color.hsv({}, {}, {})",
Angle::deg(
c.hue.into_degrees().rem_euclid(360.0 + ANGLE_EPSILON) as _
),
Ratio::new(c.saturation as _),
Ratio::new(c.value as _),
)
.repr(),
Ratio::new(c.saturation as _).repr(),
Ratio::new(c.value as _).repr(),
)
} else {
write!(
f,
"color.hsv({:?}, {:?}, {:?}, {:?})",
eco_format!(
"color.hsv({}, {}, {}, {})",
Angle::deg(
c.hue.into_degrees().rem_euclid(360.0 + ANGLE_EPSILON) as _
),
Ratio::new(c.saturation as _),
Ratio::new(c.value as _),
Ratio::new(c.alpha as _),
)
.repr(),
Ratio::new(c.saturation as _).repr(),
Ratio::new(c.value as _).repr(),
Ratio::new(c.alpha as _).repr(),
)
}
}
@ -1404,7 +1399,7 @@ impl FromStr for Color {
}
/// An 8-bit CMYK color.
#[derive(Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Cmyk {
/// The cyan component.
pub c: f32,

View File

@ -1,6 +1,5 @@
use crate::eval::{CastInfo, FromValue, IntoValue, Reflect};
use super::*;
use crate::eval::{CastInfo, FromValue, IntoValue, Reflect};
/// A container with components for the four corners of a rectangle.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]

View File

@ -16,7 +16,7 @@ use super::*;
/// #stack(dir: direction.rtl)[A][B][C]
/// ```
#[ty(scope, name = "direction")]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Dir {
/// Left to right.
LTR,
@ -117,14 +117,14 @@ impl Dir {
}
}
impl Debug for Dir {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::LTR => "ltr",
Self::RTL => "rtl",
Self::TTB => "ttb",
Self::BTT => "btt",
})
impl Repr for Dir {
fn repr(&self) -> EcoString {
match self {
Self::LTR => "ltr".into(),
Self::RTL => "rtl".into(),
Self::TTB => "ttb".into(),
Self::BTT => "btt".into(),
}
}
}

View File

@ -3,7 +3,7 @@ use super::*;
/// A length that is relative to the font size.
///
/// `1em` is the same as the font size.
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Em(Scalar);
impl Em {
@ -68,9 +68,9 @@ impl Numeric for Em {
}
}
impl Debug for Em {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}em", self.get())
impl Repr for Em {
fn repr(&self) -> EcoString {
format_float(self.get(), None, "em")
}
}

View File

@ -13,7 +13,7 @@ use super::*;
/// Left #h(1fr) Left-ish #h(2fr) Right
/// ```
#[ty(name = "fraction")]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Fr(Scalar);
impl Fr {
@ -63,9 +63,9 @@ impl Numeric for Fr {
}
}
impl Debug for Fr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}fr", round_2(self.get()))
impl Repr for Fr {
fn repr(&self) -> EcoString {
format_float(self.get(), Some(2), "fr")
}
}

View File

@ -1,17 +1,14 @@
use std::f64::consts::{FRAC_PI_2, PI, TAU};
use std::f64::{EPSILON, NEG_INFINITY};
use std::fmt::{self, Debug, Write};
use std::hash::Hash;
use std::sync::Arc;
use typst_macros::{cast, func, scope, ty, Cast};
use typst_syntax::{Span, Spanned};
use super::color::{Hsl, Hsv};
use super::*;
use crate::diag::{bail, error, SourceResult};
use crate::eval::{array, Args, Array, Func, IntoValue};
use crate::eval::{array, cast, func, scope, ty, Args, Array, Cast, Func, IntoValue};
use crate::geom::{ColorSpace, Smart};
use crate::syntax::{Span, Spanned};
/// A color gradient.
///
@ -169,7 +166,7 @@ use crate::geom::{ColorSpace, Smart};
/// )
/// ```
#[ty(scope)]
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Gradient {
Linear(Arc<LinearGradient>),
}
@ -531,16 +528,16 @@ impl Gradient {
}
}
impl Debug for Gradient {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Repr for Gradient {
fn repr(&self) -> EcoString {
match self {
Self::Linear(linear) => linear.fmt(f),
Self::Linear(linear) => linear.repr(),
}
}
}
/// A gradient that interpolates between two colors along an axis.
#[derive(Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct LinearGradient {
/// The color stops of this gradient.
pub stops: Vec<(Color, Ratio)>,
@ -554,40 +551,50 @@ pub struct LinearGradient {
pub anti_alias: bool,
}
impl Debug for LinearGradient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("gradient.linear(")?;
impl Repr for LinearGradient {
fn repr(&self) -> EcoString {
let mut r = EcoString::from("gradient.linear(");
let angle = self.angle.to_rad().rem_euclid(TAU);
if angle.abs() < EPSILON {
// Default value, do nothing
} else if (angle - FRAC_PI_2).abs() < EPSILON {
f.write_str("dir: rtl, ")?;
r.push_str("dir: rtl, ");
} else if (angle - PI).abs() < EPSILON {
f.write_str("dir: ttb, ")?;
r.push_str("dir: ttb, ");
} else if (angle - 3.0 * FRAC_PI_2).abs() < EPSILON {
f.write_str("dir: btt, ")?;
r.push_str("dir: btt, ");
} else {
write!(f, "angle: {:?}, ", self.angle)?;
r.push_str("angle: ");
r.push_str(&self.angle.repr());
r.push_str(", ");
}
if self.space != ColorSpace::Oklab {
write!(f, "space: {:?}, ", self.space.into_value())?;
r.push_str("space: ");
r.push_str(&self.space.into_value().repr());
r.push_str(", ");
}
if self.relative.is_custom() {
write!(f, "relative: {:?}, ", self.relative.into_value())?;
r.push_str("relative: ");
r.push_str(&self.relative.into_value().repr());
r.push_str(", ");
}
for (i, (color, offset)) in self.stops.iter().enumerate() {
write!(f, "({color:?}, {offset:?})")?;
r.push('(');
r.push_str(&color.repr());
r.push_str(", ");
r.push_str(&offset.repr());
r.push(')');
if i != self.stops.len() - 1 {
f.write_str(", ")?;
r.push_str(", ");
}
}
f.write_char(')')
r.push(')');
r
}
}

View File

@ -1,5 +1,3 @@
use ecow::eco_format;
use super::*;
use crate::diag::{At, Hint, SourceResult};
use crate::syntax::Span;
@ -33,7 +31,7 @@ use crate::syntax::Span;
/// (that is, excluding the `em` component).
/// - `em`: The amount of `em` units in this length, as a [float]($float).
#[ty(scope)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Length {
/// The absolute part.
pub abs: Abs,
@ -75,7 +73,8 @@ impl Length {
return Ok(());
}
Err(eco_format!(
"cannot convert a length with non-zero em units (`{self:?}`) to {unit}"
"cannot convert a length with non-zero em units (`{}`) to {unit}",
self.repr()
))
.hint(eco_format!("use `length.abs.{unit}()` instead to ignore its em component"))
.at(span)
@ -127,12 +126,12 @@ impl Length {
}
}
impl Debug for Length {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for Length {
fn repr(&self) -> EcoString {
match (self.abs.is_zero(), self.em.is_zero()) {
(false, false) => write!(f, "{:?} + {:?}", self.abs, self.em),
(true, false) => self.em.fmt(f),
(_, true) => self.abs.fmt(f),
(false, false) => eco_format!("{} + {}", self.abs.repr(), self.em.repr()),
(true, false) => self.em.repr(),
(_, true) => self.abs.repr(),
}
}
}

View File

@ -61,9 +61,12 @@ use std::hash::{Hash, Hasher};
use std::iter::Sum;
use std::ops::*;
use ecow::{eco_format, EcoString};
use crate::diag::{bail, StrResult};
use crate::eval::{array, cast, func, scope, ty, Array, Dict, Value};
use crate::eval::{array, cast, func, scope, ty, Array, Dict, Repr, Value};
use crate::model::{Fold, Resolve, StyleChain};
use crate::util::fmt::format_float;
/// Generic access to a structure's components.
pub trait Get<Index> {

View File

@ -1,7 +1,7 @@
use super::*;
/// How a fill or stroke should be painted.
#[derive(Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Paint {
/// A solid color.
Solid(Color),
@ -32,11 +32,11 @@ impl From<Gradient> for Paint {
}
}
impl Debug for Paint {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for Paint {
fn repr(&self) -> EcoString {
match self {
Self::Solid(color) => color.fmt(f),
Self::Gradient(gradient) => gradient.fmt(f),
Self::Solid(color) => color.repr(),
Self::Gradient(gradient) => gradient.repr(),
}
}
}

View File

@ -12,7 +12,7 @@ use super::*;
/// ]
/// ```
#[ty]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Ratio(Scalar);
impl Ratio {
@ -62,9 +62,9 @@ impl Ratio {
}
}
impl Debug for Ratio {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}%", round_2(100.0 * self.get()))
impl Repr for Ratio {
fn repr(&self) -> EcoString {
format_float(self.get() * 100.0, Some(2), "%")
}
}

View File

@ -18,7 +18,7 @@ use super::*;
/// - `length`: Its length component.
/// - `ratio`: Its ratio component.
#[ty(name = "relative", title = "Relative Length")]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Rel<T: Numeric = Length> {
/// The relative part.
pub rel: Ratio,
@ -80,12 +80,12 @@ impl Rel<Length> {
}
}
impl<T: Numeric> Debug for Rel<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl<T: Numeric + Repr> Repr for Rel<T> {
fn repr(&self) -> EcoString {
match (self.rel.is_zero(), self.abs.is_zero()) {
(false, false) => write!(f, "{:?} + {:?}", self.rel, self.abs),
(false, true) => self.rel.fmt(f),
(true, _) => self.abs.fmt(f),
(false, false) => eco_format!("{} + {}", self.rel.repr(), self.abs.repr()),
(false, true) => self.rel.repr(),
(true, _) => self.abs.repr(),
}
}
}

View File

@ -3,7 +3,7 @@ use super::*;
/// A 64-bit float that implements `Eq`, `Ord` and `Hash`.
///
/// Panics if it's `NaN` during any of those operations.
#[derive(Default, Copy, Clone)]
#[derive(Debug, Default, Copy, Clone)]
pub struct Scalar(f64);
// We have to detect NaNs this way since `f64::is_nan` isnt const
@ -61,9 +61,9 @@ impl From<Scalar> for f64 {
}
}
impl Debug for Scalar {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(&self.0, f)
impl Repr for Scalar {
fn repr(&self) -> EcoString {
self.0.repr()
}
}

View File

@ -1,6 +1,5 @@
use crate::eval::{CastInfo, FromValue, IntoValue, Reflect};
use super::*;
use crate::eval::{CastInfo, FromValue, IntoValue, Reflect};
/// A container with left, top, right and bottom components.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]

View File

@ -1,6 +1,5 @@
use crate::eval::{AutoValue, CastInfo, FromValue, IntoValue, Reflect};
use super::*;
use crate::eval::{AutoValue, CastInfo, FromValue, IntoValue, Reflect};
/// A value that can be automatically determined.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]

View File

@ -1,6 +1,5 @@
use crate::eval::{dict, Cast, FromValue, NoneValue};
use super::*;
use crate::eval::{dict, Cast, FromValue, NoneValue};
/// Defines how to draw a line.
///
@ -67,7 +66,7 @@ use super::*;
/// dictionary format above. For example, `{(2pt + blue).thickness}` is `{2pt}`.
/// Meanwhile, `{(2pt + blue).cap}` is `{auto}` because it's unspecified.
#[ty]
#[derive(Default, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct Stroke<T: Numeric = Length> {
/// The stroke's paint.
pub paint: Smart<Paint>,
@ -146,8 +145,9 @@ impl Stroke<Abs> {
}
}
impl<T: Numeric + Debug> Debug for Stroke<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl<T: Numeric + Repr> Repr for Stroke<T> {
fn repr(&self) -> EcoString {
let mut r = EcoString::new();
let Self {
paint,
thickness,
@ -163,46 +163,59 @@ impl<T: Numeric + Debug> Debug for Stroke<T> {
{
match (&self.paint, &self.thickness) {
(Smart::Custom(paint), Smart::Custom(thickness)) => {
write!(f, "{thickness:?} + {paint:?}")
r.push_str(&thickness.repr());
r.push_str(" + ");
r.push_str(&paint.repr());
}
(Smart::Custom(paint), Smart::Auto) => paint.fmt(f),
(Smart::Auto, Smart::Custom(thickness)) => thickness.fmt(f),
(Smart::Auto, Smart::Auto) => f.pad("1pt + black"),
(Smart::Custom(paint), Smart::Auto) => r.push_str(&paint.repr()),
(Smart::Auto, Smart::Custom(thickness)) => r.push_str(&thickness.repr()),
(Smart::Auto, Smart::Auto) => r.push_str("1pt + black"),
}
} else {
write!(f, "(")?;
r.push('(');
let mut sep = "";
if let Smart::Custom(paint) = &paint {
write!(f, "{}paint: {:?}", sep, paint)?;
r.push_str(sep);
r.push_str("paint: ");
r.push_str(&paint.repr());
sep = ", ";
}
if let Smart::Custom(thickness) = &thickness {
write!(f, "{}thickness: {:?}", sep, thickness)?;
r.push_str(sep);
r.push_str("thickness: ");
r.push_str(&thickness.repr());
sep = ", ";
}
if let Smart::Custom(cap) = &line_cap {
write!(f, "{}cap: {:?}", sep, cap)?;
r.push_str(sep);
r.push_str("cap: ");
r.push_str(&cap.repr());
sep = ", ";
}
if let Smart::Custom(join) = &line_join {
write!(f, "{}join: {:?}", sep, join)?;
r.push_str(sep);
r.push_str("join: ");
r.push_str(&join.repr());
sep = ", ";
}
if let Smart::Custom(dash) = &dash_pattern {
write!(f, "{}dash: ", sep)?;
r.push_str(sep);
r.push_str("cap: ");
if let Some(dash) = dash {
Debug::fmt(dash, f)?;
r.push_str(&dash.repr());
} else {
Debug::fmt(&NoneValue, f)?;
r.push_str(&NoneValue.repr());
}
sep = ", ";
}
if let Smart::Custom(miter_limit) = &miter_limit {
write!(f, "{}miter-limit: {:?}", sep, miter_limit)?;
r.push_str(sep);
r.push_str("miter-limit: ");
r.push_str(&miter_limit.repr());
}
write!(f, ")")?;
Ok(())
r.push(')');
}
r
}
}
@ -277,43 +290,43 @@ cast! {
}
/// The line cap of a stroke
#[derive(Copy, Clone, Eq, PartialEq, Hash, Cast)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum LineCap {
Butt,
Round,
Square,
}
impl Debug for LineCap {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for LineCap {
fn repr(&self) -> EcoString {
match self {
LineCap::Butt => write!(f, "\"butt\""),
LineCap::Round => write!(f, "\"round\""),
LineCap::Square => write!(f, "\"square\""),
Self::Butt => "butt".repr(),
Self::Round => "round".repr(),
Self::Square => "square".repr(),
}
}
}
/// The line join of a stroke
#[derive(Copy, Clone, Eq, PartialEq, Hash, Cast)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum LineJoin {
Miter,
Round,
Bevel,
}
impl Debug for LineJoin {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for LineJoin {
fn repr(&self) -> EcoString {
match self {
LineJoin::Miter => write!(f, "\"miter\""),
LineJoin::Round => write!(f, "\"round\""),
LineJoin::Bevel => write!(f, "\"bevel\""),
Self::Miter => "miter".repr(),
Self::Round => "round".repr(),
Self::Bevel => "bevel".repr(),
}
}
}
/// A line dash pattern.
#[derive(Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct DashPattern<T: Numeric = Length, DT = DashLength<T>> {
/// The dash array.
pub array: Vec<DT>,
@ -321,18 +334,19 @@ pub struct DashPattern<T: Numeric = Length, DT = DashLength<T>> {
pub phase: T,
}
impl<T: Numeric + Debug, DT: Debug> Debug for DashPattern<T, DT> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "(array: (")?;
impl<T: Numeric + Repr, DT: Repr> Repr for DashPattern<T, DT> {
fn repr(&self) -> EcoString {
let mut r = EcoString::from("(array: (");
for (i, elem) in self.array.iter().enumerate() {
if i == 0 {
write!(f, "{:?}", elem)?;
} else {
write!(f, ", {:?}", elem)?;
if i != 0 {
r.push_str(", ")
}
r.push_str(&elem.repr())
}
write!(f, "), phase: {:?})", self.phase)?;
Ok(())
r.push_str("), phase: ");
r.push_str(&self.phase.repr());
r.push(')');
r
}
}
@ -384,7 +398,7 @@ cast! {
}
/// The length of a dash in a line dash pattern.
#[derive(Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum DashLength<T: Numeric = Length> {
LineWidth,
Length(T),
@ -399,11 +413,11 @@ impl<T: Numeric> DashLength<T> {
}
}
impl<T: Numeric + Debug> Debug for DashLength<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
impl<T: Numeric + Repr> Repr for DashLength<T> {
fn repr(&self) -> EcoString {
match self {
Self::LineWidth => write!(f, "\"dot\""),
Self::Length(v) => Debug::fmt(v, f),
Self::LineWidth => "dot".repr(),
Self::Length(v) => v.repr(),
}
}
}

View File

@ -1,5 +1,5 @@
use std::any::TypeId;
use std::fmt::{self, Debug, Formatter, Write};
use std::fmt::Debug;
use std::iter::{self, Sum};
use std::ops::{Add, AddAssign};
@ -13,7 +13,7 @@ use super::{
};
use crate::diag::{SourceResult, StrResult};
use crate::doc::Meta;
use crate::eval::{func, scope, ty, Dict, FromValue, IntoValue, Str, Value, Vm};
use crate::eval::{func, scope, ty, Dict, FromValue, IntoValue, Repr, Str, Value, Vm};
use crate::syntax::Span;
use crate::util::pretty_array_like;
@ -61,7 +61,7 @@ use crate::util::pretty_array_like;
/// elements the content is composed of and what fields they have.
/// Alternatively, you can inspect the output of the [`repr`]($repr) function.
#[ty(scope)]
#[derive(Clone, Hash)]
#[derive(Debug, Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Content {
elem: Element,
@ -528,30 +528,26 @@ impl Content {
}
}
impl Debug for Content {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for Content {
fn repr(&self) -> EcoString {
let name = self.elem.name();
if let Some(text) = item!(text_str)(self) {
f.write_char('[')?;
f.write_str(&text)?;
f.write_char(']')?;
return Ok(());
return eco_format!("[{}]", text);
} else if name == "space" {
return f.write_str("[ ]");
return ("[ ]").into();
}
let mut pieces: Vec<_> = self
.fields()
.into_iter()
.map(|(name, value)| eco_format!("{name}: {value:?}"))
.map(|(name, value)| eco_format!("{}: {}", name, value.repr()))
.collect();
if self.is::<StyledElem>() {
pieces.push(EcoString::from(".."));
}
f.write_str(name)?;
f.write_str(&pretty_array_like(&pieces, false))
eco_format!("{}{}", name, pretty_array_like(&pieces, false))
}
}
@ -696,15 +692,15 @@ pub trait PlainText {
/// The missing field access error message.
#[cold]
fn missing_field(field: &str) -> EcoString {
eco_format!("content does not contain field {:?}", Str::from(field))
eco_format!("content does not contain field {}", field.repr())
}
/// The missing field access error message when no default value was given.
#[cold]
fn missing_field_no_default(field: &str) -> EcoString {
eco_format!(
"content does not contain field {:?} and \
"content does not contain field {} and \
no default value was specified",
Str::from(field)
field.repr()
)
}

View File

@ -1,16 +1,17 @@
use ecow::EcoString;
use std::any::TypeId;
use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter};
use std::fmt::Debug;
use once_cell::sync::Lazy;
use super::{Content, Selector, Styles};
use crate::diag::SourceResult;
use crate::eval::{cast, Args, Dict, Func, ParamInfo, Scope, Value, Vm};
use crate::eval::{cast, Args, Dict, Func, ParamInfo, Repr, Scope, Value, Vm};
use crate::util::Static;
/// A document element.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Element(Static<NativeElementData>);
impl Element {
@ -93,9 +94,9 @@ impl Element {
}
}
impl Debug for Element {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.name())
impl Repr for Element {
fn repr(&self) -> EcoString {
self.name().into()
}
}
@ -150,6 +151,7 @@ pub trait Set {
}
/// Defines a native element.
#[derive(Debug)]
pub struct NativeElementData {
pub name: &'static str,
pub title: &'static str,

View File

@ -1,17 +1,17 @@
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap};
use std::fmt::{self, Debug, Formatter};
use std::fmt::Debug;
use std::hash::Hash;
use std::num::NonZeroUsize;
use comemo::{Prehashed, Track, Tracked, Validate};
use ecow::EcoVec;
use ecow::{EcoString, EcoVec};
use indexmap::IndexMap;
use super::{Content, Selector};
use crate::diag::{bail, StrResult};
use crate::doc::{Frame, FrameItem, Meta, Position};
use crate::eval::{cast, func, scope, ty, Dict, Value, Vm};
use crate::eval::{cast, func, scope, ty, Dict, Repr, Value, Vm};
use crate::geom::{Point, Transform};
use crate::model::Label;
use crate::util::NonZeroExt;
@ -24,7 +24,7 @@ use crate::util::NonZeroExt;
/// or shown element with the [`location()`]($content.location) method on
/// content.
#[ty(scope)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Location {
/// The hash of the element.
hash: u128,
@ -83,9 +83,9 @@ impl Location {
}
}
impl Debug for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("..")
impl Repr for Location {
fn repr(&self) -> EcoString {
"..".into()
}
}

View File

@ -1,8 +1,8 @@
use std::fmt::{self, Debug, Formatter};
use std::fmt::Debug;
use ecow::EcoString;
use ecow::{eco_format, EcoString};
use crate::eval::{func, scope, ty};
use crate::eval::{func, scope, ty, Repr};
/// A label for an element.
///
@ -23,7 +23,7 @@ use crate::eval::{func, scope, ty};
/// This function also has dedicated syntax: You can create a label by enclosing
/// its name in angle brackets. This works both in markup and code.
#[ty(scope)]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Label(pub EcoString);
#[scope]
@ -38,9 +38,9 @@ impl Label {
}
}
impl Debug for Label {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "<{}>", self.0)
impl Repr for Label {
fn repr(&self) -> EcoString {
eco_format!("<{}>", self.0)
}
}

View File

@ -1,5 +1,5 @@
use std::any::{Any, TypeId};
use std::fmt::{self, Debug, Formatter, Write};
use std::fmt::Debug;
use std::sync::Arc;
use ecow::{eco_format, EcoString, EcoVec};
@ -7,8 +7,8 @@ use ecow::{eco_format, EcoString, EcoVec};
use super::{Content, Element, Label, Locatable, Location};
use crate::diag::{bail, StrResult};
use crate::eval::{
cast, func, scope, ty, CastInfo, Dict, FromValue, Func, Reflect, Regex, Str, Symbol,
Type, Value,
cast, func, scope, ty, CastInfo, Dict, FromValue, Func, Reflect, Regex, Repr, Str,
Symbol, Type, Value,
};
use crate::util::pretty_array_like;
@ -49,7 +49,7 @@ use crate::util::pretty_array_like;
/// === But this will not.
/// ```
#[ty(scope)]
#[derive(Clone, PartialEq, Hash)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum Selector {
/// Matches a specific type of element.
///
@ -209,42 +209,37 @@ impl From<Location> for Selector {
}
}
impl Debug for Selector {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
impl Repr for Selector {
fn repr(&self) -> EcoString {
match self {
Self::Elem(elem, dict) => {
f.write_str(elem.name())?;
if let Some(dict) = dict {
f.write_str(".where")?;
dict.fmt(f)?;
eco_format!("{}.where{}", elem.name(), dict.repr())
} else {
elem.name().into()
}
Ok(())
}
Self::Label(label) => label.fmt(f),
Self::Regex(regex) => regex.fmt(f),
Self::Can(cap) => cap.fmt(f),
Self::Label(label) => label.repr(),
Self::Regex(regex) => regex.repr(),
Self::Can(cap) => eco_format!("{cap:?}"),
Self::Or(selectors) | Self::And(selectors) => {
f.write_str(if matches!(self, Self::Or(_)) { "or" } else { "and" })?;
let pieces: Vec<_> =
selectors.iter().map(|sel| eco_format!("{sel:?}")).collect();
f.write_str(&pretty_array_like(&pieces, false))
let function = if matches!(self, Self::Or(_)) { "or" } else { "and" };
let pieces: Vec<_> = selectors.iter().map(Selector::repr).collect();
eco_format!("{}{}", function, pretty_array_like(&pieces, false))
}
Self::Location(loc) => loc.fmt(f),
Self::Location(loc) => loc.repr(),
Self::Before { selector, end: split, inclusive }
| Self::After { selector, start: split, inclusive } => {
selector.fmt(f)?;
if matches!(self, Self::Before { .. }) {
f.write_str(".before(")?;
} else {
f.write_str(".after(")?;
}
split.fmt(f)?;
if !*inclusive {
f.write_str(", inclusive: false")?;
}
f.write_char(')')
let method =
if matches!(self, Self::Before { .. }) { "before" } else { "after" };
let inclusive_arg = if !*inclusive { ", inclusive: false" } else { "" };
eco_format!(
"{}.{}({}{})",
selector.repr(),
method,
split.repr(),
inclusive_arg
)
}
}
}

View File

@ -8,12 +8,12 @@ use ecow::{eco_vec, EcoString, EcoVec};
use super::{Content, Element, NativeElement, Selector, Vt};
use crate::diag::{SourceResult, Trace, Tracepoint};
use crate::eval::{cast, ty, Args, FromValue, Func, IntoValue, Value, Vm};
use crate::eval::{cast, ty, Args, FromValue, Func, IntoValue, Repr, Value, Vm};
use crate::syntax::Span;
/// A list of style properties.
#[ty]
#[derive(Default, PartialEq, Clone, Hash)]
#[derive(Debug, Default, PartialEq, Clone, Hash)]
pub struct Styles(EcoVec<Prehashed<Style>>);
impl Styles {
@ -86,9 +86,9 @@ impl From<Style> for Styles {
}
}
impl Debug for Styles {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("..")
impl Repr for Styles {
fn repr(&self) -> EcoString {
"..".into()
}
}

View File

@ -1,3 +1,54 @@
use ecow::{eco_format, EcoString};
/// Format an integer in a base.
pub fn format_int_with_base(mut n: i64, base: i64) -> EcoString {
if n == 0 {
return "0".into();
}
// In Rust, `format!("{:x}", -14i64)` is not `-e` but `fffffffffffffff2`.
// So we can only use the built-in for decimal, not bin/oct/hex.
if base == 10 {
return eco_format!("{n}");
}
// The largest output is `to_base(i64::MIN, 2)`, which is 65 chars long.
const SIZE: usize = 65;
let mut digits = [b'\0'; SIZE];
let mut i = SIZE;
// It's tempting to take the absolute value, but this will fail for i64::MIN.
// Instead, we turn n negative, as -i64::MAX is perfectly representable.
let negative = n < 0;
if n > 0 {
n = -n;
}
while n != 0 {
let digit = char::from_digit(-(n % base) as u32, base as u32);
i -= 1;
digits[i] = digit.unwrap_or('?') as u8;
n /= base;
}
if negative {
i -= 1;
digits[i] = b'-';
}
std::str::from_utf8(&digits[i..]).unwrap_or_default().into()
}
/// Converts a float to a string representation with a specific precision and a
/// suffix, all with a single allocation.
pub fn format_float(mut value: f64, precision: Option<u8>, suffix: &str) -> EcoString {
if let Some(p) = precision {
let offset = 10_f64.powi(p as i32);
value = (value * offset).round() / offset;
}
eco_format!("{}{}", value, suffix)
}
/// Format pieces separated with commas and a final "and" or "or".
pub fn separated_list(pieces: &[impl AsRef<str>], last: &str) -> String {
let mut buf = String::new();

View File

@ -1,7 +1,7 @@
//! Utilities.
pub mod fat;
mod fmt;
pub mod fmt;
pub use self::fmt::{pretty_array_like, pretty_comma_list, separated_list};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -22,7 +22,9 @@ use walkdir::WalkDir;
use typst::diag::{bail, FileError, FileResult, Severity, StrResult};
use typst::doc::{Document, Frame, FrameItem, Meta};
use typst::eval::{eco_format, func, Bytes, Datetime, Library, NoneValue, Tracer, Value};
use typst::eval::{
eco_format, func, Bytes, Datetime, Library, NoneValue, Repr, Tracer, Value,
};
use typst::font::{Font, FontBook};
use typst::geom::{Abs, Color, Smart};
use typst::syntax::{FileId, PackageVersion, Source, SyntaxNode, VirtualPath};
@ -154,7 +156,7 @@ fn library() -> Library {
#[func]
fn test(lhs: Value, rhs: Value) -> StrResult<NoneValue> {
if lhs != rhs {
bail!("Assertion failed: {lhs:?} != {rhs:?}");
bail!("Assertion failed: {} != {}", lhs.repr(), rhs.repr());
}
Ok(NoneValue)
}
@ -162,7 +164,7 @@ fn library() -> Library {
#[func]
fn test_repr(lhs: Value, rhs: Value) -> StrResult<NoneValue> {
if lhs.repr() != rhs.repr() {
bail!("Assertion failed: {lhs:?} != {rhs:?}");
bail!("Assertion failed: {} != {}", lhs.repr(), rhs.repr());
}
Ok(NoneValue)
}