mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Move all pretty printing into one module and pretty print values 🦋
This commit is contained in:
parent
f9197dcfef
commit
1711b67877
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Errors are never fatal, the document will always compile and yield a layout.
|
//! Errors are never fatal, the document will always compile and yield a layout.
|
||||||
|
|
||||||
use crate::syntax::SpanVec;
|
use crate::syntax::Spanned;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
/// The result of some pass: Some output `T` and [`Feedback`] data.
|
/// The result of some pass: Some output `T` and [`Feedback`] data.
|
||||||
@ -25,9 +25,9 @@ impl<T> Pass<T> {
|
|||||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||||
pub struct Feedback {
|
pub struct Feedback {
|
||||||
/// Diagnostics about the source code.
|
/// Diagnostics about the source code.
|
||||||
pub diags: SpanVec<Diag>,
|
pub diags: Vec<Spanned<Diag>>,
|
||||||
/// Decorations of the source code for semantic syntax highlighting.
|
/// Decorations of the source code for semantic syntax highlighting.
|
||||||
pub decos: SpanVec<Deco>,
|
pub decos: Vec<Spanned<Deco>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Feedback {
|
impl Feedback {
|
||||||
|
11
src/env.rs
11
src/env.rs
@ -7,6 +7,7 @@ use std::fs;
|
|||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use fontdock::fs::FsSource;
|
||||||
use image::io::Reader as ImageReader;
|
use image::io::Reader as ImageReader;
|
||||||
use image::{DynamicImage, GenericImageView, ImageFormat};
|
use image::{DynamicImage, GenericImageView, ImageFormat};
|
||||||
|
|
||||||
@ -21,6 +22,16 @@ pub struct Env {
|
|||||||
pub resources: ResourceLoader,
|
pub resources: ResourceLoader,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Env {
|
||||||
|
/// Create an empty environment for testing purposes.
|
||||||
|
pub fn blank() -> Self {
|
||||||
|
Self {
|
||||||
|
fonts: FontLoader::new(Box::new(FsSource::new(vec![])), vec![]),
|
||||||
|
resources: ResourceLoader::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads resource from the file system.
|
/// Loads resource from the file system.
|
||||||
pub struct ResourceLoader {
|
pub struct ResourceLoader {
|
||||||
paths: HashMap<PathBuf, ResourceId>,
|
paths: HashMap<PathBuf, ResourceId>,
|
||||||
|
@ -377,11 +377,11 @@ impl Eval for ExprArgs {
|
|||||||
.map(|arg| match arg {
|
.map(|arg| match arg {
|
||||||
ExprArg::Pos(expr) => ValueArg {
|
ExprArg::Pos(expr) => ValueArg {
|
||||||
name: None,
|
name: None,
|
||||||
value: expr.eval(ctx).with_span(expr.span()),
|
value: Spanned::new(expr.eval(ctx), expr.span()),
|
||||||
},
|
},
|
||||||
ExprArg::Named(Named { name, expr }) => ValueArg {
|
ExprArg::Named(Named { name, expr }) => ValueArg {
|
||||||
name: Some(name.string.clone().with_span(name.span)),
|
name: Some(Spanned::new(name.string.clone(), name.span)),
|
||||||
value: expr.eval(ctx).with_span(expr.span()),
|
value: Spanned::new(expr.eval(ctx), expr.span()),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -97,44 +97,9 @@ impl Default for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for Value {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
match self {
|
|
||||||
Value::None => p.push_str("none"),
|
|
||||||
Value::Bool(v) => v.pretty(p),
|
|
||||||
Value::Int(v) => v.pretty(p),
|
|
||||||
Value::Float(v) => v.pretty(p),
|
|
||||||
Value::Length(v) => v.pretty(p),
|
|
||||||
Value::Angle(v) => v.pretty(p),
|
|
||||||
Value::Relative(v) => v.pretty(p),
|
|
||||||
Value::Linear(v) => v.pretty(p),
|
|
||||||
Value::Color(v) => v.pretty(p),
|
|
||||||
Value::Str(v) => v.pretty(p),
|
|
||||||
Value::Array(v) => v.pretty(p),
|
|
||||||
Value::Dict(v) => v.pretty(p),
|
|
||||||
Value::Template(v) => v.pretty(p),
|
|
||||||
Value::Func(v) => v.pretty(p),
|
|
||||||
Value::Args(v) => v.pretty(p),
|
|
||||||
Value::Any(v) => v.pretty(p),
|
|
||||||
Value::Error => p.push_str("<error>"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An array value: `(1, "hi", 12cm)`.
|
/// An array value: `(1, "hi", 12cm)`.
|
||||||
pub type ValueArray = Vec<Value>;
|
pub type ValueArray = Vec<Value>;
|
||||||
|
|
||||||
impl Pretty for ValueArray {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('(');
|
|
||||||
p.join(self, ", ", |item, p| item.pretty(p));
|
|
||||||
if self.len() == 1 {
|
|
||||||
p.push(',');
|
|
||||||
}
|
|
||||||
p.push(')');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A dictionary value: `(color: #f79143, pattern: dashed)`.
|
/// A dictionary value: `(color: #f79143, pattern: dashed)`.
|
||||||
pub type ValueDict = BTreeMap<String, Value>;
|
pub type ValueDict = BTreeMap<String, Value>;
|
||||||
|
|
||||||
@ -157,16 +122,6 @@ impl Pretty for ValueDict {
|
|||||||
/// A template value: `[*Hi* there]`.
|
/// A template value: `[*Hi* there]`.
|
||||||
pub type ValueTemplate = Vec<TemplateNode>;
|
pub type ValueTemplate = Vec<TemplateNode>;
|
||||||
|
|
||||||
impl Pretty for ValueTemplate {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('[');
|
|
||||||
for part in self {
|
|
||||||
part.pretty(p);
|
|
||||||
}
|
|
||||||
p.push(']');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// One chunk of a template.
|
/// One chunk of a template.
|
||||||
///
|
///
|
||||||
/// Evaluating a template expression creates only a single chunk. Adding two
|
/// Evaluating a template expression creates only a single chunk. Adding two
|
||||||
@ -185,16 +140,6 @@ pub enum TemplateNode {
|
|||||||
Any(TemplateAny),
|
Any(TemplateAny),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for TemplateNode {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
match self {
|
|
||||||
// TODO: Pretty-print the values.
|
|
||||||
Self::Tree { tree, .. } => tree.pretty(p),
|
|
||||||
Self::Any(any) => any.pretty(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A reference-counted dynamic template node (can implement custom behaviour).
|
/// A reference-counted dynamic template node (can implement custom behaviour).
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TemplateAny {
|
pub struct TemplateAny {
|
||||||
@ -210,6 +155,12 @@ impl TemplateAny {
|
|||||||
{
|
{
|
||||||
Self { name: name.into(), f: Rc::new(f) }
|
Self { name: name.into(), f: Rc::new(f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The name of the template node.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for TemplateAny {
|
impl PartialEq for TemplateAny {
|
||||||
@ -227,14 +178,6 @@ impl Deref for TemplateAny {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for TemplateAny {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('<');
|
|
||||||
p.push_str(&self.name);
|
|
||||||
p.push('>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for TemplateAny {
|
impl Debug for TemplateAny {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
f.debug_struct("TemplateAny").finish()
|
f.debug_struct("TemplateAny").finish()
|
||||||
@ -256,6 +199,11 @@ impl ValueFunc {
|
|||||||
{
|
{
|
||||||
Self { name: name.into(), f: Rc::new(f) }
|
Self { name: name.into(), f: Rc::new(f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The name of the function.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for ValueFunc {
|
impl PartialEq for ValueFunc {
|
||||||
@ -273,14 +221,6 @@ impl Deref for ValueFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ValueFunc {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('<');
|
|
||||||
p.push_str(&self.name);
|
|
||||||
p.push('>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for ValueFunc {
|
impl Debug for ValueFunc {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
f.debug_struct("ValueFunc").field("name", &self.name).finish()
|
f.debug_struct("ValueFunc").field("name", &self.name).finish()
|
||||||
@ -417,14 +357,6 @@ impl ValueArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ValueArgs {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('<');
|
|
||||||
p.join(&self.items, ", ", |item, p| item.pretty(p));
|
|
||||||
p.push('>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a workaround because `-> impl Trait + 'a + 'b` does not work.
|
// This is a workaround because `-> impl Trait + 'a + 'b` does not work.
|
||||||
//
|
//
|
||||||
// See also: https://github.com/rust-lang/rust/issues/49431
|
// See also: https://github.com/rust-lang/rust/issues/49431
|
||||||
@ -451,16 +383,6 @@ impl ValueArg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ValueArg {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
if let Some(name) = &self.name {
|
|
||||||
p.push_str(&name.v);
|
|
||||||
p.push_str(": ");
|
|
||||||
}
|
|
||||||
self.value.v.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper around a dynamic value.
|
/// A wrapper around a dynamic value.
|
||||||
pub struct ValueAny(Box<dyn Bounds>);
|
pub struct ValueAny(Box<dyn Bounds>);
|
||||||
|
|
||||||
@ -510,18 +432,18 @@ impl PartialEq for ValueAny {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ValueAny {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
write!(p, "{}", self.0).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for ValueAny {
|
impl Debug for ValueAny {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
f.debug_tuple("ValueAny").field(&self.0).finish()
|
f.debug_tuple("ValueAny").field(&self.0).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for ValueAny {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trait Bounds: Debug + Display + 'static {
|
trait Bounds: Debug + Display + 'static {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn into_any(self: Box<Self>) -> Box<dyn Any>;
|
fn into_any(self: Box<Self>) -> Box<dyn Any>;
|
||||||
@ -618,7 +540,7 @@ where
|
|||||||
match T::cast(value.v) {
|
match T::cast(value.v) {
|
||||||
CastResult::Ok(t) => CastResult::Ok(t),
|
CastResult::Ok(t) => CastResult::Ok(t),
|
||||||
CastResult::Warn(t, m) => CastResult::Warn(t, m),
|
CastResult::Warn(t, m) => CastResult::Warn(t, m),
|
||||||
CastResult::Err(v) => CastResult::Err(v.with_span(span)),
|
CastResult::Err(v) => CastResult::Err(Spanned::new(v, span)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,9 +552,9 @@ where
|
|||||||
fn cast(value: Spanned<Value>) -> CastResult<Self, Spanned<Value>> {
|
fn cast(value: Spanned<Value>) -> CastResult<Self, Spanned<Value>> {
|
||||||
let span = value.span;
|
let span = value.span;
|
||||||
match T::cast(value.v) {
|
match T::cast(value.v) {
|
||||||
CastResult::Ok(t) => CastResult::Ok(t.with_span(span)),
|
CastResult::Ok(t) => CastResult::Ok(Spanned::new(t, span)),
|
||||||
CastResult::Warn(t, m) => CastResult::Warn(t.with_span(span), m),
|
CastResult::Warn(t, m) => CastResult::Warn(Spanned::new(t, span), m),
|
||||||
CastResult::Err(v) => CastResult::Err(v.with_span(span)),
|
CastResult::Err(v) => CastResult::Err(Spanned::new(v, span)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ pub fn exec(
|
|||||||
) -> Pass<layout::Tree> {
|
) -> Pass<layout::Tree> {
|
||||||
let mut ctx = ExecContext::new(env, state);
|
let mut ctx = ExecContext::new(env, state);
|
||||||
ctx.start_page_group(Softness::Hard);
|
ctx.start_page_group(Softness::Hard);
|
||||||
tree.exec_with(&mut ctx, &map);
|
tree.exec_with_map(&mut ctx, &map);
|
||||||
ctx.end_page_group(|s| s == Softness::Hard);
|
ctx.end_page_group(|s| s == Softness::Hard);
|
||||||
ctx.finish()
|
ctx.finish()
|
||||||
}
|
}
|
||||||
@ -51,21 +51,21 @@ pub trait Exec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a node with an expression map that applies to it.
|
/// Execute a node with an expression map that applies to it.
|
||||||
pub trait ExecWith {
|
pub trait ExecWithMap {
|
||||||
/// Execute the node.
|
/// Execute the node.
|
||||||
fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap);
|
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecWith for Tree {
|
impl ExecWithMap for Tree {
|
||||||
fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||||
for node in self {
|
for node in self {
|
||||||
node.exec_with(ctx, map);
|
node.exec_with_map(ctx, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecWith for Node {
|
impl ExecWithMap for Node {
|
||||||
fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||||
match self {
|
match self {
|
||||||
Node::Text(text) => ctx.push_text(text),
|
Node::Text(text) => ctx.push_text(text),
|
||||||
Node::Space => ctx.push_space(),
|
Node::Space => ctx.push_space(),
|
||||||
@ -73,21 +73,21 @@ impl ExecWith for Node {
|
|||||||
Node::Parbreak => ctx.apply_parbreak(),
|
Node::Parbreak => ctx.apply_parbreak(),
|
||||||
Node::Strong => ctx.state.font.strong ^= true,
|
Node::Strong => ctx.state.font.strong ^= true,
|
||||||
Node::Emph => ctx.state.font.emph ^= true,
|
Node::Emph => ctx.state.font.emph ^= true,
|
||||||
Node::Heading(heading) => heading.exec_with(ctx, map),
|
Node::Heading(heading) => heading.exec_with_map(ctx, map),
|
||||||
Node::Raw(raw) => raw.exec(ctx),
|
Node::Raw(raw) => raw.exec(ctx),
|
||||||
Node::Expr(expr) => map[&(expr as *const _)].exec(ctx),
|
Node::Expr(expr) => map[&(expr as *const _)].exec(ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecWith for NodeHeading {
|
impl ExecWithMap for NodeHeading {
|
||||||
fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||||
let prev = ctx.state.clone();
|
let prev = ctx.state.clone();
|
||||||
let upscale = 1.5 - 0.1 * self.level as f64;
|
let upscale = 1.5 - 0.1 * self.level as f64;
|
||||||
ctx.state.font.scale *= upscale;
|
ctx.state.font.scale *= upscale;
|
||||||
ctx.state.font.strong = true;
|
ctx.state.font.strong = true;
|
||||||
|
|
||||||
self.contents.exec_with(ctx, map);
|
self.contents.exec_with_map(ctx, map);
|
||||||
ctx.apply_parbreak();
|
ctx.apply_parbreak();
|
||||||
|
|
||||||
ctx.state = prev;
|
ctx.state = prev;
|
||||||
@ -154,7 +154,7 @@ impl Exec for ValueTemplate {
|
|||||||
impl Exec for TemplateNode {
|
impl Exec for TemplateNode {
|
||||||
fn exec(&self, ctx: &mut ExecContext) {
|
fn exec(&self, ctx: &mut ExecContext) {
|
||||||
match self {
|
match self {
|
||||||
Self::Tree { tree, map } => tree.exec_with(ctx, &map),
|
Self::Tree { tree, map } => tree.exec_with_map(ctx, &map),
|
||||||
Self::Any(any) => any.exec(ctx),
|
Self::Any(any) => any.exec(ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::Scanner;
|
use super::Scanner;
|
||||||
use crate::syntax::{Location, Offset, Pos};
|
use crate::syntax::{Location, Pos};
|
||||||
|
|
||||||
/// Enables conversion of byte position to locations.
|
/// Enables conversion of byte position to locations.
|
||||||
pub struct LineMap<'s> {
|
pub struct LineMap<'s> {
|
||||||
@ -44,7 +44,7 @@ impl<'s> LineMap<'s> {
|
|||||||
pub fn pos(&self, location: Location) -> Option<Pos> {
|
pub fn pos(&self, location: Location) -> Option<Pos> {
|
||||||
// Determine the boundaries of the line.
|
// Determine the boundaries of the line.
|
||||||
let line_idx = location.line.checked_sub(1)? as usize;
|
let line_idx = location.line.checked_sub(1)? as usize;
|
||||||
let line_start = self.line_starts.get(line_idx)?;
|
let line_start = *self.line_starts.get(line_idx)?;
|
||||||
let line_end = self
|
let line_end = self
|
||||||
.line_starts
|
.line_starts
|
||||||
.get(location.line as usize)
|
.get(location.line as usize)
|
||||||
@ -64,7 +64,7 @@ impl<'s> LineMap<'s> {
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(line_start.offset(Pos(line_offset as u32)))
|
Some(line_start + line_offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{is_newline, Scanner};
|
use super::{is_newline, Scanner};
|
||||||
use crate::syntax::{Ident, NodeRaw, Offset, Pos};
|
use crate::syntax::{Ident, NodeRaw, Pos};
|
||||||
|
|
||||||
/// Resolve all escape sequences in a string.
|
/// Resolve all escape sequences in a string.
|
||||||
pub fn resolve_string(string: &str) -> String {
|
pub fn resolve_string(string: &str) -> String {
|
||||||
@ -52,7 +52,7 @@ pub fn resolve_raw(text: &str, backticks: usize, start: Pos) -> NodeRaw {
|
|||||||
let (tag, inner) = split_at_lang_tag(text);
|
let (tag, inner) = split_at_lang_tag(text);
|
||||||
let (lines, had_newline) = trim_and_split_raw(inner);
|
let (lines, had_newline) = trim_and_split_raw(inner);
|
||||||
NodeRaw {
|
NodeRaw {
|
||||||
lang: Ident::new(tag, start .. start.offset(tag.len())),
|
lang: Ident::new(tag, start .. start + tag.len()),
|
||||||
lines,
|
lines,
|
||||||
block: had_newline,
|
block: had_newline,
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,5 @@ pub use crate::geom::*;
|
|||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::layout::Node;
|
pub use crate::layout::Node;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::syntax::{Span, Spanned, WithSpan};
|
pub use crate::syntax::{Span, Spanned};
|
||||||
pub use crate::{error, typify, warning};
|
pub use crate::{error, typify, warning};
|
||||||
|
609
src/pretty.rs
609
src/pretty.rs
@ -3,7 +3,9 @@
|
|||||||
use std::fmt::{Arguments, Result, Write};
|
use std::fmt::{Arguments, Result, Write};
|
||||||
|
|
||||||
use crate::color::{Color, RgbaColor};
|
use crate::color::{Color, RgbaColor};
|
||||||
|
use crate::eval::*;
|
||||||
use crate::geom::{Angle, Length, Linear, Relative};
|
use crate::geom::{Angle, Length, Linear, Relative};
|
||||||
|
use crate::syntax::*;
|
||||||
|
|
||||||
/// Pretty print an item and return the resulting string.
|
/// Pretty print an item and return the resulting string.
|
||||||
pub fn pretty<T>(item: &T) -> String
|
pub fn pretty<T>(item: &T) -> String
|
||||||
@ -15,12 +17,37 @@ where
|
|||||||
p.finish()
|
p.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pretty printing.
|
/// Pretty print an item with an expression map and return the resulting string.
|
||||||
|
pub fn pretty_with_map<T>(item: &T, map: &ExprMap) -> String
|
||||||
|
where
|
||||||
|
T: PrettyWithMap + ?Sized,
|
||||||
|
{
|
||||||
|
let mut p = Printer::new();
|
||||||
|
item.pretty_with_map(&mut p, Some(map));
|
||||||
|
p.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty print an item.
|
||||||
pub trait Pretty {
|
pub trait Pretty {
|
||||||
/// Pretty print this item into the given printer.
|
/// Pretty print this item into the given printer.
|
||||||
fn pretty(&self, p: &mut Printer);
|
fn pretty(&self, p: &mut Printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pretty print an item with an expression map that applies to it.
|
||||||
|
pub trait PrettyWithMap {
|
||||||
|
/// Pretty print this item into the given printer.
|
||||||
|
fn pretty_with_map(&self, p: &mut Printer, map: Option<&ExprMap>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pretty for T
|
||||||
|
where
|
||||||
|
T: PrettyWithMap,
|
||||||
|
{
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
self.pretty_with_map(p, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A buffer into which items are printed.
|
/// A buffer into which items are printed.
|
||||||
pub struct Printer {
|
pub struct Printer {
|
||||||
buf: String,
|
buf: String,
|
||||||
@ -76,6 +103,463 @@ impl Write for Printer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PrettyWithMap for Tree {
|
||||||
|
fn pretty_with_map(&self, p: &mut Printer, map: Option<&ExprMap>) {
|
||||||
|
for node in self {
|
||||||
|
node.pretty_with_map(p, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyWithMap for Node {
|
||||||
|
fn pretty_with_map(&self, p: &mut Printer, map: Option<&ExprMap>) {
|
||||||
|
match self {
|
||||||
|
Self::Strong => p.push('*'),
|
||||||
|
Self::Emph => p.push('_'),
|
||||||
|
Self::Space => p.push(' '),
|
||||||
|
Self::Linebreak => p.push_str(r"\"),
|
||||||
|
Self::Parbreak => p.push_str("\n\n"),
|
||||||
|
// TODO: Handle escaping.
|
||||||
|
Self::Text(text) => p.push_str(&text),
|
||||||
|
Self::Heading(heading) => heading.pretty_with_map(p, map),
|
||||||
|
Self::Raw(raw) => raw.pretty(p),
|
||||||
|
Self::Expr(expr) => {
|
||||||
|
if let Some(map) = map {
|
||||||
|
let value = &map[&(expr as *const _)];
|
||||||
|
value.pretty(p);
|
||||||
|
} else if let Expr::Call(call) = expr {
|
||||||
|
// Format function templates appropriately.
|
||||||
|
pretty_bracketed(call, p, false)
|
||||||
|
} else {
|
||||||
|
expr.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrettyWithMap for NodeHeading {
|
||||||
|
fn pretty_with_map(&self, p: &mut Printer, map: Option<&ExprMap>) {
|
||||||
|
for _ in 0 ..= self.level {
|
||||||
|
p.push('=');
|
||||||
|
}
|
||||||
|
self.contents.pretty_with_map(p, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for NodeRaw {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
// Find out how many backticks we need.
|
||||||
|
let mut backticks = 1;
|
||||||
|
|
||||||
|
// Language tag and block-level are only possible with 3+ backticks.
|
||||||
|
if self.lang.is_some() || self.block {
|
||||||
|
backticks = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// More backticks may be required if there are lots of consecutive
|
||||||
|
// backticks in the lines.
|
||||||
|
let mut count;
|
||||||
|
for line in &self.lines {
|
||||||
|
count = 0;
|
||||||
|
for c in line.chars() {
|
||||||
|
if c == '`' {
|
||||||
|
count += 1;
|
||||||
|
backticks = backticks.max(3).max(count + 1);
|
||||||
|
} else {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starting backticks.
|
||||||
|
for _ in 0 .. backticks {
|
||||||
|
p.push('`');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Language tag.
|
||||||
|
if let Some(lang) = &self.lang {
|
||||||
|
lang.pretty(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start untrimming.
|
||||||
|
if self.block {
|
||||||
|
p.push('\n');
|
||||||
|
} else if backticks >= 3 {
|
||||||
|
p.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The lines.
|
||||||
|
p.join(&self.lines, "\n", |line, p| p.push_str(line));
|
||||||
|
|
||||||
|
// End untrimming.
|
||||||
|
if self.block {
|
||||||
|
p.push('\n');
|
||||||
|
} else if self.lines.last().map_or(false, |line| line.trim_end().ends_with('`')) {
|
||||||
|
p.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ending backticks.
|
||||||
|
for _ in 0 .. backticks {
|
||||||
|
p.push('`');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for Expr {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
match self {
|
||||||
|
Self::Lit(v) => v.pretty(p),
|
||||||
|
Self::Ident(v) => v.pretty(p),
|
||||||
|
Self::Array(v) => v.pretty(p),
|
||||||
|
Self::Dict(v) => v.pretty(p),
|
||||||
|
Self::Template(v) => v.pretty(p),
|
||||||
|
Self::Group(v) => v.pretty(p),
|
||||||
|
Self::Block(v) => v.pretty(p),
|
||||||
|
Self::Unary(v) => v.pretty(p),
|
||||||
|
Self::Binary(v) => v.pretty(p),
|
||||||
|
Self::Call(v) => v.pretty(p),
|
||||||
|
Self::Let(v) => v.pretty(p),
|
||||||
|
Self::If(v) => v.pretty(p),
|
||||||
|
Self::For(v) => v.pretty(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for Lit {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
self.kind.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for LitKind {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
match self {
|
||||||
|
Self::None => p.push_str("none"),
|
||||||
|
Self::Bool(v) => v.pretty(p),
|
||||||
|
Self::Int(v) => v.pretty(p),
|
||||||
|
Self::Float(v) => v.pretty(p),
|
||||||
|
Self::Length(v, u) => {
|
||||||
|
write!(p, "{}{}", ryu::Buffer::new().format(*v), u).unwrap();
|
||||||
|
}
|
||||||
|
Self::Angle(v, u) => {
|
||||||
|
write!(p, "{}{}", ryu::Buffer::new().format(*v), u).unwrap();
|
||||||
|
}
|
||||||
|
Self::Percent(v) => {
|
||||||
|
write!(p, "{}%", ryu::Buffer::new().format(*v)).unwrap();
|
||||||
|
}
|
||||||
|
Self::Color(v) => v.pretty(p),
|
||||||
|
Self::Str(v) => v.pretty(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprArray {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('(');
|
||||||
|
p.join(&self.items, ", ", |item, p| item.pretty(p));
|
||||||
|
if self.items.len() == 1 {
|
||||||
|
p.push(',');
|
||||||
|
}
|
||||||
|
p.push(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprDict {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('(');
|
||||||
|
if self.items.is_empty() {
|
||||||
|
p.push(':');
|
||||||
|
} else {
|
||||||
|
p.join(&self.items, ", ", |named, p| named.pretty(p));
|
||||||
|
}
|
||||||
|
p.push(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for Named {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
self.name.pretty(p);
|
||||||
|
p.push_str(": ");
|
||||||
|
self.expr.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprTemplate {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
if let [Node::Expr(Expr::Call(call))] = self.tree.as_slice() {
|
||||||
|
pretty_bracketed(call, p, false);
|
||||||
|
} else {
|
||||||
|
p.push('[');
|
||||||
|
self.tree.pretty_with_map(p, None);
|
||||||
|
p.push(']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprGroup {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('(');
|
||||||
|
self.expr.pretty(p);
|
||||||
|
p.push(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprBlock {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('{');
|
||||||
|
if self.exprs.len() > 1 {
|
||||||
|
p.push(' ');
|
||||||
|
}
|
||||||
|
p.join(&self.exprs, "; ", |expr, p| expr.pretty(p));
|
||||||
|
if self.exprs.len() > 1 {
|
||||||
|
p.push(' ');
|
||||||
|
}
|
||||||
|
p.push('}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprUnary {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
self.op.pretty(p);
|
||||||
|
if self.op == UnOp::Not {
|
||||||
|
p.push(' ');
|
||||||
|
}
|
||||||
|
self.expr.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for UnOp {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push_str(self.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprBinary {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
self.lhs.pretty(p);
|
||||||
|
p.push(' ');
|
||||||
|
self.op.pretty(p);
|
||||||
|
p.push(' ');
|
||||||
|
self.rhs.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for BinOp {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push_str(self.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprCall {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
self.callee.pretty(p);
|
||||||
|
p.push('(');
|
||||||
|
self.args.pretty(p);
|
||||||
|
p.push(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty print a function template, with body or chaining when possible.
|
||||||
|
pub fn pretty_bracketed(call: &ExprCall, p: &mut Printer, chained: bool) {
|
||||||
|
if chained {
|
||||||
|
p.push_str(" | ");
|
||||||
|
} else {
|
||||||
|
p.push_str("#[");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function name.
|
||||||
|
call.callee.pretty(p);
|
||||||
|
|
||||||
|
let mut write_args = |items: &[ExprArg]| {
|
||||||
|
if !items.is_empty() {
|
||||||
|
p.push(' ');
|
||||||
|
p.join(items, ", ", |item, p| item.pretty(p));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match call.args.items.as_slice() {
|
||||||
|
// This can written as a chain.
|
||||||
|
//
|
||||||
|
// Example: Transforms "#[v][[f]]" => "#[v | f]".
|
||||||
|
[head @ .., ExprArg::Pos(Expr::Call(call))] => {
|
||||||
|
write_args(head);
|
||||||
|
pretty_bracketed(call, p, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can be written with a body.
|
||||||
|
//
|
||||||
|
// Example: Transforms "#[v [Hi]]" => "#[v][Hi]".
|
||||||
|
[head @ .., ExprArg::Pos(Expr::Template(template))] => {
|
||||||
|
write_args(head);
|
||||||
|
p.push(']');
|
||||||
|
template.pretty(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
items => {
|
||||||
|
write_args(items);
|
||||||
|
p.push(']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprArgs {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.join(&self.items, ", ", |item, p| item.pretty(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprArg {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
match self {
|
||||||
|
Self::Pos(expr) => expr.pretty(p),
|
||||||
|
Self::Named(named) => named.pretty(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprLet {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push_str("#let ");
|
||||||
|
self.binding.pretty(p);
|
||||||
|
if let Some(init) = &self.init {
|
||||||
|
p.push_str(" = ");
|
||||||
|
init.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprIf {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push_str("#if ");
|
||||||
|
self.condition.pretty(p);
|
||||||
|
p.push(' ');
|
||||||
|
self.if_body.pretty(p);
|
||||||
|
if let Some(expr) = &self.else_body {
|
||||||
|
p.push_str(" #else ");
|
||||||
|
expr.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprFor {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push_str("#for ");
|
||||||
|
self.pattern.pretty(p);
|
||||||
|
p.push_str(" #in ");
|
||||||
|
self.iter.pretty(p);
|
||||||
|
p.push(' ');
|
||||||
|
self.body.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ForPattern {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
match self {
|
||||||
|
Self::Value(v) => v.pretty(p),
|
||||||
|
Self::KeyValue(k, v) => {
|
||||||
|
k.pretty(p);
|
||||||
|
p.push_str(", ");
|
||||||
|
v.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for Ident {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push_str(self.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for Value {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
match self {
|
||||||
|
Value::None => p.push_str("none"),
|
||||||
|
Value::Bool(v) => v.pretty(p),
|
||||||
|
Value::Int(v) => v.pretty(p),
|
||||||
|
Value::Float(v) => v.pretty(p),
|
||||||
|
Value::Length(v) => v.pretty(p),
|
||||||
|
Value::Angle(v) => v.pretty(p),
|
||||||
|
Value::Relative(v) => v.pretty(p),
|
||||||
|
Value::Linear(v) => v.pretty(p),
|
||||||
|
Value::Color(v) => v.pretty(p),
|
||||||
|
Value::Str(v) => v.pretty(p),
|
||||||
|
Value::Array(v) => v.pretty(p),
|
||||||
|
Value::Dict(v) => v.pretty(p),
|
||||||
|
Value::Template(v) => v.pretty(p),
|
||||||
|
Value::Func(v) => v.pretty(p),
|
||||||
|
Value::Args(v) => v.pretty(p),
|
||||||
|
Value::Any(v) => v.pretty(p),
|
||||||
|
Value::Error => p.push_str("<error>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ValueArray {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('(');
|
||||||
|
p.join(self, ", ", |item, p| item.pretty(p));
|
||||||
|
if self.len() == 1 {
|
||||||
|
p.push(',');
|
||||||
|
}
|
||||||
|
p.push(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ValueTemplate {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('[');
|
||||||
|
for part in self {
|
||||||
|
part.pretty(p);
|
||||||
|
}
|
||||||
|
p.push(']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for TemplateNode {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
match self {
|
||||||
|
Self::Tree { tree, map } => tree.pretty_with_map(p, Some(map)),
|
||||||
|
Self::Any(any) => any.pretty(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for TemplateAny {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('<');
|
||||||
|
p.push_str(self.name());
|
||||||
|
p.push('>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ValueFunc {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('<');
|
||||||
|
p.push_str(self.name());
|
||||||
|
p.push('>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ValueArgs {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push('<');
|
||||||
|
p.join(&self.items, ", ", |item, p| item.pretty(p));
|
||||||
|
p.push('>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ValueArg {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
if let Some(name) = &self.name {
|
||||||
|
p.push_str(&name.v);
|
||||||
|
p.push_str(": ");
|
||||||
|
}
|
||||||
|
self.value.v.pretty(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Pretty for i64 {
|
impl Pretty for i64 {
|
||||||
fn pretty(&self, p: &mut Printer) {
|
fn pretty(&self, p: &mut Printer) {
|
||||||
p.push_str(itoa::Buffer::new().format(*self));
|
p.push_str(itoa::Buffer::new().format(*self));
|
||||||
@ -123,11 +607,134 @@ pretty_display! {
|
|||||||
Linear,
|
Linear,
|
||||||
RgbaColor,
|
RgbaColor,
|
||||||
Color,
|
Color,
|
||||||
|
ValueAny,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::env::Env;
|
||||||
|
use crate::eval::eval;
|
||||||
|
use crate::parse::parse;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn test(src: &str, exp: &str) {
|
||||||
|
let tree = parse(src).output;
|
||||||
|
let found = pretty(&tree);
|
||||||
|
if exp != found {
|
||||||
|
println!("tree: {:#?}", tree);
|
||||||
|
println!("expected: {}", exp);
|
||||||
|
println!("found: {}", found);
|
||||||
|
panic!("test failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn roundtrip(src: &str) {
|
||||||
|
test(src, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pretty_print_node() {
|
||||||
|
// Basic text and markup.
|
||||||
|
roundtrip("*");
|
||||||
|
roundtrip("_");
|
||||||
|
roundtrip(" ");
|
||||||
|
roundtrip("\\ ");
|
||||||
|
roundtrip("\n\n");
|
||||||
|
roundtrip("hi");
|
||||||
|
|
||||||
|
// Heading.
|
||||||
|
roundtrip("= *Ok*");
|
||||||
|
|
||||||
|
// Raw.
|
||||||
|
roundtrip("``");
|
||||||
|
roundtrip("`nolang 1`");
|
||||||
|
roundtrip("```lang 1```");
|
||||||
|
roundtrip("```lang 1 ```");
|
||||||
|
roundtrip("```hi line ```");
|
||||||
|
roundtrip("```py\ndef\n```");
|
||||||
|
roundtrip("```\n line \n```");
|
||||||
|
roundtrip("```\n`\n```");
|
||||||
|
roundtrip("``` ` ```");
|
||||||
|
roundtrip("````\n```\n```\n````");
|
||||||
|
test("```lang```", "```lang ```");
|
||||||
|
test("```1 ```", "``");
|
||||||
|
test("``` 1```", "`1`");
|
||||||
|
test("``` 1 ```", "`1 `");
|
||||||
|
test("```` ` ````", "``` ` ```");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pretty_print_expr() {
|
||||||
|
// Basic expressions.
|
||||||
|
roundtrip("{none}");
|
||||||
|
roundtrip("{hi}");
|
||||||
|
roundtrip("{true}");
|
||||||
|
roundtrip("{10}");
|
||||||
|
roundtrip("{3.14}");
|
||||||
|
roundtrip("{10.0pt}");
|
||||||
|
roundtrip("{14.1deg}");
|
||||||
|
roundtrip("{20.0%}");
|
||||||
|
roundtrip("{#abcdef}");
|
||||||
|
roundtrip(r#"{"hi"}"#);
|
||||||
|
test(r#"{"let's \" go"}"#, r#"{"let's \" go"}"#);
|
||||||
|
|
||||||
|
// Arrays.
|
||||||
|
roundtrip("{()}");
|
||||||
|
roundtrip("{(1)}");
|
||||||
|
roundtrip("{(1, 2, 3)}");
|
||||||
|
|
||||||
|
// Dictionaries.
|
||||||
|
roundtrip("{(:)}");
|
||||||
|
roundtrip("{(key: value)}");
|
||||||
|
roundtrip("{(a: 1, b: 2)}");
|
||||||
|
|
||||||
|
// Templates.
|
||||||
|
roundtrip("[]");
|
||||||
|
roundtrip("[*Ok*]");
|
||||||
|
roundtrip("{[f]}");
|
||||||
|
|
||||||
|
// Groups.
|
||||||
|
roundtrip("{(1)}");
|
||||||
|
|
||||||
|
// Blocks.
|
||||||
|
roundtrip("{}");
|
||||||
|
roundtrip("{1}");
|
||||||
|
roundtrip("{ #let x = 1; x += 2; x + 1 }");
|
||||||
|
roundtrip("[{}]");
|
||||||
|
|
||||||
|
// Operators.
|
||||||
|
roundtrip("{-x}");
|
||||||
|
roundtrip("{not true}");
|
||||||
|
roundtrip("{1 + 3}");
|
||||||
|
|
||||||
|
// Parenthesized calls.
|
||||||
|
roundtrip("{v()}");
|
||||||
|
roundtrip("{v(1)}");
|
||||||
|
roundtrip("{v(a: 1, b)}");
|
||||||
|
|
||||||
|
// Function templates.
|
||||||
|
roundtrip("#[v]");
|
||||||
|
roundtrip("#[v 1]");
|
||||||
|
roundtrip("#[v 1, 2][*Ok*]");
|
||||||
|
roundtrip("#[v 1 | f 2]");
|
||||||
|
test("{#[v]}", "{v()}");
|
||||||
|
test("#[v 1, #[f 2]]", "#[v 1 | f 2]");
|
||||||
|
|
||||||
|
// Keywords.
|
||||||
|
roundtrip("#let x = 1 + 2");
|
||||||
|
roundtrip("#if x [y] #else [z]");
|
||||||
|
roundtrip("#for x #in y {z}");
|
||||||
|
roundtrip("#for k, x #in y {z}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pretty_print_with_map() {
|
||||||
|
let tree = parse("*[{1+2}[{4}]]*{2+3}").output;
|
||||||
|
let map = eval(&mut Env::blank(), &tree, &Default::default()).output;
|
||||||
|
assert_eq!(pretty_with_map(&tree, &map), "*[3[4]]*5");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pretty_print_str() {
|
fn test_pretty_print_str() {
|
||||||
|
@ -56,26 +56,6 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for Expr {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
match self {
|
|
||||||
Self::Lit(v) => v.pretty(p),
|
|
||||||
Self::Ident(v) => v.pretty(p),
|
|
||||||
Self::Array(v) => v.pretty(p),
|
|
||||||
Self::Dict(v) => v.pretty(p),
|
|
||||||
Self::Template(v) => v.pretty(p),
|
|
||||||
Self::Group(v) => v.pretty(p),
|
|
||||||
Self::Block(v) => v.pretty(p),
|
|
||||||
Self::Unary(v) => v.pretty(p),
|
|
||||||
Self::Binary(v) => v.pretty(p),
|
|
||||||
Self::Call(v) => v.pretty(p),
|
|
||||||
Self::Let(v) => v.pretty(p),
|
|
||||||
Self::If(v) => v.pretty(p),
|
|
||||||
Self::For(v) => v.pretty(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A literal.
|
/// A literal.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Lit {
|
pub struct Lit {
|
||||||
@ -85,12 +65,6 @@ pub struct Lit {
|
|||||||
pub kind: LitKind,
|
pub kind: LitKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for Lit {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
self.kind.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A kind of literal.
|
/// A kind of literal.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum LitKind {
|
pub enum LitKind {
|
||||||
@ -117,28 +91,6 @@ pub enum LitKind {
|
|||||||
Str(String),
|
Str(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for LitKind {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
match self {
|
|
||||||
Self::None => p.push_str("none"),
|
|
||||||
Self::Bool(v) => v.pretty(p),
|
|
||||||
Self::Int(v) => v.pretty(p),
|
|
||||||
Self::Float(v) => v.pretty(p),
|
|
||||||
Self::Length(v, u) => {
|
|
||||||
write!(p, "{}{}", ryu::Buffer::new().format(*v), u).unwrap();
|
|
||||||
}
|
|
||||||
Self::Angle(v, u) => {
|
|
||||||
write!(p, "{}{}", ryu::Buffer::new().format(*v), u).unwrap();
|
|
||||||
}
|
|
||||||
Self::Percent(v) => {
|
|
||||||
write!(p, "{}%", ryu::Buffer::new().format(*v)).unwrap();
|
|
||||||
}
|
|
||||||
Self::Color(v) => v.pretty(p),
|
|
||||||
Self::Str(v) => v.pretty(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An array expression: `(1, "hi", 12cm)`.
|
/// An array expression: `(1, "hi", 12cm)`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprArray {
|
pub struct ExprArray {
|
||||||
@ -148,17 +100,6 @@ pub struct ExprArray {
|
|||||||
pub items: Vec<Expr>,
|
pub items: Vec<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprArray {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('(');
|
|
||||||
p.join(&self.items, ", ", |item, p| item.pretty(p));
|
|
||||||
if self.items.len() == 1 {
|
|
||||||
p.push(',');
|
|
||||||
}
|
|
||||||
p.push(')');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
|
/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprDict {
|
pub struct ExprDict {
|
||||||
@ -168,18 +109,6 @@ pub struct ExprDict {
|
|||||||
pub items: Vec<Named>,
|
pub items: Vec<Named>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprDict {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('(');
|
|
||||||
if self.items.is_empty() {
|
|
||||||
p.push(':');
|
|
||||||
} else {
|
|
||||||
p.join(&self.items, ", ", |named, p| named.pretty(p));
|
|
||||||
}
|
|
||||||
p.push(')');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A pair of a name and an expression: `pattern: dashed`.
|
/// A pair of a name and an expression: `pattern: dashed`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Named {
|
pub struct Named {
|
||||||
@ -196,14 +125,6 @@ impl Named {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for Named {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
self.name.pretty(p);
|
|
||||||
p.push_str(": ");
|
|
||||||
self.expr.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A template expression: `[*Hi* there!]`.
|
/// A template expression: `[*Hi* there!]`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprTemplate {
|
pub struct ExprTemplate {
|
||||||
@ -213,18 +134,6 @@ pub struct ExprTemplate {
|
|||||||
pub tree: Rc<Tree>,
|
pub tree: Rc<Tree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprTemplate {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
if let [Node::Expr(Expr::Call(call))] = self.tree.as_slice() {
|
|
||||||
call.pretty_bracketed(p, false);
|
|
||||||
} else {
|
|
||||||
p.push('[');
|
|
||||||
self.tree.pretty(p);
|
|
||||||
p.push(']');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A grouped expression: `(1 + 2)`.
|
/// A grouped expression: `(1 + 2)`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprGroup {
|
pub struct ExprGroup {
|
||||||
@ -234,14 +143,6 @@ pub struct ExprGroup {
|
|||||||
pub expr: Box<Expr>,
|
pub expr: Box<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprGroup {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('(');
|
|
||||||
self.expr.pretty(p);
|
|
||||||
p.push(')');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A block expression: `{ #let x = 1; x + 2 }`.
|
/// A block expression: `{ #let x = 1; x + 2 }`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprBlock {
|
pub struct ExprBlock {
|
||||||
@ -253,20 +154,6 @@ pub struct ExprBlock {
|
|||||||
pub scoping: bool,
|
pub scoping: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprBlock {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push('{');
|
|
||||||
if self.exprs.len() > 1 {
|
|
||||||
p.push(' ');
|
|
||||||
}
|
|
||||||
p.join(&self.exprs, "; ", |expr, p| expr.pretty(p));
|
|
||||||
if self.exprs.len() > 1 {
|
|
||||||
p.push(' ');
|
|
||||||
}
|
|
||||||
p.push('}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A unary operation: `-x`.
|
/// A unary operation: `-x`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprUnary {
|
pub struct ExprUnary {
|
||||||
@ -278,16 +165,6 @@ pub struct ExprUnary {
|
|||||||
pub expr: Box<Expr>,
|
pub expr: Box<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprUnary {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
self.op.pretty(p);
|
|
||||||
if self.op == UnOp::Not {
|
|
||||||
p.push(' ');
|
|
||||||
}
|
|
||||||
self.expr.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A unary operator.
|
/// A unary operator.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum UnOp {
|
pub enum UnOp {
|
||||||
@ -328,12 +205,6 @@ impl UnOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for UnOp {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push_str(self.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A binary operation: `a + b`.
|
/// A binary operation: `a + b`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprBinary {
|
pub struct ExprBinary {
|
||||||
@ -347,16 +218,6 @@ pub struct ExprBinary {
|
|||||||
pub rhs: Box<Expr>,
|
pub rhs: Box<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprBinary {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
self.lhs.pretty(p);
|
|
||||||
p.push(' ');
|
|
||||||
self.op.pretty(p);
|
|
||||||
p.push(' ');
|
|
||||||
self.rhs.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A binary operator.
|
/// A binary operator.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum BinOp {
|
pub enum BinOp {
|
||||||
@ -484,12 +345,6 @@ impl BinOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for BinOp {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push_str(self.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The associativity of a binary operator.
|
/// The associativity of a binary operator.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum Associativity {
|
pub enum Associativity {
|
||||||
@ -510,60 +365,6 @@ pub struct ExprCall {
|
|||||||
pub args: ExprArgs,
|
pub args: ExprArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprCall {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
self.callee.pretty(p);
|
|
||||||
p.push('(');
|
|
||||||
self.args.pretty(p);
|
|
||||||
p.push(')');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExprCall {
|
|
||||||
/// Pretty print a function template, with body or chaining when possible.
|
|
||||||
pub fn pretty_bracketed(&self, p: &mut Printer, chained: bool) {
|
|
||||||
if chained {
|
|
||||||
p.push_str(" | ");
|
|
||||||
} else {
|
|
||||||
p.push_str("#[");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function name.
|
|
||||||
self.callee.pretty(p);
|
|
||||||
|
|
||||||
let mut write_args = |items: &[ExprArg]| {
|
|
||||||
if !items.is_empty() {
|
|
||||||
p.push(' ');
|
|
||||||
p.join(items, ", ", |item, p| item.pretty(p));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.args.items.as_slice() {
|
|
||||||
// This can written as a chain.
|
|
||||||
//
|
|
||||||
// Example: Transforms "#[v][[f]]" => "#[v | f]".
|
|
||||||
[head @ .., ExprArg::Pos(Expr::Call(call))] => {
|
|
||||||
write_args(head);
|
|
||||||
call.pretty_bracketed(p, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This can be written with a body.
|
|
||||||
//
|
|
||||||
// Example: Transforms "#[v [Hi]]" => "#[v][Hi]".
|
|
||||||
[head @ .., ExprArg::Pos(Expr::Template(template))] => {
|
|
||||||
write_args(head);
|
|
||||||
p.push(']');
|
|
||||||
template.pretty(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
items => {
|
|
||||||
write_args(items);
|
|
||||||
p.push(']');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The arguments to a function: `12, draw: false`.
|
/// The arguments to a function: `12, draw: false`.
|
||||||
///
|
///
|
||||||
/// In case of a bracketed invocation with a body, the body is _not_
|
/// In case of a bracketed invocation with a body, the body is _not_
|
||||||
@ -576,12 +377,6 @@ pub struct ExprArgs {
|
|||||||
pub items: Vec<ExprArg>,
|
pub items: Vec<ExprArg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprArgs {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.join(&self.items, ", ", |item, p| item.pretty(p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An argument to a function call: `12` or `draw: false`.
|
/// An argument to a function call: `12` or `draw: false`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ExprArg {
|
pub enum ExprArg {
|
||||||
@ -601,15 +396,6 @@ impl ExprArg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprArg {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
match self {
|
|
||||||
Self::Pos(expr) => expr.pretty(p),
|
|
||||||
Self::Named(named) => named.pretty(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A let expression: `#let x = 1`.
|
/// A let expression: `#let x = 1`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprLet {
|
pub struct ExprLet {
|
||||||
@ -621,17 +407,6 @@ pub struct ExprLet {
|
|||||||
pub init: Option<Box<Expr>>,
|
pub init: Option<Box<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprLet {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push_str("#let ");
|
|
||||||
self.binding.pretty(p);
|
|
||||||
if let Some(init) = &self.init {
|
|
||||||
p.push_str(" = ");
|
|
||||||
init.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An if expression: `#if x { y } #else { z }`.
|
/// An if expression: `#if x { y } #else { z }`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprIf {
|
pub struct ExprIf {
|
||||||
@ -645,19 +420,6 @@ pub struct ExprIf {
|
|||||||
pub else_body: Option<Box<Expr>>,
|
pub else_body: Option<Box<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprIf {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push_str("#if ");
|
|
||||||
self.condition.pretty(p);
|
|
||||||
p.push(' ');
|
|
||||||
self.if_body.pretty(p);
|
|
||||||
if let Some(expr) = &self.else_body {
|
|
||||||
p.push_str(" #else ");
|
|
||||||
expr.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A for expression: `#for x #in y { z }`.
|
/// A for expression: `#for x #in y { z }`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprFor {
|
pub struct ExprFor {
|
||||||
@ -671,17 +433,6 @@ pub struct ExprFor {
|
|||||||
pub body: Box<Expr>,
|
pub body: Box<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprFor {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push_str("#for ");
|
|
||||||
self.pattern.pretty(p);
|
|
||||||
p.push_str(" #in ");
|
|
||||||
self.iter.pretty(p);
|
|
||||||
p.push(' ');
|
|
||||||
self.body.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A pattern in a for loop.
|
/// A pattern in a for loop.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ForPattern {
|
pub enum ForPattern {
|
||||||
@ -700,16 +451,3 @@ impl ForPattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ForPattern {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
match self {
|
|
||||||
Self::Value(v) => v.pretty(p),
|
|
||||||
Self::KeyValue(k, v) => {
|
|
||||||
k.pretty(p);
|
|
||||||
p.push_str(", ");
|
|
||||||
v.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,7 +3,6 @@ use std::ops::Deref;
|
|||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
|
|
||||||
use super::Span;
|
use super::Span;
|
||||||
use crate::pretty::{Pretty, Printer};
|
|
||||||
|
|
||||||
/// An Unicode identifier with a few extra permissible characters.
|
/// An Unicode identifier with a few extra permissible characters.
|
||||||
///
|
///
|
||||||
@ -53,12 +52,6 @@ impl Deref for Ident {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for Ident {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
p.push_str(self.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether a string is a valid identifier.
|
/// Whether a string is a valid identifier.
|
||||||
pub fn is_ident(string: &str) -> bool {
|
pub fn is_ident(string: &str) -> bool {
|
||||||
let mut chars = string.chars();
|
let mut chars = string.chars();
|
||||||
|
@ -13,133 +13,5 @@ pub use node::*;
|
|||||||
pub use span::*;
|
pub use span::*;
|
||||||
pub use token::*;
|
pub use token::*;
|
||||||
|
|
||||||
use crate::pretty::{Pretty, Printer};
|
|
||||||
|
|
||||||
/// The abstract syntax tree.
|
/// The abstract syntax tree.
|
||||||
pub type Tree = Vec<Node>;
|
pub type Tree = Vec<Node>;
|
||||||
|
|
||||||
impl Pretty for Tree {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
for node in self {
|
|
||||||
node.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::parse::parse;
|
|
||||||
use crate::pretty::pretty;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn test(src: &str, exp: &str) {
|
|
||||||
let tree = parse(src).output;
|
|
||||||
let found = pretty(&tree);
|
|
||||||
if exp != found {
|
|
||||||
println!("tree: {:#?}", tree);
|
|
||||||
println!("expected: {}", exp);
|
|
||||||
println!("found: {}", found);
|
|
||||||
panic!("test failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn roundtrip(src: &str) {
|
|
||||||
test(src, src);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pretty_print_node() {
|
|
||||||
// Basic text and markup.
|
|
||||||
roundtrip("*");
|
|
||||||
roundtrip("_");
|
|
||||||
roundtrip(" ");
|
|
||||||
roundtrip("\\ ");
|
|
||||||
roundtrip("\n\n");
|
|
||||||
roundtrip("hi");
|
|
||||||
|
|
||||||
// Heading.
|
|
||||||
roundtrip("= *Ok*");
|
|
||||||
|
|
||||||
// Raw.
|
|
||||||
roundtrip("``");
|
|
||||||
roundtrip("`nolang 1`");
|
|
||||||
roundtrip("```lang 1```");
|
|
||||||
roundtrip("```lang 1 ```");
|
|
||||||
roundtrip("```hi line ```");
|
|
||||||
roundtrip("```py\ndef\n```");
|
|
||||||
roundtrip("```\n line \n```");
|
|
||||||
roundtrip("```\n`\n```");
|
|
||||||
roundtrip("``` ` ```");
|
|
||||||
roundtrip("````\n```\n```\n````");
|
|
||||||
test("```lang```", "```lang ```");
|
|
||||||
test("```1 ```", "``");
|
|
||||||
test("``` 1```", "`1`");
|
|
||||||
test("``` 1 ```", "`1 `");
|
|
||||||
test("```` ` ````", "``` ` ```");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pretty_print_expr() {
|
|
||||||
// Basic expressions.
|
|
||||||
roundtrip("{none}");
|
|
||||||
roundtrip("{hi}");
|
|
||||||
roundtrip("{true}");
|
|
||||||
roundtrip("{10}");
|
|
||||||
roundtrip("{3.14}");
|
|
||||||
roundtrip("{10.0pt}");
|
|
||||||
roundtrip("{14.1deg}");
|
|
||||||
roundtrip("{20.0%}");
|
|
||||||
roundtrip("{#abcdef}");
|
|
||||||
roundtrip(r#"{"hi"}"#);
|
|
||||||
test(r#"{"let's \" go"}"#, r#"{"let's \" go"}"#);
|
|
||||||
|
|
||||||
// Arrays.
|
|
||||||
roundtrip("{()}");
|
|
||||||
roundtrip("{(1)}");
|
|
||||||
roundtrip("{(1, 2, 3)}");
|
|
||||||
|
|
||||||
// Dictionaries.
|
|
||||||
roundtrip("{(:)}");
|
|
||||||
roundtrip("{(key: value)}");
|
|
||||||
roundtrip("{(a: 1, b: 2)}");
|
|
||||||
|
|
||||||
// Templates.
|
|
||||||
roundtrip("[]");
|
|
||||||
roundtrip("[*Ok*]");
|
|
||||||
roundtrip("{[f]}");
|
|
||||||
|
|
||||||
// Groups.
|
|
||||||
roundtrip("{(1)}");
|
|
||||||
|
|
||||||
// Blocks.
|
|
||||||
roundtrip("{}");
|
|
||||||
roundtrip("{1}");
|
|
||||||
roundtrip("{ #let x = 1; x += 2; x + 1 }");
|
|
||||||
roundtrip("[{}]");
|
|
||||||
|
|
||||||
// Operators.
|
|
||||||
roundtrip("{-x}");
|
|
||||||
roundtrip("{not true}");
|
|
||||||
roundtrip("{1 + 3}");
|
|
||||||
|
|
||||||
// Parenthesized calls.
|
|
||||||
roundtrip("{v()}");
|
|
||||||
roundtrip("{v(1)}");
|
|
||||||
roundtrip("{v(a: 1, b)}");
|
|
||||||
|
|
||||||
// Function templates.
|
|
||||||
roundtrip("#[v]");
|
|
||||||
roundtrip("#[v 1]");
|
|
||||||
roundtrip("#[v 1, 2][*Ok*]");
|
|
||||||
roundtrip("#[v 1 | f 2]");
|
|
||||||
test("{#[v]}", "{v()}");
|
|
||||||
test("#[v 1, #[f 2]]", "#[v 1 | f 2]");
|
|
||||||
|
|
||||||
// Keywords.
|
|
||||||
roundtrip("#let x = 1 + 2");
|
|
||||||
roundtrip("#if x [y] #else [z]");
|
|
||||||
roundtrip("#for x #in y {z}");
|
|
||||||
roundtrip("#for k, x #in y {z}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -23,30 +23,6 @@ pub enum Node {
|
|||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for Node {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
match self {
|
|
||||||
Self::Strong => p.push('*'),
|
|
||||||
Self::Emph => p.push('_'),
|
|
||||||
Self::Space => p.push(' '),
|
|
||||||
Self::Linebreak => p.push_str(r"\"),
|
|
||||||
Self::Parbreak => p.push_str("\n\n"),
|
|
||||||
// TODO: Handle escaping.
|
|
||||||
Self::Text(text) => p.push_str(&text),
|
|
||||||
Self::Heading(heading) => heading.pretty(p),
|
|
||||||
Self::Raw(raw) => raw.pretty(p),
|
|
||||||
Self::Expr(expr) => {
|
|
||||||
if let Expr::Call(call) = expr {
|
|
||||||
// Format function templates appropriately.
|
|
||||||
call.pretty_bracketed(p, false)
|
|
||||||
} else {
|
|
||||||
expr.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A section heading: `= Introduction`.
|
/// A section heading: `= Introduction`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct NodeHeading {
|
pub struct NodeHeading {
|
||||||
@ -56,15 +32,6 @@ pub struct NodeHeading {
|
|||||||
pub contents: Tree,
|
pub contents: Tree,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for NodeHeading {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
for _ in 0 ..= self.level {
|
|
||||||
p.push('=');
|
|
||||||
}
|
|
||||||
self.contents.pretty(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A raw block with optional syntax highlighting: `` `raw` ``.
|
/// A raw block with optional syntax highlighting: `` `raw` ``.
|
||||||
///
|
///
|
||||||
/// Raw blocks start with 1 or 3+ backticks and end with the same number of
|
/// Raw blocks start with 1 or 3+ backticks and end with the same number of
|
||||||
@ -139,62 +106,3 @@ pub struct NodeRaw {
|
|||||||
/// and contains at least one newline.
|
/// and contains at least one newline.
|
||||||
pub block: bool,
|
pub block: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for NodeRaw {
|
|
||||||
fn pretty(&self, p: &mut Printer) {
|
|
||||||
// Find out how many backticks we need.
|
|
||||||
let mut backticks = 1;
|
|
||||||
|
|
||||||
// Language tag and block-level are only possible with 3+ backticks.
|
|
||||||
if self.lang.is_some() || self.block {
|
|
||||||
backticks = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// More backticks may be required if there are lots of consecutive
|
|
||||||
// backticks in the lines.
|
|
||||||
let mut count;
|
|
||||||
for line in &self.lines {
|
|
||||||
count = 0;
|
|
||||||
for c in line.chars() {
|
|
||||||
if c == '`' {
|
|
||||||
count += 1;
|
|
||||||
backticks = backticks.max(3).max(count + 1);
|
|
||||||
} else {
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Starting backticks.
|
|
||||||
for _ in 0 .. backticks {
|
|
||||||
p.push('`');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Language tag.
|
|
||||||
if let Some(lang) = &self.lang {
|
|
||||||
lang.pretty(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start untrimming.
|
|
||||||
if self.block {
|
|
||||||
p.push('\n');
|
|
||||||
} else if backticks >= 3 {
|
|
||||||
p.push(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// The lines.
|
|
||||||
p.join(&self.lines, "\n", |line, p| p.push_str(line));
|
|
||||||
|
|
||||||
// End untrimming.
|
|
||||||
if self.block {
|
|
||||||
p.push('\n');
|
|
||||||
} else if self.lines.last().map_or(false, |line| line.trim_end().ends_with('`')) {
|
|
||||||
p.push(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ending backticks.
|
|
||||||
for _ in 0 .. backticks {
|
|
||||||
p.push('`');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,40 +1,11 @@
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
use std::ops::Range;
|
use std::ops::{Add, Range};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static CMP_SPANS: Cell<bool> = Cell::new(true);
|
static CMP_SPANS: Cell<bool> = Cell::new(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Annotate a value with a span.
|
|
||||||
pub trait WithSpan: Sized {
|
|
||||||
/// Wraps `self` in a `Spanned` with the given span.
|
|
||||||
fn with_span(self, span: impl Into<Span>) -> Spanned<Self> {
|
|
||||||
Spanned::new(self, span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> WithSpan for T {}
|
|
||||||
|
|
||||||
/// Span offsetting.
|
|
||||||
pub trait Offset {
|
|
||||||
/// Offset all spans contained in `Self` by the given position.
|
|
||||||
fn offset(self, by: impl Into<Pos>) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A vector of spanned values of type `T`.
|
|
||||||
pub type SpanVec<T> = Vec<Spanned<T>>;
|
|
||||||
|
|
||||||
impl<T> Offset for SpanVec<T> {
|
|
||||||
fn offset(mut self, by: impl Into<Pos>) -> Self {
|
|
||||||
let by = by.into();
|
|
||||||
for spanned in &mut self {
|
|
||||||
spanned.span = spanned.span.offset(by);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
@ -68,29 +39,6 @@ impl<T> Spanned<T> {
|
|||||||
{
|
{
|
||||||
Spanned { v: f(self.v), span: self.span }
|
Spanned { v: f(self.v), span: self.span }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps the span while keeping the value.
|
|
||||||
pub fn map_span<F>(mut self, f: F) -> Self
|
|
||||||
where
|
|
||||||
F: FnOnce(Span) -> Span,
|
|
||||||
{
|
|
||||||
self.span = f(self.span);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Spanned<Option<T>> {
|
|
||||||
/// Swap the spanned and the option.
|
|
||||||
pub fn transpose(self) -> Option<Spanned<T>> {
|
|
||||||
let Spanned { v, span } = self;
|
|
||||||
v.map(|v| v.with_span(span))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Offset for Spanned<T> {
|
|
||||||
fn offset(self, by: impl Into<Pos>) -> Self {
|
|
||||||
self.map_span(|span| span.offset(by))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Debug> Debug for Spanned<T> {
|
impl<T: Debug> Debug for Spanned<T> {
|
||||||
@ -171,16 +119,6 @@ impl Span {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Offset for Span {
|
|
||||||
fn offset(self, by: impl Into<Pos>) -> Self {
|
|
||||||
let by = by.into();
|
|
||||||
Self {
|
|
||||||
start: self.start.offset(by),
|
|
||||||
end: self.end.offset(by),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Span {}
|
impl Eq for Span {}
|
||||||
|
|
||||||
impl PartialEq for Span {
|
impl PartialEq for Span {
|
||||||
@ -234,9 +172,14 @@ impl Pos {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Offset for Pos {
|
impl<T> Add<T> for Pos
|
||||||
fn offset(self, by: impl Into<Pos>) -> Self {
|
where
|
||||||
Pos(self.0 + by.into().0)
|
T: Into<Pos>,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: T) -> Self {
|
||||||
|
Pos(self.0 + rhs.into().0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
@ -25,7 +25,7 @@ use typst::layout::{Element, Expansion, Fill, Frame, Geometry, Image, Shape};
|
|||||||
use typst::library;
|
use typst::library;
|
||||||
use typst::parse::{LineMap, Scanner};
|
use typst::parse::{LineMap, Scanner};
|
||||||
use typst::shaping::Shaped;
|
use typst::shaping::Shaped;
|
||||||
use typst::syntax::{Location, Pos, SpanVec, Spanned, WithSpan};
|
use typst::syntax::{Location, Pos, Spanned};
|
||||||
use typst::typeset;
|
use typst::typeset;
|
||||||
|
|
||||||
const TYP_DIR: &str = "typ";
|
const TYP_DIR: &str = "typ";
|
||||||
@ -266,7 +266,7 @@ fn test_part(
|
|||||||
(ok, frames)
|
(ok, frames)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, SpanVec<Diag>) {
|
fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, Vec<Spanned<Diag>>) {
|
||||||
let mut diags = vec![];
|
let mut diags = vec![];
|
||||||
let mut compare_ref = None;
|
let mut compare_ref = None;
|
||||||
|
|
||||||
@ -304,7 +304,8 @@ fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, SpanVec<Diag>) {
|
|||||||
let mut s = Scanner::new(rest);
|
let mut s = Scanner::new(rest);
|
||||||
let (start, _, end) = (pos(&mut s), s.eat_assert('-'), pos(&mut s));
|
let (start, _, end) = (pos(&mut s), s.eat_assert('-'), pos(&mut s));
|
||||||
|
|
||||||
diags.push(Diag::new(level, s.rest().trim()).with_span(start .. end));
|
let diag = Diag::new(level, s.rest().trim());
|
||||||
|
diags.push(Spanned::new(diag, start .. end));
|
||||||
}
|
}
|
||||||
|
|
||||||
diags.sort_by_key(|d| d.span);
|
diags.sort_by_key(|d| d.span);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user