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.
|
||||
|
||||
use crate::syntax::SpanVec;
|
||||
use crate::syntax::Spanned;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
/// 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)]
|
||||
pub struct Feedback {
|
||||
/// Diagnostics about the source code.
|
||||
pub diags: SpanVec<Diag>,
|
||||
pub diags: Vec<Spanned<Diag>>,
|
||||
/// Decorations of the source code for semantic syntax highlighting.
|
||||
pub decos: SpanVec<Deco>,
|
||||
pub decos: Vec<Spanned<Deco>>,
|
||||
}
|
||||
|
||||
impl Feedback {
|
||||
|
11
src/env.rs
11
src/env.rs
@ -7,6 +7,7 @@ use std::fs;
|
||||
use std::io::Cursor;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use fontdock::fs::FsSource;
|
||||
use image::io::Reader as ImageReader;
|
||||
use image::{DynamicImage, GenericImageView, ImageFormat};
|
||||
|
||||
@ -21,6 +22,16 @@ pub struct Env {
|
||||
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.
|
||||
pub struct ResourceLoader {
|
||||
paths: HashMap<PathBuf, ResourceId>,
|
||||
|
@ -377,11 +377,11 @@ impl Eval for ExprArgs {
|
||||
.map(|arg| match arg {
|
||||
ExprArg::Pos(expr) => ValueArg {
|
||||
name: None,
|
||||
value: expr.eval(ctx).with_span(expr.span()),
|
||||
value: Spanned::new(expr.eval(ctx), expr.span()),
|
||||
},
|
||||
ExprArg::Named(Named { name, expr }) => ValueArg {
|
||||
name: Some(name.string.clone().with_span(name.span)),
|
||||
value: expr.eval(ctx).with_span(expr.span()),
|
||||
name: Some(Spanned::new(name.string.clone(), name.span)),
|
||||
value: Spanned::new(expr.eval(ctx), expr.span()),
|
||||
},
|
||||
})
|
||||
.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)`.
|
||||
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)`.
|
||||
pub type ValueDict = BTreeMap<String, Value>;
|
||||
|
||||
@ -157,16 +122,6 @@ impl Pretty for ValueDict {
|
||||
/// A template value: `[*Hi* there]`.
|
||||
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.
|
||||
///
|
||||
/// Evaluating a template expression creates only a single chunk. Adding two
|
||||
@ -185,16 +140,6 @@ pub enum TemplateNode {
|
||||
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).
|
||||
#[derive(Clone)]
|
||||
pub struct TemplateAny {
|
||||
@ -210,6 +155,12 @@ impl TemplateAny {
|
||||
{
|
||||
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 {
|
||||
@ -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 {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.debug_struct("TemplateAny").finish()
|
||||
@ -256,6 +199,11 @@ impl ValueFunc {
|
||||
{
|
||||
Self { name: name.into(), f: Rc::new(f) }
|
||||
}
|
||||
|
||||
/// The name of the function.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
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.
|
||||
//
|
||||
// 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.
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
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 {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn into_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
@ -618,7 +540,7 @@ where
|
||||
match T::cast(value.v) {
|
||||
CastResult::Ok(t) => CastResult::Ok(t),
|
||||
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>> {
|
||||
let span = value.span;
|
||||
match T::cast(value.v) {
|
||||
CastResult::Ok(t) => CastResult::Ok(t.with_span(span)),
|
||||
CastResult::Warn(t, m) => CastResult::Warn(t.with_span(span), m),
|
||||
CastResult::Err(v) => CastResult::Err(v.with_span(span)),
|
||||
CastResult::Ok(t) => CastResult::Ok(Spanned::new(t, span)),
|
||||
CastResult::Warn(t, m) => CastResult::Warn(Spanned::new(t, span), m),
|
||||
CastResult::Err(v) => CastResult::Err(Spanned::new(v, span)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ pub fn exec(
|
||||
) -> Pass<layout::Tree> {
|
||||
let mut ctx = ExecContext::new(env, state);
|
||||
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.finish()
|
||||
}
|
||||
@ -51,21 +51,21 @@ pub trait Exec {
|
||||
}
|
||||
|
||||
/// Execute a node with an expression map that applies to it.
|
||||
pub trait ExecWith {
|
||||
pub trait ExecWithMap {
|
||||
/// 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 {
|
||||
fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||
impl ExecWithMap for Tree {
|
||||
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||
for node in self {
|
||||
node.exec_with(ctx, map);
|
||||
node.exec_with_map(ctx, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecWith for Node {
|
||||
fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||
impl ExecWithMap for Node {
|
||||
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||
match self {
|
||||
Node::Text(text) => ctx.push_text(text),
|
||||
Node::Space => ctx.push_space(),
|
||||
@ -73,21 +73,21 @@ impl ExecWith for Node {
|
||||
Node::Parbreak => ctx.apply_parbreak(),
|
||||
Node::Strong => ctx.state.font.strong ^= 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::Expr(expr) => map[&(expr as *const _)].exec(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecWith for NodeHeading {
|
||||
fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||
impl ExecWithMap for NodeHeading {
|
||||
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||
let prev = ctx.state.clone();
|
||||
let upscale = 1.5 - 0.1 * self.level as f64;
|
||||
ctx.state.font.scale *= upscale;
|
||||
ctx.state.font.strong = true;
|
||||
|
||||
self.contents.exec_with(ctx, map);
|
||||
self.contents.exec_with_map(ctx, map);
|
||||
ctx.apply_parbreak();
|
||||
|
||||
ctx.state = prev;
|
||||
@ -154,7 +154,7 @@ impl Exec for ValueTemplate {
|
||||
impl Exec for TemplateNode {
|
||||
fn exec(&self, ctx: &mut ExecContext) {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::Scanner;
|
||||
use crate::syntax::{Location, Offset, Pos};
|
||||
use crate::syntax::{Location, Pos};
|
||||
|
||||
/// Enables conversion of byte position to locations.
|
||||
pub struct LineMap<'s> {
|
||||
@ -44,7 +44,7 @@ impl<'s> LineMap<'s> {
|
||||
pub fn pos(&self, location: Location) -> Option<Pos> {
|
||||
// Determine the boundaries of the line.
|
||||
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
|
||||
.line_starts
|
||||
.get(location.line as usize)
|
||||
@ -64,7 +64,7 @@ impl<'s> LineMap<'s> {
|
||||
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 crate::syntax::{Ident, NodeRaw, Offset, Pos};
|
||||
use crate::syntax::{Ident, NodeRaw, Pos};
|
||||
|
||||
/// Resolve all escape sequences in a 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 (lines, had_newline) = trim_and_split_raw(inner);
|
||||
NodeRaw {
|
||||
lang: Ident::new(tag, start .. start.offset(tag.len())),
|
||||
lang: Ident::new(tag, start .. start + tag.len()),
|
||||
lines,
|
||||
block: had_newline,
|
||||
}
|
||||
|
@ -12,5 +12,5 @@ pub use crate::geom::*;
|
||||
#[doc(no_inline)]
|
||||
pub use crate::layout::Node;
|
||||
#[doc(no_inline)]
|
||||
pub use crate::syntax::{Span, Spanned, WithSpan};
|
||||
pub use crate::syntax::{Span, Spanned};
|
||||
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 crate::color::{Color, RgbaColor};
|
||||
use crate::eval::*;
|
||||
use crate::geom::{Angle, Length, Linear, Relative};
|
||||
use crate::syntax::*;
|
||||
|
||||
/// Pretty print an item and return the resulting string.
|
||||
pub fn pretty<T>(item: &T) -> String
|
||||
@ -15,12 +17,37 @@ where
|
||||
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 {
|
||||
/// Pretty print this item into the given 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.
|
||||
pub struct Printer {
|
||||
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 {
|
||||
fn pretty(&self, p: &mut Printer) {
|
||||
p.push_str(itoa::Buffer::new().format(*self));
|
||||
@ -123,11 +607,134 @@ pretty_display! {
|
||||
Linear,
|
||||
RgbaColor,
|
||||
Color,
|
||||
ValueAny,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
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]
|
||||
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.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Lit {
|
||||
@ -85,12 +65,6 @@ pub struct Lit {
|
||||
pub kind: LitKind,
|
||||
}
|
||||
|
||||
impl Pretty for Lit {
|
||||
fn pretty(&self, p: &mut Printer) {
|
||||
self.kind.pretty(p);
|
||||
}
|
||||
}
|
||||
|
||||
/// A kind of literal.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum LitKind {
|
||||
@ -117,28 +91,6 @@ pub enum LitKind {
|
||||
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)`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprArray {
|
||||
@ -148,17 +100,6 @@ pub struct ExprArray {
|
||||
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)`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprDict {
|
||||
@ -168,18 +109,6 @@ pub struct ExprDict {
|
||||
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`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
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!]`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprTemplate {
|
||||
@ -213,18 +134,6 @@ pub struct ExprTemplate {
|
||||
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)`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprGroup {
|
||||
@ -234,14 +143,6 @@ pub struct ExprGroup {
|
||||
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 }`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprBlock {
|
||||
@ -253,20 +154,6 @@ pub struct ExprBlock {
|
||||
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`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprUnary {
|
||||
@ -278,16 +165,6 @@ pub struct ExprUnary {
|
||||
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.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
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`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprBinary {
|
||||
@ -347,16 +218,6 @@ pub struct ExprBinary {
|
||||
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.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
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.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Associativity {
|
||||
@ -510,60 +365,6 @@ pub struct ExprCall {
|
||||
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`.
|
||||
///
|
||||
/// In case of a bracketed invocation with a body, the body is _not_
|
||||
@ -576,12 +377,6 @@ pub struct ExprArgs {
|
||||
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`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
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`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprLet {
|
||||
@ -621,17 +407,6 @@ pub struct ExprLet {
|
||||
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 }`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprIf {
|
||||
@ -645,19 +420,6 @@ pub struct ExprIf {
|
||||
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 }`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprFor {
|
||||
@ -671,17 +433,6 @@ pub struct ExprFor {
|
||||
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.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
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 super::Span;
|
||||
use crate::pretty::{Pretty, Printer};
|
||||
|
||||
/// 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.
|
||||
pub fn is_ident(string: &str) -> bool {
|
||||
let mut chars = string.chars();
|
||||
|
@ -13,133 +13,5 @@ pub use node::*;
|
||||
pub use span::*;
|
||||
pub use token::*;
|
||||
|
||||
use crate::pretty::{Pretty, Printer};
|
||||
|
||||
/// The abstract syntax tree.
|
||||
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),
|
||||
}
|
||||
|
||||
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`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct NodeHeading {
|
||||
@ -56,15 +32,6 @@ pub struct NodeHeading {
|
||||
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` ``.
|
||||
///
|
||||
/// 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.
|
||||
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::fmt::{self, Debug, Display, Formatter};
|
||||
use std::ops::Range;
|
||||
use std::ops::{Add, Range};
|
||||
|
||||
thread_local! {
|
||||
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.
|
||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
@ -68,29 +39,6 @@ impl<T> Spanned<T> {
|
||||
{
|
||||
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> {
|
||||
@ -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 PartialEq for Span {
|
||||
@ -234,9 +172,14 @@ impl Pos {
|
||||
}
|
||||
}
|
||||
|
||||
impl Offset for Pos {
|
||||
fn offset(self, by: impl Into<Pos>) -> Self {
|
||||
Pos(self.0 + by.into().0)
|
||||
impl<T> Add<T> for Pos
|
||||
where
|
||||
T: Into<Pos>,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: T) -> Self {
|
||||
Pos(self.0 + rhs.into().0)
|
||||
}
|
||||
}
|
||||
|
||||
|
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::parse::{LineMap, Scanner};
|
||||
use typst::shaping::Shaped;
|
||||
use typst::syntax::{Location, Pos, SpanVec, Spanned, WithSpan};
|
||||
use typst::syntax::{Location, Pos, Spanned};
|
||||
use typst::typeset;
|
||||
|
||||
const TYP_DIR: &str = "typ";
|
||||
@ -266,7 +266,7 @@ fn test_part(
|
||||
(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 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 (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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user