diff --git a/src/pretty/markup.rs b/src/pretty/markup.rs new file mode 100644 index 0000000..2ad3500 --- /dev/null +++ b/src/pretty/markup.rs @@ -0,0 +1,118 @@ +use std::collections::VecDeque; + +use pretty::BoxDoc; +use typst_syntax::ast::*; +use typst_syntax::{ast, SyntaxNode}; + +use crate::pretty::trivia; +use crate::PrettyPrinter; + +impl PrettyPrinter { + pub fn convert_markup<'a>(&'a self, root: Markup<'a>) -> BoxDoc<'a, ()> { + let mut doc: BoxDoc<()> = BoxDoc::nil(); + #[derive(Debug, Default)] + struct Line<'a> { + has_text: bool, + nodes: VecDeque<&'a SyntaxNode>, + } + // break markup into lines, split by stmt, parbreak, newline, multiline raw, equation + // if a line contains text, it will be skipped by the formatter to keep the original format + let mut lines = { + let mut lines: VecDeque = VecDeque::new(); + let mut current_line = Line { + has_text: false, + nodes: VecDeque::new(), + }; + for node in root.to_untyped().children() { + let mut break_line = false; + if let Some(space) = node.cast::() { + if space.to_untyped().text().contains('\n') { + break_line = true; + } + } else if let Some(pb) = node.cast::() { + if pb.to_untyped().text().contains('\n') { + break_line = true; + } + } else if node.kind().is_stmt() { + break_line = true; + } else if let Some(expr) = node.cast::() { + match expr { + ast::Expr::Text(_) => current_line.has_text = true, + ast::Expr::Raw(r) => { + if r.block() { + break_line = true; + } else { + current_line.has_text = true; + } + } + ast::Expr::Strong(_) | ast::Expr::Emph(_) => current_line.has_text = true, + ast::Expr::Code(_) => break_line = true, + ast::Expr::Equation(e) if e.block() => break_line = true, + _ => (), + } + } + current_line.nodes.push_back(node); + if break_line { + lines.push_back(current_line); + current_line = Line::default(); + } + } + if !current_line.nodes.is_empty() { + lines.push_back(current_line); + } + lines + }; + + // remove prefix and postfix spaces + while let Some(Line { has_text: _, nodes }) = lines.front() { + if nodes.len() == 1 { + if let Some(_space) = nodes[0].cast::() { + lines.pop_front(); + continue; + } + } + break; + } + while let Some(Line { has_text: _, nodes }) = lines.back() { + if nodes.len() == 1 { + if let Some(_space) = nodes[0].cast::() { + lines.pop_back(); + continue; + } + } + break; + } + if let Some(Line { has_text: _, nodes }) = lines.front_mut() { + if let Some(_space) = nodes.front().and_then(|node| node.cast::()) { + nodes.pop_front(); + } + } + if let Some(Line { has_text: _, nodes }) = lines.back_mut() { + if let Some(_space) = nodes.back().and_then(|node| node.cast::()) { + nodes.pop_back(); + } + } + + for Line { has_text, nodes } in lines.into_iter() { + for node in nodes { + if let Some(space) = node.cast::() { + doc = doc.append(self.convert_space(space)); + continue; + } + if let Some(pb) = node.cast::() { + doc = doc.append(self.convert_parbreak(pb)); + continue; + } + if has_text { + doc = doc.append(self.format_disabled(node)); + } else if let Some(expr) = node.cast::() { + let expr_doc = self.convert_expr(expr); + doc = doc.append(expr_doc); + } else { + doc = doc.append(trivia(node)); + } + } + } + doc + } +} diff --git a/src/pretty/mod.rs b/src/pretty/mod.rs index 7cf5411..6ff93e8 100644 --- a/src/pretty/mod.rs +++ b/src/pretty/mod.rs @@ -1,1175 +1,1104 @@ -use std::borrow::Cow; -use std::collections::HashSet; - -use itertools::Itertools; -use pretty::BoxDoc; -use typst_syntax::{ast, SyntaxNode}; -use typst_syntax::{ast::*, SyntaxKind}; - -use crate::util::{self, pretty_items}; - -#[derive(Debug, Default)] -pub struct PrettyPrinter { - disabled_nodes: HashSet, -} - -impl PrettyPrinter { - pub fn new(disabled_nodes: HashSet) -> Self { - Self { disabled_nodes } - } -} - -impl PrettyPrinter { - pub fn convert_markup<'a>(&'a self, root: Markup<'a>) -> BoxDoc<'a, ()> { - let mut doc: BoxDoc<()> = BoxDoc::nil(); - #[derive(Debug, Default)] - struct Line<'a> { - has_text: bool, - nodes: Vec<&'a SyntaxNode>, - } - // break markup into lines, split by stmt, parbreak, newline, multiline raw, equation - // if a line contains text, it will be skipped by the formatter to keep the original format - let lines = { - let mut lines: Vec = vec![]; - let mut current_line = Line { - has_text: false, - nodes: vec![], - }; - for node in root.to_untyped().children() { - let mut break_line = false; - if let Some(space) = node.cast::() { - if space.to_untyped().text().contains('\n') { - break_line = true; - } - } else if let Some(pb) = node.cast::() { - if pb.to_untyped().text().contains('\n') { - break_line = true; - } - } else if node.kind().is_stmt() { - break_line = true; - } else if let Some(expr) = node.cast::() { - match expr { - ast::Expr::Text(_) => current_line.has_text = true, - ast::Expr::Raw(r) => { - if r.block() { - break_line = true; - } else { - current_line.has_text = true; - } - } - ast::Expr::Strong(_) | ast::Expr::Emph(_) => current_line.has_text = true, - ast::Expr::Code(_) => break_line = true, - ast::Expr::Equation(e) if e.block() => break_line = true, - _ => (), - } - } - current_line.nodes.push(node); - if break_line { - lines.push(current_line); - current_line = Line::default(); - } - } - if !current_line.nodes.is_empty() { - lines.push(current_line); - } - lines - }; - for Line { has_text, nodes } in lines { - for node in nodes { - if let Some(space) = node.cast::() { - doc = doc.append(self.convert_space(space)); - continue; - } - if let Some(pb) = node.cast::() { - doc = doc.append(self.convert_parbreak(pb)); - continue; - } - if has_text { - doc = doc.append(self.format_disabled(node)); - } else if let Some(expr) = node.cast::() { - let expr_doc = self.convert_expr(expr); - doc = doc.append(expr_doc); - } else { - doc = doc.append(trivia(node)); - } - } - } - doc - } - - fn check_disabled<'a>(&'a self, node: &'a SyntaxNode) -> Option> { - if self.disabled_nodes.contains(node) { - Some(self.format_disabled(node)) - } else { - None - } - } - - fn format_disabled<'a>(&'a self, node: &'a SyntaxNode) -> BoxDoc<'a, ()> { - return BoxDoc::text(node.clone().into_text().to_string()); - } - - fn convert_expr<'a>(&'a self, expr: Expr<'a>) -> BoxDoc<'a, ()> { - if let Some(res) = self.check_disabled(expr.to_untyped()) { - return res; - } - match expr { - ast::Expr::Text(t) => self.convert_text(t), - ast::Expr::Space(s) => self.convert_space(s), - ast::Expr::Linebreak(b) => self.convert_linebreak(b), - ast::Expr::Parbreak(b) => self.convert_parbreak(b), - ast::Expr::Escape(e) => self.convert_escape(e), - ast::Expr::Shorthand(s) => self.convert_shorthand(s), - ast::Expr::SmartQuote(s) => self.convert_smart_quote(s), - ast::Expr::Strong(s) => self.convert_strong(s), - ast::Expr::Emph(e) => self.convert_emph(e), - ast::Expr::Raw(r) => self.convert_raw(r), - ast::Expr::Link(l) => self.convert_link(l), - ast::Expr::Label(l) => self.convert_label(l), - ast::Expr::Ref(r) => self.convert_ref(r), - ast::Expr::Heading(h) => self.convert_heading(h), - ast::Expr::List(l) => self.convert_list_item(l), - ast::Expr::Enum(e) => self.convert_enum_item(e), - ast::Expr::Term(t) => self.convert_term_item(t), - ast::Expr::Equation(e) => self.convert_equation(e), - ast::Expr::Math(m) => self.convert_math(m), - ast::Expr::MathIdent(mi) => trivia(mi.to_untyped()), - ast::Expr::MathAlignPoint(map) => trivia(map.to_untyped()), - ast::Expr::MathDelimited(md) => self.convert_math_delimited(md), - ast::Expr::MathAttach(ma) => self.convert_math_attach(ma), - ast::Expr::MathPrimes(mp) => self.convert_math_primes(mp), - ast::Expr::MathFrac(mf) => self.convert_math_frac(mf), - ast::Expr::MathRoot(mr) => self.convert_math_root(mr), - ast::Expr::Ident(i) => self.convert_ident(i), - ast::Expr::None(n) => self.convert_none(n), - ast::Expr::Auto(a) => self.convert_auto(a), - ast::Expr::Bool(b) => self.convert_bool(b), - ast::Expr::Int(i) => self.convert_int(i), - ast::Expr::Float(f) => self.convert_float(f), - ast::Expr::Numeric(n) => self.convert_numeric(n), - ast::Expr::Str(s) => self.convert_str(s), - ast::Expr::Code(c) => self.convert_code_block(c), - ast::Expr::Content(c) => self.convert_content_block(c), - ast::Expr::Parenthesized(p) => self.convert_parenthesized(p), - ast::Expr::Array(a) => self.convert_array(a), - ast::Expr::Dict(d) => self.convert_dict(d), - ast::Expr::Unary(u) => self.convert_unary(u), - ast::Expr::Binary(b) => self.convert_binary(b), - ast::Expr::FieldAccess(fa) => self.convert_field_access(fa), - ast::Expr::FuncCall(fc) => self.convert_func_call(fc), - ast::Expr::Closure(c) => self.convert_closure(c), - ast::Expr::Let(l) => self.convert_let_binding(l), - ast::Expr::DestructAssign(da) => self.convert_destruct_assignment(da), - ast::Expr::Set(s) => self.convert_set_rule(s), - ast::Expr::Show(s) => self.convert_show_rule(s), - ast::Expr::Conditional(c) => self.convert_conditional(c), - ast::Expr::While(w) => self.convert_while(w), - ast::Expr::For(f) => self.convert_for(f), - ast::Expr::Import(i) => self.convert_import(i), - ast::Expr::Include(i) => self.convert_include(i), - ast::Expr::Break(b) => self.convert_break(b), - ast::Expr::Continue(c) => self.convert_continue(c), - ast::Expr::Return(r) => self.convert_return(r), - ast::Expr::Contextual(c) => self.convert_contextual(c), - } - .group() - } - - fn convert_text<'a>(&'a self, text: Text<'a>) -> BoxDoc<'a, ()> { - let node = text.to_untyped(); - trivia(node) - } - - fn convert_space<'a>(&'a self, space: Space<'a>) -> BoxDoc<'a, ()> { - let node = space.to_untyped(); - if node.text().contains('\n') { - BoxDoc::hardline() - } else { - BoxDoc::space() - } - } - - fn convert_linebreak<'a>(&'a self, linebreak: Linebreak<'a>) -> BoxDoc<'a, ()> { - let node = linebreak.to_untyped(); - trivia(node) - } - - fn convert_parbreak<'a>(&'a self, _parbreak: Parbreak<'a>) -> BoxDoc<'a, ()> { - BoxDoc::hardline().append(BoxDoc::hardline()) - } - - fn convert_escape<'a>(&'a self, escape: Escape<'a>) -> BoxDoc<'a, ()> { - let node = escape.to_untyped(); - trivia(node) - } - - fn convert_shorthand<'a>(&'a self, shorthand: Shorthand<'a>) -> BoxDoc<'a, ()> { - let node = shorthand.to_untyped(); - trivia(node) - } - - fn convert_smart_quote<'a>(&'a self, smart_quote: SmartQuote<'a>) -> BoxDoc<'a, ()> { - let node = smart_quote.to_untyped(); - trivia(node) - } - - fn convert_strong<'a>(&'a self, strong: Strong<'a>) -> BoxDoc<'a, ()> { - let body = self.convert_markup(strong.body()); - BoxDoc::text("*").append(body).append(BoxDoc::text("*")) - } - - fn convert_emph<'a>(&'a self, emph: Emph<'a>) -> BoxDoc<'a, ()> { - let body = self.convert_markup(emph.body()); - BoxDoc::text("_").append(body).append(BoxDoc::text("_")) - } - - fn convert_raw<'a>(&'a self, raw: Raw<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil(); - let is_block = raw.block(); - let has_lang = raw.lang().is_some(); - let mut is_opening = true; - for child in raw.to_untyped().children() { - if let Some(delim) = child.cast::() { - doc = doc.append(trivia(delim.to_untyped())); - if is_block && !has_lang && is_opening { - doc = doc.append(BoxDoc::hardline()); - is_opening = false; - } - } - if let Some(lang) = child.cast::() { - doc = doc.append(trivia(lang.to_untyped())); - doc = doc.append(if is_block { - BoxDoc::hardline() - } else { - BoxDoc::space() - }); - } - if let Some(line) = child.cast::() { - doc = doc.append(trivia(line.to_untyped())); - if is_block { - doc = doc.append(BoxDoc::hardline()); - } - } - } - doc - } - - fn convert_link<'a>(&'a self, link: Link<'a>) -> BoxDoc<'a, ()> { - let node = link.to_untyped(); - trivia(node) - } - - fn convert_label<'a>(&'a self, label: Label<'a>) -> BoxDoc<'a, ()> { - let node = label.to_untyped(); - trivia(node) - } - - fn convert_ref<'a>(&'a self, reference: Ref<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::text("@"); - doc = doc.append(BoxDoc::text(reference.target())); - if let Some(supplement) = reference.supplement() { - doc = doc.append(self.convert_content_block(supplement)); - } - doc - } - - fn convert_heading<'a>(&'a self, heading: Heading<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::text("=".repeat(heading.depth().into())); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_markup(heading.body())); - doc - } - - fn convert_list_item<'a>(&'a self, list_item: ListItem<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::text("-"); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_markup(list_item.body()).nest(2)); - doc - } - - fn convert_enum_item<'a>(&'a self, enum_item: EnumItem<'a>) -> BoxDoc<'a, ()> { - let mut doc = if let Some(number) = enum_item.number() { - BoxDoc::text(format!("{number}.")) - } else { - BoxDoc::text("+") - }; - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_markup(enum_item.body()).nest(2)); - doc - } - - fn convert_term_item<'a>(&'a self, term: TermItem<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::text("/"); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_markup(term.term())); - doc = doc.append(BoxDoc::text(":")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_markup(term.description()).nest(2)); - doc - } - - fn convert_equation<'a>(&'a self, equation: Equation<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::text("$"); - if equation.block() { - doc = doc.append(BoxDoc::line()); - } - doc = doc.append(self.convert_math(equation.body()).nest(2)); - if equation.block() { - doc = doc.append(BoxDoc::line()); - } - doc = doc.append(BoxDoc::text("$")); - doc - } - - fn convert_math<'a>(&'a self, math: Math<'a>) -> BoxDoc<'a, ()> { - let mut doc: BoxDoc<()> = BoxDoc::nil(); - for node in math.to_untyped().children() { - if let Some(expr) = node.cast::() { - let expr_doc = self.convert_expr(expr); - doc = doc.append(expr_doc); - } else if let Some(space) = node.cast::() { - doc = doc.append(self.convert_space(space)); - } else { - doc = doc.append(trivia(node)); - } - } - doc - } - - fn convert_ident<'a>(&'a self, ident: Ident<'a>) -> BoxDoc<'a, ()> { - let doc = BoxDoc::nil().append(BoxDoc::text(ident.as_str())); - doc - } - - fn convert_none<'a>(&'a self, _none: None<'a>) -> BoxDoc<'a, ()> { - BoxDoc::nil().append(BoxDoc::text("none")) - } - - fn convert_auto<'a>(&'a self, _auto: Auto<'a>) -> BoxDoc<'a, ()> { - BoxDoc::nil().append(BoxDoc::text("auto")) - } - - fn convert_bool<'a>(&'a self, boolean: Bool<'a>) -> BoxDoc<'a, ()> { - let node = boolean.to_untyped(); - trivia(node) - } - - fn convert_int<'a>(&'a self, int: Int<'a>) -> BoxDoc<'a, ()> { - let node = int.to_untyped(); - trivia(node) - } - - fn convert_float<'a>(&'a self, float: Float<'a>) -> BoxDoc<'a, ()> { - let node = float.to_untyped(); - trivia(node) - } - - fn convert_numeric<'a>(&'a self, numeric: Numeric<'a>) -> BoxDoc<'a, ()> { - let node = numeric.to_untyped(); - trivia(node) - } - - fn convert_str<'a>(&'a self, str: Str<'a>) -> BoxDoc<'a, ()> { - let node = str.to_untyped(); - trivia(node) - } - - fn convert_code_block<'a>(&'a self, code_block: CodeBlock<'a>) -> BoxDoc<'a, ()> { - let mut codes: Vec<_> = vec![]; - for node in code_block.to_untyped().children() { - if let Some(code) = node.cast::() { - let code_doc = self.convert_code(code); - codes.extend(code_doc); - } else if node.kind() == SyntaxKind::LineComment - || node.kind() == SyntaxKind::BlockComment - { - codes.push(to_doc(std::borrow::Cow::Borrowed(node.text()), true)); - } - } - let doc = pretty_items( - &codes, - BoxDoc::text(";").append(BoxDoc::space()), - BoxDoc::nil(), - (BoxDoc::text("{"), BoxDoc::text("}")), - true, - util::FoldStyle::Never, - ); - doc - } - - fn convert_code<'a>(&'a self, code: Code<'a>) -> Vec> { - let mut codes: Vec<_> = vec![]; - for node in code.to_untyped().children() { - if let Some(expr) = node.cast::() { - let expr_doc = self.convert_expr(expr); - codes.push(expr_doc); - } else if node.kind() == SyntaxKind::LineComment - || node.kind() == SyntaxKind::BlockComment - { - codes.push(to_doc(std::borrow::Cow::Borrowed(node.text()), true)); - } else if node.kind() == SyntaxKind::Space { - let newline_cnt = node.text().chars().filter(|c| *c == '\n').count(); - for _ in 0..newline_cnt.saturating_sub(1) { - codes.push(BoxDoc::nil()); - } - } - } - codes - } - - fn convert_content_block<'a>(&'a self, content_block: ContentBlock<'a>) -> BoxDoc<'a, ()> { - let content = self.convert_markup(content_block.body()).group().nest(2); - let doc = BoxDoc::text("[").append(content).append(BoxDoc::text("]")); - doc - } - - fn convert_parenthesized<'a>(&'a self, parenthesized: Parenthesized<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::text("("); - let inner = self.convert_expr(parenthesized.expr()); - let multiline_expr = BoxDoc::line() - .append(inner.clone()) - .append(BoxDoc::line()) - .nest(2) - .group(); - let singleline_expr = inner; - doc = doc.append(multiline_expr.flat_alt(singleline_expr)); - doc = doc.append(BoxDoc::text(")")); - doc - } - - fn convert_array<'a>(&'a self, array: Array<'a>) -> BoxDoc<'a, ()> { - let array_items = array - .items() - .map(|item| self.convert_array_item(item)) - .collect_vec(); - if array_items.len() == 1 { - let singleline = BoxDoc::text("(") - .append(array_items[0].clone()) - .append(BoxDoc::text(",")) - .append(BoxDoc::text(")")); - let multiline = BoxDoc::text("(") - .append( - BoxDoc::hardline() - .append(array_items[0].clone()) - .append(BoxDoc::text(",")) - .nest(2), - ) - .append(BoxDoc::hardline()) - .append(BoxDoc::text(")")) - .group(); - multiline.flat_alt(singleline) - } else { - pretty_items( - &array_items, - BoxDoc::text(",").append(BoxDoc::space()), - BoxDoc::text(","), - (BoxDoc::text("("), BoxDoc::text(")")), - false, - util::FoldStyle::Fit, - ) - } - } - - fn convert_array_item<'a>(&'a self, array_item: ArrayItem<'a>) -> BoxDoc<'a, ()> { - let doc = match array_item { - ArrayItem::Pos(p) => self.convert_expr(p), - ArrayItem::Spread(s) => self.convert_spread(s), - }; - doc - } - - fn convert_dict<'a>(&'a self, dict: Dict<'a>) -> BoxDoc<'a, ()> { - if dict.items().count() == 0 { - return BoxDoc::text("(:)"); - } - let dict_items = dict - .items() - .map(|item| self.convert_dict_item(item)) - .collect_vec(); - pretty_items( - &dict_items, - BoxDoc::text(",").append(BoxDoc::space()), - BoxDoc::text(","), - (BoxDoc::text("("), BoxDoc::text(")")), - false, - util::FoldStyle::Fit, - ) - } - - fn convert_dict_item<'a>(&'a self, dict_item: DictItem<'a>) -> BoxDoc<'a, ()> { - match dict_item { - DictItem::Named(n) => self.convert_named(n), - DictItem::Keyed(k) => self.convert_keyed(k), - DictItem::Spread(s) => self.convert_spread(s), - } - } - - fn convert_named<'a>(&'a self, named: Named<'a>) -> BoxDoc<'a, ()> { - if let Some(res) = self.check_disabled(named.to_untyped()) { - return res; - } - // TODO: better handling hash # - let has_hash = named - .to_untyped() - .children() - .any(|node| matches!(node.kind(), SyntaxKind::Hash)); - let mut doc = self.convert_ident(named.name()); - doc = doc.append(BoxDoc::text(":")); - doc = doc.append(BoxDoc::space()); - if has_hash { - doc = doc.append(BoxDoc::text("#")); - } - doc = doc.append(self.convert_expr(named.expr())); - doc - } - - fn convert_keyed<'a>(&'a self, keyed: Keyed<'a>) -> BoxDoc<'a, ()> { - let mut doc = self.convert_expr(keyed.key()); - doc = doc.append(BoxDoc::text(":")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_expr(keyed.expr())); - doc - } - - fn convert_unary<'a>(&'a self, unary: Unary<'a>) -> BoxDoc<'a, ()> { - let op_text = match unary.op() { - UnOp::Pos => "+", - UnOp::Neg => "-", - UnOp::Not => "not ", - }; - BoxDoc::text(op_text).append(self.convert_expr(unary.expr())) - } - - fn convert_binary<'a>(&'a self, binary: Binary<'a>) -> BoxDoc<'a, ()> { - BoxDoc::nil() - .append(self.convert_expr(binary.lhs())) - .append(BoxDoc::space()) - .append(BoxDoc::text(binary.op().as_str())) - .append(BoxDoc::space()) - .append(self.convert_expr(binary.rhs())) - } - - fn convert_field_access<'a>(&'a self, field_access: FieldAccess<'a>) -> BoxDoc<'a, ()> { - let left = BoxDoc::nil().append(self.convert_expr(field_access.target())); - let singleline_right = BoxDoc::text(".").append(self.convert_ident(field_access.field())); - let _multiline_right = BoxDoc::hardline() - .append(BoxDoc::text(".")) - .append(self.convert_ident(field_access.field())) - .nest(2) - .group(); - // TODO: typst doesn't support this - // left.append(multiline_right.flat_alt(singleline_right)) - left.append(singleline_right) - } - - fn convert_func_call<'a>(&'a self, func_call: FuncCall<'a>) -> BoxDoc<'a, ()> { - let doc = BoxDoc::nil().append(self.convert_expr(func_call.callee())); - if let Some(res) = self.check_disabled(func_call.args().to_untyped()) { - return doc.append(res); - } - let has_parenthesized_args = func_call - .args() - .to_untyped() - .children() - .any(|node| matches!(node.kind(), SyntaxKind::LeftParen | SyntaxKind::RightParen)); - let parenthesized_args = if has_parenthesized_args { - let args = self.convert_parenthesized_args(func_call.args()); - pretty_items( - &args, - BoxDoc::text(",").append(BoxDoc::space()), - BoxDoc::text(","), - (BoxDoc::text("("), BoxDoc::text(")")), - false, - util::FoldStyle::Fit, - ) - } else { - BoxDoc::nil() - }; - let doc = doc - .append(parenthesized_args) - .append(self.convert_additional_args(func_call.args(), has_parenthesized_args)); - doc - } - - fn convert_parenthesized_args<'a>(&'a self, args: Args<'a>) -> Vec> { - let node = args.to_untyped(); - let args = node - .children() - .take_while(|node| node.kind() != SyntaxKind::RightParen) - .filter_map(|node| node.cast::<'_, Arg>()) - .map(|arg| self.convert_arg(arg)) - .collect(); - args - } - - fn convert_additional_args<'a>(&'a self, args: Args<'a>, has_paren: bool) -> BoxDoc<'a, ()> { - let node = args.to_untyped(); - let args = node - .children() - .skip_while(|node| { - if has_paren { - node.kind() != SyntaxKind::RightParen - } else { - node.kind() != SyntaxKind::ContentBlock - } - }) - .filter_map(|node| node.cast::<'_, Arg>()); - BoxDoc::concat(args.map(|arg| self.convert_arg(arg))).group() - } - - fn convert_arg<'a>(&'a self, arg: Arg<'a>) -> BoxDoc<'a, ()> { - match arg { - Arg::Pos(p) => self.convert_expr(p), - Arg::Named(n) => self.convert_named(n), - Arg::Spread(s) => self.convert_spread(s), - } - } - - fn convert_closure<'a>(&'a self, closure: Closure<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil(); - let params = self.convert_params(closure.params()); - let arg_list = pretty_items( - ¶ms, - BoxDoc::text(",").append(BoxDoc::space()), - BoxDoc::text(","), - (BoxDoc::text("("), BoxDoc::text(")")), - false, - util::FoldStyle::Fit, - ); - if let Some(name) = closure.name() { - doc = doc.append(self.convert_ident(name)); - doc = doc.append(arg_list); - doc = doc.append(BoxDoc::space()); - doc = doc.append(BoxDoc::text("=")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_expr(closure.body())); - } else { - if params.len() == 1 - && matches!(closure.params().children().next().unwrap(), Param::Pos(_)) - && !matches!( - closure.params().children().next().unwrap(), - Param::Pos(Pattern::Destructuring(_)) - ) - { - doc = params[0].clone(); - } else { - doc = arg_list - } - doc = doc.append(BoxDoc::space()); - doc = doc.append(BoxDoc::text("=>")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_expr(closure.body())); - } - doc - } - - fn convert_params<'a>(&'a self, params: Params<'a>) -> Vec> { - params - .children() - .map(|param| self.convert_param(param)) - .collect() - } - - fn convert_param<'a>(&'a self, param: Param<'a>) -> BoxDoc<'a, ()> { - match param { - Param::Pos(p) => self.convert_pattern(p), - Param::Named(n) => self.convert_named(n), - Param::Spread(s) => self.convert_spread(s), - } - } - - fn convert_spread<'a>(&'a self, spread: Spread<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::text(".."); - let ident = if let Some(id) = spread.sink_ident() { - self.convert_ident(id) - } else if let Some(expr) = spread.sink_expr() { - self.convert_expr(expr) - } else { - BoxDoc::nil() - }; - doc = doc.append(ident); - doc - } - - fn convert_pattern<'a>(&'a self, pattern: Pattern<'a>) -> BoxDoc<'a, ()> { - match pattern { - Pattern::Normal(n) => self.convert_expr(n), - Pattern::Placeholder(p) => self.convert_underscore(p), - Pattern::Destructuring(d) => self.convert_destructuring(d), - Pattern::Parenthesized(p) => self.convert_parenthesized(p), - } - } - - fn convert_underscore<'a>(&'a self, _underscore: Underscore<'a>) -> BoxDoc<'a, ()> { - BoxDoc::text("_") - } - - fn convert_destructuring<'a>(&'a self, destructuring: Destructuring<'a>) -> BoxDoc<'a, ()> { - BoxDoc::text("(") - .append(BoxDoc::intersperse( - destructuring - .items() - .map(|item| self.convert_destructuring_item(item)), - BoxDoc::text(",").append(BoxDoc::line()), - )) - .append(BoxDoc::text(")")) - } - - fn convert_destructuring_item<'a>( - &'a self, - destructuring_item: DestructuringItem<'a>, - ) -> BoxDoc<'a, ()> { - match destructuring_item { - DestructuringItem::Spread(s) => self.convert_spread(s), - DestructuringItem::Named(n) => self.convert_named(n), - DestructuringItem::Pattern(p) => self.convert_pattern(p), - } - } - - fn convert_let_binding<'a>(&'a self, let_binding: LetBinding<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil() - .append(BoxDoc::text("let")) - .append(BoxDoc::space()); - match let_binding.kind() { - LetBindingKind::Normal(n) => { - doc = doc.append(self.convert_pattern(n).group()); - if let Some(expr) = let_binding.init() { - doc = doc.append(BoxDoc::space()); - doc = doc.append(BoxDoc::text("=")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_expr(expr)); - } - } - LetBindingKind::Closure(_c) => { - if let Some(c) = let_binding.init() { - doc = doc.append(self.convert_expr(c)); - } - } - } - doc - } - - fn convert_destruct_assignment<'a>( - &'a self, - destruct_assign: DestructAssignment<'a>, - ) -> BoxDoc<'a, ()> { - self.convert_pattern(destruct_assign.pattern()) - .append(BoxDoc::space()) - .append(BoxDoc::text("=")) - .append(BoxDoc::space()) - .append(self.convert_expr(destruct_assign.value())) - } - - fn convert_set_rule<'a>(&'a self, set_rule: SetRule<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil() - .append(BoxDoc::text("set")) - .append(BoxDoc::space()); - doc = doc.append(self.convert_expr(set_rule.target())); - doc = doc.append(pretty_items( - &self.convert_parenthesized_args(set_rule.args()), - BoxDoc::text(",").append(BoxDoc::space()), - BoxDoc::text(","), - (BoxDoc::text("("), BoxDoc::text(")")), - false, - util::FoldStyle::Single, - )); - if let Some(condition) = set_rule.condition() { - doc = doc.append(BoxDoc::space()); - doc = doc.append(BoxDoc::text("if")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_expr(condition)); - } - doc - } - - fn convert_show_rule<'a>(&'a self, show_rule: ShowRule<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil().append(BoxDoc::text("show")); - if let Some(selector) = show_rule.selector() { - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_expr(selector)); - } - doc = doc.append(BoxDoc::text(":")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_expr(show_rule.transform())); - doc - } - - fn convert_conditional<'a>(&'a self, conditional: Conditional<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil(); - enum CastType { - Condition, - Then, - Else, - } - let has_else = conditional.else_body().is_some(); - let mut expr_type = CastType::Condition; - for child in conditional.to_untyped().children() { - if child.kind() == SyntaxKind::If { - doc = doc.append(BoxDoc::text("if")); - doc = doc.append(BoxDoc::space()); - } else if child.kind() == SyntaxKind::Else { - doc = doc.append(BoxDoc::text("else")); - doc = doc.append(BoxDoc::space()); - } else if child.kind() == SyntaxKind::BlockComment { - doc = doc.append(trivia(child)); - doc = doc.append(BoxDoc::space()); - } else if child.kind() == SyntaxKind::LineComment { - doc = doc.append(trivia(child)); - doc = doc.append(BoxDoc::hardline()); - } else { - match expr_type { - CastType::Condition => { - if let Some(condition) = child.cast() { - doc = doc.append(self.convert_expr(condition)); - doc = doc.append(BoxDoc::space()); - expr_type = CastType::Then; - } - } - CastType::Then => { - if let Some(then_expr) = child.cast() { - doc = doc.append(self.convert_expr(then_expr).group()); - if has_else { - expr_type = CastType::Else; - doc = doc.append(BoxDoc::space()); - } - } - } - CastType::Else => { - if let Some(else_expr) = child.cast() { - doc = doc.append(self.convert_expr(else_expr).group()); - } - } - } - } - } - doc - } - - fn convert_while<'a>(&'a self, while_loop: WhileLoop<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil(); - #[derive(Debug, PartialEq)] - enum CastType { - Condition, - Body, - } - let mut expr_type = CastType::Condition; - for child in while_loop.to_untyped().children() { - if child.kind() == SyntaxKind::While { - doc = doc.append(BoxDoc::text("while")); - doc = doc.append(BoxDoc::space()); - } else if child.kind() == SyntaxKind::BlockComment { - doc = doc.append(trivia(child)); - doc = doc.append(BoxDoc::space()); - } else if child.kind() == SyntaxKind::LineComment { - doc = doc.append(trivia(child)); - doc = doc.append(BoxDoc::hardline()); - } else if let Some(expr) = child.cast() { - doc = doc.append(self.convert_expr(expr)); - if expr_type == CastType::Condition { - doc = doc.append(BoxDoc::space()); - expr_type = CastType::Body; - } - } - } - doc - } - - fn convert_for<'a>(&'a self, for_loop: ForLoop<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil(); - enum CastType { - Pattern, - Iter, - Body, - } - let mut expr_type = CastType::Pattern; - for child in for_loop.to_untyped().children() { - if child.kind() == SyntaxKind::For { - doc = doc.append(BoxDoc::text("for")); - doc = doc.append(BoxDoc::space()); - } else if child.kind() == SyntaxKind::In { - doc = doc.append(BoxDoc::text("in")); - doc = doc.append(BoxDoc::space()); - } else if child.kind() == SyntaxKind::BlockComment { - doc = doc.append(trivia(child)); - doc = doc.append(BoxDoc::space()); - } else if child.kind() == SyntaxKind::LineComment { - doc = doc.append(trivia(child)); - doc = doc.append(BoxDoc::hardline()); - } else { - match expr_type { - CastType::Pattern => { - if let Some(pattern) = child.cast() { - doc = doc.append(self.convert_pattern(pattern)); - doc = doc.append(BoxDoc::space()); - expr_type = CastType::Iter; - } - } - CastType::Iter => { - if let Some(iter) = child.cast() { - doc = doc.append(self.convert_expr(iter)); - doc = doc.append(BoxDoc::space()); - expr_type = CastType::Body; - } - } - CastType::Body => { - if let Some(body) = child.cast() { - doc = doc.append(self.convert_expr(body)); - } - } - } - } - } - doc - } - - fn convert_import<'a>(&'a self, import: ModuleImport<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil().append(BoxDoc::text("import")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_expr(import.source())); - if let Some(imports) = import.imports() { - doc = doc.append(BoxDoc::text(":")); - doc = doc.append(BoxDoc::space()); - let imports = match imports { - Imports::Wildcard => BoxDoc::text("*"), - Imports::Items(i) => BoxDoc::intersperse( - i.iter().map(|item| self.convert_import_item(item)), - BoxDoc::text(",").append(BoxDoc::line()), - ), - }; - doc = doc.append(imports.group()); - } - if let Some(new_name) = import.new_name() { - doc = doc.append(BoxDoc::space()); - doc = doc.append(BoxDoc::text("as")); - doc = doc.append(BoxDoc::space()); - doc = doc.append(self.convert_ident(new_name)); - } - doc - } - - fn convert_import_item<'a>(&'a self, import_item: ImportItem<'a>) -> BoxDoc<'a, ()> { - match import_item { - ImportItem::Simple(s) => self.convert_ident(s), - ImportItem::Renamed(r) => self - .convert_ident(r.original_name()) - .append(BoxDoc::space()) - .append(BoxDoc::text("as")) - .append(BoxDoc::space()) - .append(self.convert_ident(r.new_name())), - } - } - - fn convert_include<'a>(&'a self, include: ModuleInclude<'a>) -> BoxDoc<'a, ()> { - BoxDoc::nil() - .append(BoxDoc::text("include")) - .append(BoxDoc::space()) - .append(self.convert_expr(include.source())) - } - - fn convert_break<'a>(&'a self, _break: LoopBreak<'a>) -> BoxDoc<'a, ()> { - BoxDoc::nil().append(BoxDoc::text("break")) - } - - fn convert_continue<'a>(&'a self, _continue: LoopContinue<'a>) -> BoxDoc<'a, ()> { - BoxDoc::nil().append(BoxDoc::text("continue")) - } - - fn convert_return<'a>(&'a self, return_stmt: FuncReturn<'a>) -> BoxDoc<'a, ()> { - let mut doc = BoxDoc::nil() - .append(BoxDoc::text("return")) - .append(BoxDoc::space()); - if let Some(body) = return_stmt.body() { - doc = doc.append(self.convert_expr(body)); - } - doc - } - - fn convert_math_delimited<'a>(&'a self, math_delimited: MathDelimited<'a>) -> BoxDoc<'a, ()> { - let open = self.convert_expr(math_delimited.open()); - let close = self.convert_expr(math_delimited.close()); - let body = self.convert_math(math_delimited.body()); - let singleline = open.clone().append(body.clone()).append(close.clone()); - let multiline = open - .append(BoxDoc::hardline()) - .append(body) - .append(BoxDoc::hardline()) - .nest(2) - .append(close); - multiline.flat_alt(singleline) - } - - fn convert_math_attach<'a>(&'a self, math_attach: MathAttach<'a>) -> BoxDoc<'a, ()> { - let mut doc = self.convert_expr(math_attach.base()); - let prime_index = math_attach - .to_untyped() - .children() - .enumerate() - .skip_while(|(_i, node)| node.cast::>().is_none()) - .nth(1) - .filter(|(_i, n)| n.cast::().is_some()) - .map(|(i, _n)| i); - - let bottom_index = math_attach - .to_untyped() - .children() - .enumerate() - .skip_while(|(_i, node)| !matches!(node.kind(), SyntaxKind::Underscore)) - .find_map(|(i, n)| SyntaxNode::cast::>(n).map(|n| (i, n))) - .map(|(i, _n)| i); - - let top_index = math_attach - .to_untyped() - .children() - .enumerate() - .skip_while(|(_i, node)| !matches!(node.kind(), SyntaxKind::Hat)) - .find_map(|(i, n)| SyntaxNode::cast::>(n).map(|n| (i, n))) - .map(|(i, _n)| i); - - #[derive(Debug)] - enum IndexType { - Prime, - Bottom, - Top, - } - - let mut index_types = [IndexType::Prime, IndexType::Bottom, IndexType::Top]; - index_types.sort_by_key(|index_type| match index_type { - IndexType::Prime => prime_index, - IndexType::Bottom => bottom_index, - IndexType::Top => top_index, - }); - - for index in index_types { - match index { - IndexType::Prime => { - if let Some(primes) = math_attach.primes() { - doc = doc.append(self.convert_math_primes(primes)); - } - } - IndexType::Bottom => { - if let Some(bottom) = math_attach.bottom() { - doc = doc.append(BoxDoc::text("_")); - doc = doc.append(self.convert_expr(bottom)); - } - } - IndexType::Top => { - if let Some(top) = math_attach.top() { - doc = doc.append(BoxDoc::text("^")); - doc = doc.append(self.convert_expr(top)); - } - } - } - } - doc - } - - fn convert_math_primes<'a>(&'a self, math_primes: MathPrimes<'a>) -> BoxDoc<'a, ()> { - BoxDoc::text("'".repeat(math_primes.count())) - } - - fn convert_math_frac<'a>(&'a self, math_frac: MathFrac<'a>) -> BoxDoc<'a, ()> { - let singleline = self - .convert_expr(math_frac.num()) - .append(BoxDoc::space()) - .append(BoxDoc::text("/")) - .append(BoxDoc::space()) - .append(self.convert_expr(math_frac.denom())); - // TODO: add multiline version - singleline - } - - fn convert_math_root<'a>(&'a self, math_root: MathRoot<'a>) -> BoxDoc<'a, ()> { - let sqrt_sym = if let Some(index) = math_root.index() { - if index == 3 { - BoxDoc::text("∛") - } else if index == 4 { - BoxDoc::text("∜") - } else { - // TODO: actually unreachable - BoxDoc::text("√") - } - } else { - BoxDoc::text("√") - }; - sqrt_sym.append(self.convert_expr(math_root.radicand())) - } - - fn convert_contextual<'a>(&'a self, ctx: Contextual<'a>) -> BoxDoc<'a, ()> { - let body = self.convert_expr(ctx.body()); - BoxDoc::text("context").append(BoxDoc::space()).append(body) - } -} - -fn trivia(node: &SyntaxNode) -> BoxDoc<'_, ()> { - to_doc(std::borrow::Cow::Borrowed(node.text()), false) -} - -pub fn to_doc(s: Cow<'_, str>, strip_prefix: bool) -> BoxDoc<'_, ()> { - let get_line = |s: &str| { - if strip_prefix { - s.trim_start().to_string() - } else { - s.to_string() - } - }; - // String::lines() doesn't include the trailing newline - let has_trailing_newline = s.ends_with('\n'); - let res = BoxDoc::intersperse( - s.lines().map(|s| BoxDoc::text(get_line(s))), - BoxDoc::hardline(), - ); - if has_trailing_newline { - res.append(BoxDoc::hardline()) - } else { - res - } -} - -#[cfg(test)] -mod tests { - use typst_syntax::parse; - - use super::*; - - #[test] - fn test_to_doc() { - let tests = [ - "command can take a directory as an argument to use as the book", - "123\n456\n789", - "123\n4567\n789\n", - "123\n4568\n789\n", - ]; - for test in tests.into_iter() { - insta::assert_debug_snapshot!(to_doc(test.into(), false)); - } - } - - #[test] - fn convert_markup() { - let tests = [r"=== --open - -When you use the `--open` flag, typst-book will open the rendered book in -your default web browser after building it."]; - for test in tests.into_iter() { - let root = parse(test); - insta::assert_debug_snapshot!(root); - let markup = root.cast().unwrap(); - let printer = PrettyPrinter::default(); - let doc = printer.convert_markup(markup); - insta::assert_debug_snapshot!(doc.pretty(120).to_string()); - } - } - - #[test] - fn convert_func_call() { - let tests = [r#"#link("http://example.com")[test]"#]; - for test in tests.into_iter() { - let root = parse(test); - insta::assert_debug_snapshot!(root); - let markup = root.cast().unwrap(); - let printer = PrettyPrinter::default(); - let doc = printer.convert_markup(markup); - insta::assert_debug_snapshot!(doc.pretty(120).to_string()); - } - } -} +use std::borrow::Cow; +use std::collections::HashSet; + +use itertools::Itertools; +use pretty::BoxDoc; +use typst_syntax::{ast, SyntaxNode}; +use typst_syntax::{ast::*, SyntaxKind}; + +use crate::util::{self, pretty_items}; + +pub mod markup; + +#[derive(Debug, Default)] +pub struct PrettyPrinter { + disabled_nodes: HashSet, +} + +impl PrettyPrinter { + pub fn new(disabled_nodes: HashSet) -> Self { + Self { disabled_nodes } + } +} + +impl PrettyPrinter { + fn check_disabled<'a>(&'a self, node: &'a SyntaxNode) -> Option> { + if self.disabled_nodes.contains(node) { + Some(self.format_disabled(node)) + } else { + None + } + } + + fn format_disabled<'a>(&'a self, node: &'a SyntaxNode) -> BoxDoc<'a, ()> { + return BoxDoc::text(node.clone().into_text().to_string()); + } + + fn convert_expr<'a>(&'a self, expr: Expr<'a>) -> BoxDoc<'a, ()> { + if let Some(res) = self.check_disabled(expr.to_untyped()) { + return res; + } + match expr { + ast::Expr::Text(t) => self.convert_text(t), + ast::Expr::Space(s) => self.convert_space(s), + ast::Expr::Linebreak(b) => self.convert_linebreak(b), + ast::Expr::Parbreak(b) => self.convert_parbreak(b), + ast::Expr::Escape(e) => self.convert_escape(e), + ast::Expr::Shorthand(s) => self.convert_shorthand(s), + ast::Expr::SmartQuote(s) => self.convert_smart_quote(s), + ast::Expr::Strong(s) => self.convert_strong(s), + ast::Expr::Emph(e) => self.convert_emph(e), + ast::Expr::Raw(r) => self.convert_raw(r), + ast::Expr::Link(l) => self.convert_link(l), + ast::Expr::Label(l) => self.convert_label(l), + ast::Expr::Ref(r) => self.convert_ref(r), + ast::Expr::Heading(h) => self.convert_heading(h), + ast::Expr::List(l) => self.convert_list_item(l), + ast::Expr::Enum(e) => self.convert_enum_item(e), + ast::Expr::Term(t) => self.convert_term_item(t), + ast::Expr::Equation(e) => self.convert_equation(e), + ast::Expr::Math(m) => self.convert_math(m), + ast::Expr::MathIdent(mi) => trivia(mi.to_untyped()), + ast::Expr::MathAlignPoint(map) => trivia(map.to_untyped()), + ast::Expr::MathDelimited(md) => self.convert_math_delimited(md), + ast::Expr::MathAttach(ma) => self.convert_math_attach(ma), + ast::Expr::MathPrimes(mp) => self.convert_math_primes(mp), + ast::Expr::MathFrac(mf) => self.convert_math_frac(mf), + ast::Expr::MathRoot(mr) => self.convert_math_root(mr), + ast::Expr::Ident(i) => self.convert_ident(i), + ast::Expr::None(n) => self.convert_none(n), + ast::Expr::Auto(a) => self.convert_auto(a), + ast::Expr::Bool(b) => self.convert_bool(b), + ast::Expr::Int(i) => self.convert_int(i), + ast::Expr::Float(f) => self.convert_float(f), + ast::Expr::Numeric(n) => self.convert_numeric(n), + ast::Expr::Str(s) => self.convert_str(s), + ast::Expr::Code(c) => self.convert_code_block(c), + ast::Expr::Content(c) => self.convert_content_block(c), + ast::Expr::Parenthesized(p) => self.convert_parenthesized(p), + ast::Expr::Array(a) => self.convert_array(a), + ast::Expr::Dict(d) => self.convert_dict(d), + ast::Expr::Unary(u) => self.convert_unary(u), + ast::Expr::Binary(b) => self.convert_binary(b), + ast::Expr::FieldAccess(fa) => self.convert_field_access(fa), + ast::Expr::FuncCall(fc) => self.convert_func_call(fc), + ast::Expr::Closure(c) => self.convert_closure(c), + ast::Expr::Let(l) => self.convert_let_binding(l), + ast::Expr::DestructAssign(da) => self.convert_destruct_assignment(da), + ast::Expr::Set(s) => self.convert_set_rule(s), + ast::Expr::Show(s) => self.convert_show_rule(s), + ast::Expr::Conditional(c) => self.convert_conditional(c), + ast::Expr::While(w) => self.convert_while(w), + ast::Expr::For(f) => self.convert_for(f), + ast::Expr::Import(i) => self.convert_import(i), + ast::Expr::Include(i) => self.convert_include(i), + ast::Expr::Break(b) => self.convert_break(b), + ast::Expr::Continue(c) => self.convert_continue(c), + ast::Expr::Return(r) => self.convert_return(r), + ast::Expr::Contextual(c) => self.convert_contextual(c), + } + .group() + } + + fn convert_text<'a>(&'a self, text: Text<'a>) -> BoxDoc<'a, ()> { + let node = text.to_untyped(); + trivia(node) + } + + fn convert_space<'a>(&'a self, space: Space<'a>) -> BoxDoc<'a, ()> { + let node = space.to_untyped(); + if node.text().contains('\n') { + BoxDoc::hardline() + } else { + BoxDoc::space() + } + } + + fn convert_linebreak<'a>(&'a self, linebreak: Linebreak<'a>) -> BoxDoc<'a, ()> { + let node = linebreak.to_untyped(); + trivia(node) + } + + fn convert_parbreak<'a>(&'a self, _parbreak: Parbreak<'a>) -> BoxDoc<'a, ()> { + BoxDoc::hardline().append(BoxDoc::hardline()) + } + + fn convert_escape<'a>(&'a self, escape: Escape<'a>) -> BoxDoc<'a, ()> { + let node = escape.to_untyped(); + trivia(node) + } + + fn convert_shorthand<'a>(&'a self, shorthand: Shorthand<'a>) -> BoxDoc<'a, ()> { + let node = shorthand.to_untyped(); + trivia(node) + } + + fn convert_smart_quote<'a>(&'a self, smart_quote: SmartQuote<'a>) -> BoxDoc<'a, ()> { + let node = smart_quote.to_untyped(); + trivia(node) + } + + fn convert_strong<'a>(&'a self, strong: Strong<'a>) -> BoxDoc<'a, ()> { + let body = self.convert_markup(strong.body()); + BoxDoc::text("*").append(body).append(BoxDoc::text("*")) + } + + fn convert_emph<'a>(&'a self, emph: Emph<'a>) -> BoxDoc<'a, ()> { + let body = self.convert_markup(emph.body()); + BoxDoc::text("_").append(body).append(BoxDoc::text("_")) + } + + fn convert_raw<'a>(&'a self, raw: Raw<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil(); + let is_block = raw.block(); + let has_lang = raw.lang().is_some(); + let mut is_opening = true; + for child in raw.to_untyped().children() { + if let Some(delim) = child.cast::() { + doc = doc.append(trivia(delim.to_untyped())); + if is_block && !has_lang && is_opening { + doc = doc.append(BoxDoc::hardline()); + is_opening = false; + } + } + if let Some(lang) = child.cast::() { + doc = doc.append(trivia(lang.to_untyped())); + doc = doc.append(if is_block { + BoxDoc::hardline() + } else { + BoxDoc::space() + }); + } + if let Some(line) = child.cast::() { + doc = doc.append(trivia(line.to_untyped())); + if is_block { + doc = doc.append(BoxDoc::hardline()); + } + } + } + doc + } + + fn convert_link<'a>(&'a self, link: Link<'a>) -> BoxDoc<'a, ()> { + let node = link.to_untyped(); + trivia(node) + } + + fn convert_label<'a>(&'a self, label: Label<'a>) -> BoxDoc<'a, ()> { + let node = label.to_untyped(); + trivia(node) + } + + fn convert_ref<'a>(&'a self, reference: Ref<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::text("@"); + doc = doc.append(BoxDoc::text(reference.target())); + if let Some(supplement) = reference.supplement() { + doc = doc.append(self.convert_content_block(supplement)); + } + doc + } + + fn convert_heading<'a>(&'a self, heading: Heading<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::text("=".repeat(heading.depth().into())); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_markup(heading.body())); + doc + } + + fn convert_list_item<'a>(&'a self, list_item: ListItem<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::text("-"); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_markup(list_item.body()).nest(2)); + doc + } + + fn convert_enum_item<'a>(&'a self, enum_item: EnumItem<'a>) -> BoxDoc<'a, ()> { + let mut doc = if let Some(number) = enum_item.number() { + BoxDoc::text(format!("{number}.")) + } else { + BoxDoc::text("+") + }; + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_markup(enum_item.body()).nest(2)); + doc + } + + fn convert_term_item<'a>(&'a self, term: TermItem<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::text("/"); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_markup(term.term())); + doc = doc.append(BoxDoc::text(":")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_markup(term.description()).nest(2)); + doc + } + + fn convert_equation<'a>(&'a self, equation: Equation<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::text("$"); + if equation.block() { + doc = doc.append(BoxDoc::line()); + } + doc = doc.append(self.convert_math(equation.body()).nest(2)); + if equation.block() { + doc = doc.append(BoxDoc::line()); + } + doc = doc.append(BoxDoc::text("$")); + doc + } + + fn convert_math<'a>(&'a self, math: Math<'a>) -> BoxDoc<'a, ()> { + let mut doc: BoxDoc<()> = BoxDoc::nil(); + for node in math.to_untyped().children() { + if let Some(expr) = node.cast::() { + let expr_doc = self.convert_expr(expr); + doc = doc.append(expr_doc); + } else if let Some(space) = node.cast::() { + doc = doc.append(self.convert_space(space)); + } else { + doc = doc.append(trivia(node)); + } + } + doc + } + + fn convert_ident<'a>(&'a self, ident: Ident<'a>) -> BoxDoc<'a, ()> { + let doc = BoxDoc::nil().append(BoxDoc::text(ident.as_str())); + doc + } + + fn convert_none<'a>(&'a self, _none: None<'a>) -> BoxDoc<'a, ()> { + BoxDoc::nil().append(BoxDoc::text("none")) + } + + fn convert_auto<'a>(&'a self, _auto: Auto<'a>) -> BoxDoc<'a, ()> { + BoxDoc::nil().append(BoxDoc::text("auto")) + } + + fn convert_bool<'a>(&'a self, boolean: Bool<'a>) -> BoxDoc<'a, ()> { + let node = boolean.to_untyped(); + trivia(node) + } + + fn convert_int<'a>(&'a self, int: Int<'a>) -> BoxDoc<'a, ()> { + let node = int.to_untyped(); + trivia(node) + } + + fn convert_float<'a>(&'a self, float: Float<'a>) -> BoxDoc<'a, ()> { + let node = float.to_untyped(); + trivia(node) + } + + fn convert_numeric<'a>(&'a self, numeric: Numeric<'a>) -> BoxDoc<'a, ()> { + let node = numeric.to_untyped(); + trivia(node) + } + + fn convert_str<'a>(&'a self, str: Str<'a>) -> BoxDoc<'a, ()> { + let node = str.to_untyped(); + trivia(node) + } + + fn convert_code_block<'a>(&'a self, code_block: CodeBlock<'a>) -> BoxDoc<'a, ()> { + let mut codes: Vec<_> = vec![]; + for node in code_block.to_untyped().children() { + if let Some(code) = node.cast::() { + let code_doc = self.convert_code(code); + codes.extend(code_doc); + } else if node.kind() == SyntaxKind::LineComment + || node.kind() == SyntaxKind::BlockComment + { + codes.push(to_doc(std::borrow::Cow::Borrowed(node.text()), true)); + } + } + let doc = pretty_items( + &codes, + BoxDoc::text(";").append(BoxDoc::space()), + BoxDoc::nil(), + (BoxDoc::text("{"), BoxDoc::text("}")), + true, + util::FoldStyle::Never, + ); + doc + } + + fn convert_code<'a>(&'a self, code: Code<'a>) -> Vec> { + let mut codes: Vec<_> = vec![]; + for node in code.to_untyped().children() { + if let Some(expr) = node.cast::() { + let expr_doc = self.convert_expr(expr); + codes.push(expr_doc); + } else if node.kind() == SyntaxKind::LineComment + || node.kind() == SyntaxKind::BlockComment + { + codes.push(to_doc(std::borrow::Cow::Borrowed(node.text()), true)); + } else if node.kind() == SyntaxKind::Space { + let newline_cnt = node.text().chars().filter(|c| *c == '\n').count(); + for _ in 0..newline_cnt.saturating_sub(1) { + codes.push(BoxDoc::nil()); + } + } + } + codes + } + + fn convert_content_block<'a>(&'a self, content_block: ContentBlock<'a>) -> BoxDoc<'a, ()> { + let content = BoxDoc::line_() + .append(self.convert_markup(content_block.body()).group()) + .group() + .nest(2) + .append(BoxDoc::line_()); + let doc = BoxDoc::text("[").append(content).append(BoxDoc::text("]")); + doc + } + + fn convert_parenthesized<'a>(&'a self, parenthesized: Parenthesized<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::text("("); + let inner = self.convert_expr(parenthesized.expr()); + let multiline_expr = BoxDoc::line() + .append(inner.clone()) + .append(BoxDoc::line()) + .nest(2) + .group(); + let singleline_expr = inner; + doc = doc.append(multiline_expr.flat_alt(singleline_expr)); + doc = doc.append(BoxDoc::text(")")); + doc + } + + fn convert_array<'a>(&'a self, array: Array<'a>) -> BoxDoc<'a, ()> { + let array_items = array + .items() + .map(|item| self.convert_array_item(item)) + .collect_vec(); + if array_items.len() == 1 { + let singleline = BoxDoc::text("(") + .append(array_items[0].clone()) + .append(BoxDoc::text(",")) + .append(BoxDoc::text(")")); + let multiline = BoxDoc::text("(") + .append( + BoxDoc::hardline() + .append(array_items[0].clone()) + .append(BoxDoc::text(",")) + .nest(2), + ) + .append(BoxDoc::hardline()) + .append(BoxDoc::text(")")) + .group(); + multiline.flat_alt(singleline) + } else { + pretty_items( + &array_items, + BoxDoc::text(",").append(BoxDoc::space()), + BoxDoc::text(","), + (BoxDoc::text("("), BoxDoc::text(")")), + false, + util::FoldStyle::Fit, + ) + } + } + + fn convert_array_item<'a>(&'a self, array_item: ArrayItem<'a>) -> BoxDoc<'a, ()> { + let doc = match array_item { + ArrayItem::Pos(p) => self.convert_expr(p), + ArrayItem::Spread(s) => self.convert_spread(s), + }; + doc + } + + fn convert_dict<'a>(&'a self, dict: Dict<'a>) -> BoxDoc<'a, ()> { + if dict.items().count() == 0 { + return BoxDoc::text("(:)"); + } + let dict_items = dict + .items() + .map(|item| self.convert_dict_item(item)) + .collect_vec(); + pretty_items( + &dict_items, + BoxDoc::text(",").append(BoxDoc::space()), + BoxDoc::text(","), + (BoxDoc::text("("), BoxDoc::text(")")), + false, + util::FoldStyle::Fit, + ) + } + + fn convert_dict_item<'a>(&'a self, dict_item: DictItem<'a>) -> BoxDoc<'a, ()> { + match dict_item { + DictItem::Named(n) => self.convert_named(n), + DictItem::Keyed(k) => self.convert_keyed(k), + DictItem::Spread(s) => self.convert_spread(s), + } + } + + fn convert_named<'a>(&'a self, named: Named<'a>) -> BoxDoc<'a, ()> { + if let Some(res) = self.check_disabled(named.to_untyped()) { + return res; + } + // TODO: better handling hash # + let has_hash = named + .to_untyped() + .children() + .any(|node| matches!(node.kind(), SyntaxKind::Hash)); + let mut doc = self.convert_ident(named.name()); + doc = doc.append(BoxDoc::text(":")); + doc = doc.append(BoxDoc::space()); + if has_hash { + doc = doc.append(BoxDoc::text("#")); + } + doc = doc.append(self.convert_expr(named.expr())); + doc + } + + fn convert_keyed<'a>(&'a self, keyed: Keyed<'a>) -> BoxDoc<'a, ()> { + let mut doc = self.convert_expr(keyed.key()); + doc = doc.append(BoxDoc::text(":")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_expr(keyed.expr())); + doc + } + + fn convert_unary<'a>(&'a self, unary: Unary<'a>) -> BoxDoc<'a, ()> { + let op_text = match unary.op() { + UnOp::Pos => "+", + UnOp::Neg => "-", + UnOp::Not => "not ", + }; + BoxDoc::text(op_text).append(self.convert_expr(unary.expr())) + } + + fn convert_binary<'a>(&'a self, binary: Binary<'a>) -> BoxDoc<'a, ()> { + BoxDoc::nil() + .append(self.convert_expr(binary.lhs())) + .append(BoxDoc::space()) + .append(BoxDoc::text(binary.op().as_str())) + .append(BoxDoc::space()) + .append(self.convert_expr(binary.rhs())) + } + + fn convert_field_access<'a>(&'a self, field_access: FieldAccess<'a>) -> BoxDoc<'a, ()> { + let left = BoxDoc::nil().append(self.convert_expr(field_access.target())); + let singleline_right = BoxDoc::text(".").append(self.convert_ident(field_access.field())); + let _multiline_right = BoxDoc::hardline() + .append(BoxDoc::text(".")) + .append(self.convert_ident(field_access.field())) + .nest(2) + .group(); + // TODO: typst doesn't support this + // left.append(multiline_right.flat_alt(singleline_right)) + left.append(singleline_right) + } + + fn convert_func_call<'a>(&'a self, func_call: FuncCall<'a>) -> BoxDoc<'a, ()> { + let doc = BoxDoc::nil().append(self.convert_expr(func_call.callee())); + if let Some(res) = self.check_disabled(func_call.args().to_untyped()) { + return doc.append(res); + } + let has_parenthesized_args = func_call + .args() + .to_untyped() + .children() + .any(|node| matches!(node.kind(), SyntaxKind::LeftParen | SyntaxKind::RightParen)); + let parenthesized_args = if has_parenthesized_args { + let args = self.convert_parenthesized_args(func_call.args()); + pretty_items( + &args, + BoxDoc::text(",").append(BoxDoc::space()), + BoxDoc::text(","), + (BoxDoc::text("("), BoxDoc::text(")")), + false, + util::FoldStyle::Fit, + ) + } else { + BoxDoc::nil() + }; + let doc = doc + .append(parenthesized_args) + .append(self.convert_additional_args(func_call.args(), has_parenthesized_args)); + doc + } + + fn convert_parenthesized_args<'a>(&'a self, args: Args<'a>) -> Vec> { + let node = args.to_untyped(); + let args = node + .children() + .take_while(|node| node.kind() != SyntaxKind::RightParen) + .filter_map(|node| node.cast::<'_, Arg>()) + .map(|arg| self.convert_arg(arg)) + .collect(); + args + } + + fn convert_additional_args<'a>(&'a self, args: Args<'a>, has_paren: bool) -> BoxDoc<'a, ()> { + let node = args.to_untyped(); + let args = node + .children() + .skip_while(|node| { + if has_paren { + node.kind() != SyntaxKind::RightParen + } else { + node.kind() != SyntaxKind::ContentBlock + } + }) + .filter_map(|node| node.cast::<'_, Arg>()); + BoxDoc::concat(args.map(|arg| self.convert_arg(arg))).group() + } + + fn convert_arg<'a>(&'a self, arg: Arg<'a>) -> BoxDoc<'a, ()> { + match arg { + Arg::Pos(p) => self.convert_expr(p), + Arg::Named(n) => self.convert_named(n), + Arg::Spread(s) => self.convert_spread(s), + } + } + + fn convert_closure<'a>(&'a self, closure: Closure<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil(); + let params = self.convert_params(closure.params()); + let arg_list = pretty_items( + ¶ms, + BoxDoc::text(",").append(BoxDoc::space()), + BoxDoc::text(","), + (BoxDoc::text("("), BoxDoc::text(")")), + false, + util::FoldStyle::Fit, + ); + if let Some(name) = closure.name() { + doc = doc.append(self.convert_ident(name)); + doc = doc.append(arg_list); + doc = doc.append(BoxDoc::space()); + doc = doc.append(BoxDoc::text("=")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_expr(closure.body())); + } else { + if params.len() == 1 + && matches!(closure.params().children().next().unwrap(), Param::Pos(_)) + && !matches!( + closure.params().children().next().unwrap(), + Param::Pos(Pattern::Destructuring(_)) + ) + { + doc = params[0].clone(); + } else { + doc = arg_list + } + doc = doc.append(BoxDoc::space()); + doc = doc.append(BoxDoc::text("=>")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_expr(closure.body())); + } + doc + } + + fn convert_params<'a>(&'a self, params: Params<'a>) -> Vec> { + params + .children() + .map(|param| self.convert_param(param)) + .collect() + } + + fn convert_param<'a>(&'a self, param: Param<'a>) -> BoxDoc<'a, ()> { + match param { + Param::Pos(p) => self.convert_pattern(p), + Param::Named(n) => self.convert_named(n), + Param::Spread(s) => self.convert_spread(s), + } + } + + fn convert_spread<'a>(&'a self, spread: Spread<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::text(".."); + let ident = if let Some(id) = spread.sink_ident() { + self.convert_ident(id) + } else if let Some(expr) = spread.sink_expr() { + self.convert_expr(expr) + } else { + BoxDoc::nil() + }; + doc = doc.append(ident); + doc + } + + fn convert_pattern<'a>(&'a self, pattern: Pattern<'a>) -> BoxDoc<'a, ()> { + match pattern { + Pattern::Normal(n) => self.convert_expr(n), + Pattern::Placeholder(p) => self.convert_underscore(p), + Pattern::Destructuring(d) => self.convert_destructuring(d), + Pattern::Parenthesized(p) => self.convert_parenthesized(p), + } + } + + fn convert_underscore<'a>(&'a self, _underscore: Underscore<'a>) -> BoxDoc<'a, ()> { + BoxDoc::text("_") + } + + fn convert_destructuring<'a>(&'a self, destructuring: Destructuring<'a>) -> BoxDoc<'a, ()> { + BoxDoc::text("(") + .append(BoxDoc::intersperse( + destructuring + .items() + .map(|item| self.convert_destructuring_item(item)), + BoxDoc::text(",").append(BoxDoc::line()), + )) + .append(BoxDoc::text(")")) + } + + fn convert_destructuring_item<'a>( + &'a self, + destructuring_item: DestructuringItem<'a>, + ) -> BoxDoc<'a, ()> { + match destructuring_item { + DestructuringItem::Spread(s) => self.convert_spread(s), + DestructuringItem::Named(n) => self.convert_named(n), + DestructuringItem::Pattern(p) => self.convert_pattern(p), + } + } + + fn convert_let_binding<'a>(&'a self, let_binding: LetBinding<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil() + .append(BoxDoc::text("let")) + .append(BoxDoc::space()); + match let_binding.kind() { + LetBindingKind::Normal(n) => { + doc = doc.append(self.convert_pattern(n).group()); + if let Some(expr) = let_binding.init() { + doc = doc.append(BoxDoc::space()); + doc = doc.append(BoxDoc::text("=")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_expr(expr)); + } + } + LetBindingKind::Closure(_c) => { + if let Some(c) = let_binding.init() { + doc = doc.append(self.convert_expr(c)); + } + } + } + doc + } + + fn convert_destruct_assignment<'a>( + &'a self, + destruct_assign: DestructAssignment<'a>, + ) -> BoxDoc<'a, ()> { + self.convert_pattern(destruct_assign.pattern()) + .append(BoxDoc::space()) + .append(BoxDoc::text("=")) + .append(BoxDoc::space()) + .append(self.convert_expr(destruct_assign.value())) + } + + fn convert_set_rule<'a>(&'a self, set_rule: SetRule<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil() + .append(BoxDoc::text("set")) + .append(BoxDoc::space()); + doc = doc.append(self.convert_expr(set_rule.target())); + doc = doc.append(pretty_items( + &self.convert_parenthesized_args(set_rule.args()), + BoxDoc::text(",").append(BoxDoc::space()), + BoxDoc::text(","), + (BoxDoc::text("("), BoxDoc::text(")")), + false, + util::FoldStyle::Single, + )); + if let Some(condition) = set_rule.condition() { + doc = doc.append(BoxDoc::space()); + doc = doc.append(BoxDoc::text("if")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_expr(condition)); + } + doc + } + + fn convert_show_rule<'a>(&'a self, show_rule: ShowRule<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil().append(BoxDoc::text("show")); + if let Some(selector) = show_rule.selector() { + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_expr(selector)); + } + doc = doc.append(BoxDoc::text(":")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_expr(show_rule.transform())); + doc + } + + fn convert_conditional<'a>(&'a self, conditional: Conditional<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil(); + enum CastType { + Condition, + Then, + Else, + } + let has_else = conditional.else_body().is_some(); + let mut expr_type = CastType::Condition; + for child in conditional.to_untyped().children() { + if child.kind() == SyntaxKind::If { + doc = doc.append(BoxDoc::text("if")); + doc = doc.append(BoxDoc::space()); + } else if child.kind() == SyntaxKind::Else { + doc = doc.append(BoxDoc::text("else")); + doc = doc.append(BoxDoc::space()); + } else if child.kind() == SyntaxKind::BlockComment { + doc = doc.append(trivia(child)); + doc = doc.append(BoxDoc::space()); + } else if child.kind() == SyntaxKind::LineComment { + doc = doc.append(trivia(child)); + doc = doc.append(BoxDoc::hardline()); + } else { + match expr_type { + CastType::Condition => { + if let Some(condition) = child.cast() { + doc = doc.append(self.convert_expr(condition)); + doc = doc.append(BoxDoc::space()); + expr_type = CastType::Then; + } + } + CastType::Then => { + if let Some(then_expr) = child.cast() { + doc = doc.append(self.convert_expr(then_expr).group()); + if has_else { + expr_type = CastType::Else; + doc = doc.append(BoxDoc::space()); + } + } + } + CastType::Else => { + if let Some(else_expr) = child.cast() { + doc = doc.append(self.convert_expr(else_expr).group()); + } + } + } + } + } + doc + } + + fn convert_while<'a>(&'a self, while_loop: WhileLoop<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil(); + #[derive(Debug, PartialEq)] + enum CastType { + Condition, + Body, + } + let mut expr_type = CastType::Condition; + for child in while_loop.to_untyped().children() { + if child.kind() == SyntaxKind::While { + doc = doc.append(BoxDoc::text("while")); + doc = doc.append(BoxDoc::space()); + } else if child.kind() == SyntaxKind::BlockComment { + doc = doc.append(trivia(child)); + doc = doc.append(BoxDoc::space()); + } else if child.kind() == SyntaxKind::LineComment { + doc = doc.append(trivia(child)); + doc = doc.append(BoxDoc::hardline()); + } else if let Some(expr) = child.cast() { + doc = doc.append(self.convert_expr(expr)); + if expr_type == CastType::Condition { + doc = doc.append(BoxDoc::space()); + expr_type = CastType::Body; + } + } + } + doc + } + + fn convert_for<'a>(&'a self, for_loop: ForLoop<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil(); + enum CastType { + Pattern, + Iter, + Body, + } + let mut expr_type = CastType::Pattern; + for child in for_loop.to_untyped().children() { + if child.kind() == SyntaxKind::For { + doc = doc.append(BoxDoc::text("for")); + doc = doc.append(BoxDoc::space()); + } else if child.kind() == SyntaxKind::In { + doc = doc.append(BoxDoc::text("in")); + doc = doc.append(BoxDoc::space()); + } else if child.kind() == SyntaxKind::BlockComment { + doc = doc.append(trivia(child)); + doc = doc.append(BoxDoc::space()); + } else if child.kind() == SyntaxKind::LineComment { + doc = doc.append(trivia(child)); + doc = doc.append(BoxDoc::hardline()); + } else { + match expr_type { + CastType::Pattern => { + if let Some(pattern) = child.cast() { + doc = doc.append(self.convert_pattern(pattern)); + doc = doc.append(BoxDoc::space()); + expr_type = CastType::Iter; + } + } + CastType::Iter => { + if let Some(iter) = child.cast() { + doc = doc.append(self.convert_expr(iter)); + doc = doc.append(BoxDoc::space()); + expr_type = CastType::Body; + } + } + CastType::Body => { + if let Some(body) = child.cast() { + doc = doc.append(self.convert_expr(body)); + } + } + } + } + } + doc + } + + fn convert_import<'a>(&'a self, import: ModuleImport<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil().append(BoxDoc::text("import")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_expr(import.source())); + if let Some(imports) = import.imports() { + doc = doc.append(BoxDoc::text(":")); + doc = doc.append(BoxDoc::space()); + let imports = match imports { + Imports::Wildcard => BoxDoc::text("*"), + Imports::Items(i) => BoxDoc::intersperse( + i.iter().map(|item| self.convert_import_item(item)), + BoxDoc::text(",").append(BoxDoc::line()), + ), + }; + doc = doc.append(imports.group()); + } + if let Some(new_name) = import.new_name() { + doc = doc.append(BoxDoc::space()); + doc = doc.append(BoxDoc::text("as")); + doc = doc.append(BoxDoc::space()); + doc = doc.append(self.convert_ident(new_name)); + } + doc + } + + fn convert_import_item<'a>(&'a self, import_item: ImportItem<'a>) -> BoxDoc<'a, ()> { + match import_item { + ImportItem::Simple(s) => self.convert_ident(s), + ImportItem::Renamed(r) => self + .convert_ident(r.original_name()) + .append(BoxDoc::space()) + .append(BoxDoc::text("as")) + .append(BoxDoc::space()) + .append(self.convert_ident(r.new_name())), + } + } + + fn convert_include<'a>(&'a self, include: ModuleInclude<'a>) -> BoxDoc<'a, ()> { + BoxDoc::nil() + .append(BoxDoc::text("include")) + .append(BoxDoc::space()) + .append(self.convert_expr(include.source())) + } + + fn convert_break<'a>(&'a self, _break: LoopBreak<'a>) -> BoxDoc<'a, ()> { + BoxDoc::nil().append(BoxDoc::text("break")) + } + + fn convert_continue<'a>(&'a self, _continue: LoopContinue<'a>) -> BoxDoc<'a, ()> { + BoxDoc::nil().append(BoxDoc::text("continue")) + } + + fn convert_return<'a>(&'a self, return_stmt: FuncReturn<'a>) -> BoxDoc<'a, ()> { + let mut doc = BoxDoc::nil() + .append(BoxDoc::text("return")) + .append(BoxDoc::space()); + if let Some(body) = return_stmt.body() { + doc = doc.append(self.convert_expr(body)); + } + doc + } + + fn convert_math_delimited<'a>(&'a self, math_delimited: MathDelimited<'a>) -> BoxDoc<'a, ()> { + let open = self.convert_expr(math_delimited.open()); + let close = self.convert_expr(math_delimited.close()); + let body = self.convert_math(math_delimited.body()); + let singleline = open.clone().append(body.clone()).append(close.clone()); + let multiline = open + .append(BoxDoc::hardline()) + .append(body) + .append(BoxDoc::hardline()) + .nest(2) + .append(close); + multiline.flat_alt(singleline) + } + + fn convert_math_attach<'a>(&'a self, math_attach: MathAttach<'a>) -> BoxDoc<'a, ()> { + let mut doc = self.convert_expr(math_attach.base()); + let prime_index = math_attach + .to_untyped() + .children() + .enumerate() + .skip_while(|(_i, node)| node.cast::>().is_none()) + .nth(1) + .filter(|(_i, n)| n.cast::().is_some()) + .map(|(i, _n)| i); + + let bottom_index = math_attach + .to_untyped() + .children() + .enumerate() + .skip_while(|(_i, node)| !matches!(node.kind(), SyntaxKind::Underscore)) + .find_map(|(i, n)| SyntaxNode::cast::>(n).map(|n| (i, n))) + .map(|(i, _n)| i); + + let top_index = math_attach + .to_untyped() + .children() + .enumerate() + .skip_while(|(_i, node)| !matches!(node.kind(), SyntaxKind::Hat)) + .find_map(|(i, n)| SyntaxNode::cast::>(n).map(|n| (i, n))) + .map(|(i, _n)| i); + + #[derive(Debug)] + enum IndexType { + Prime, + Bottom, + Top, + } + + let mut index_types = [IndexType::Prime, IndexType::Bottom, IndexType::Top]; + index_types.sort_by_key(|index_type| match index_type { + IndexType::Prime => prime_index, + IndexType::Bottom => bottom_index, + IndexType::Top => top_index, + }); + + for index in index_types { + match index { + IndexType::Prime => { + if let Some(primes) = math_attach.primes() { + doc = doc.append(self.convert_math_primes(primes)); + } + } + IndexType::Bottom => { + if let Some(bottom) = math_attach.bottom() { + doc = doc.append(BoxDoc::text("_")); + doc = doc.append(self.convert_expr(bottom)); + } + } + IndexType::Top => { + if let Some(top) = math_attach.top() { + doc = doc.append(BoxDoc::text("^")); + doc = doc.append(self.convert_expr(top)); + } + } + } + } + doc + } + + fn convert_math_primes<'a>(&'a self, math_primes: MathPrimes<'a>) -> BoxDoc<'a, ()> { + BoxDoc::text("'".repeat(math_primes.count())) + } + + fn convert_math_frac<'a>(&'a self, math_frac: MathFrac<'a>) -> BoxDoc<'a, ()> { + let singleline = self + .convert_expr(math_frac.num()) + .append(BoxDoc::space()) + .append(BoxDoc::text("/")) + .append(BoxDoc::space()) + .append(self.convert_expr(math_frac.denom())); + // TODO: add multiline version + singleline + } + + fn convert_math_root<'a>(&'a self, math_root: MathRoot<'a>) -> BoxDoc<'a, ()> { + let sqrt_sym = if let Some(index) = math_root.index() { + if index == 3 { + BoxDoc::text("∛") + } else if index == 4 { + BoxDoc::text("∜") + } else { + // TODO: actually unreachable + BoxDoc::text("√") + } + } else { + BoxDoc::text("√") + }; + sqrt_sym.append(self.convert_expr(math_root.radicand())) + } + + fn convert_contextual<'a>(&'a self, ctx: Contextual<'a>) -> BoxDoc<'a, ()> { + let body = self.convert_expr(ctx.body()); + BoxDoc::text("context").append(BoxDoc::space()).append(body) + } +} + +fn trivia(node: &SyntaxNode) -> BoxDoc<'_, ()> { + to_doc(std::borrow::Cow::Borrowed(node.text()), false) +} + +pub fn to_doc(s: Cow<'_, str>, strip_prefix: bool) -> BoxDoc<'_, ()> { + let get_line = |s: &str| { + if strip_prefix { + s.trim_start().to_string() + } else { + s.to_string() + } + }; + // String::lines() doesn't include the trailing newline + let has_trailing_newline = s.ends_with('\n') || s.ends_with('\r'); + let res = BoxDoc::intersperse( + s.lines().map(|s| BoxDoc::text(get_line(s))), + BoxDoc::hardline(), + ); + if has_trailing_newline { + res.append(BoxDoc::hardline()) + } else { + res + } +} + +#[cfg(test)] +mod tests { + use typst_syntax::parse; + + use super::*; + + #[test] + fn test_to_doc() { + let tests = [ + "command can take a directory as an argument to use as the book", + "123\n456\n789", + "123\n4567\n789\n", + "123\n4568\n789\n", + ]; + for test in tests.into_iter() { + insta::assert_debug_snapshot!(to_doc(test.into(), false)); + } + } + + #[test] + fn convert_markup() { + let tests = [r"=== --open + +When you use the `--open` flag, typst-book will open the rendered book in +your default web browser after building it."]; + for test in tests.into_iter() { + let root = parse(test); + insta::assert_debug_snapshot!(root); + let markup = root.cast().unwrap(); + let printer = PrettyPrinter::default(); + let doc = printer.convert_markup(markup); + insta::assert_debug_snapshot!(doc.pretty(120).to_string()); + } + } + + #[test] + fn convert_func_call() { + let tests = [r#"#link("http://example.com")[test]"#]; + for test in tests.into_iter() { + let root = parse(test); + insta::assert_debug_snapshot!(root); + let markup = root.cast().unwrap(); + let printer = PrettyPrinter::default(); + let doc = printer.convert_markup(markup); + insta::assert_debug_snapshot!(doc.pretty(120).to_string()); + } + } +} diff --git a/tests/assets/unit/markup/content-space.typ b/tests/assets/unit/markup/content-space.typ new file mode 100644 index 0000000..0209fcb --- /dev/null +++ b/tests/assets/unit/markup/content-space.typ @@ -0,0 +1,5 @@ +#{ + show heading.where(level: 1): it => box(width: 100%)[ + #v(0.5em) + ] +} diff --git a/tests/assets/unit/windows.typ b/tests/assets/unit/windows.typ new file mode 100644 index 0000000..cce4f21 --- /dev/null +++ b/tests/assets/unit/windows.typ @@ -0,0 +1,10 @@ +#align(center, table( + columns: 2, + column-gutter: 1em, + [ + $ f_n = cases( + a &"if" n = 0, + r dot f_(n - 1) &"else" + ) $ + ], +)) diff --git a/tests/snapshots/assets__check_file@book.typ-120.snap b/tests/snapshots/assets__check_file@book.typ-120.snap index 7b27697..b323b67 100644 --- a/tests/snapshots/assets__check_file@book.typ-120.snap +++ b/tests/snapshots/assets__check_file@book.typ-120.snap @@ -14,7 +14,8 @@ input_file: tests/assets/book.typ repository-edit: "https://github.com/Myriad-Dreamin/typst-book/edit/main/github-pages/docs/{path}", authors: ("Myriad-Dreamin", "7mile"), language: "en", - summary: [ // begin of summary + summary: [ + // begin of summary #prefix-chapter("introduction.typ")[Introduction] = User Guide - #chapter("guide/installation.typ", section: "1")[Installation] diff --git a/tests/snapshots/assets__check_file@book.typ-40.snap b/tests/snapshots/assets__check_file@book.typ-40.snap index 8a2bfd1..cd167f1 100644 --- a/tests/snapshots/assets__check_file@book.typ-40.snap +++ b/tests/snapshots/assets__check_file@book.typ-40.snap @@ -14,10 +14,11 @@ input_file: tests/assets/book.typ repository-edit: "https://github.com/Myriad-Dreamin/typst-book/edit/main/github-pages/docs/{path}", authors: ("Myriad-Dreamin", "7mile"), language: "en", - summary: [ // begin of summary - #prefix-chapter( - "introduction.typ", - )[Introduction] + summary: [ + // begin of summary + #prefix-chapter("introduction.typ")[ + Introduction + ] = User Guide - #chapter( "guide/installation.typ", @@ -31,10 +32,9 @@ input_file: tests/assets/book.typ "guide/faq.typ", section: "3", )[Frequently Asked Questions] - - #chapter( - none, - section: "4", - )[Further reading] + - #chapter(none, section: "4")[ + Further reading + ] = Reference Guide - #chapter( "cli/main.typ", @@ -87,26 +87,21 @@ input_file: tests/assets/book.typ "format/theme.typ", section: "6.2", )[Theme] - - #chapter( - none, - section: "6.3", - )[Typst Support] - - #chapter( - none, - section: "7", - )[For developers] - - #chapter( - none, - section: "7.1", - )[Typst-side APIs] - - #chapter( - none, - section: "7.2", - )[typst-book CLI Internals] - - #chapter( - none, - section: "7.3", - )[Alternative Backends] + - #chapter(none, section: "6.3")[ + Typst Support + ] + - #chapter(none, section: "7")[ + For developers + ] + - #chapter(none, section: "7.1")[ + Typst-side APIs + ] + - #chapter(none, section: "7.2")[ + typst-book CLI Internals + ] + - #chapter(none, section: "7.3")[ + Alternative Backends + ] // end of summary ], ) diff --git a/tests/snapshots/assets__check_file@book.typ-80.snap b/tests/snapshots/assets__check_file@book.typ-80.snap index 7b27697..b323b67 100644 --- a/tests/snapshots/assets__check_file@book.typ-80.snap +++ b/tests/snapshots/assets__check_file@book.typ-80.snap @@ -14,7 +14,8 @@ input_file: tests/assets/book.typ repository-edit: "https://github.com/Myriad-Dreamin/typst-book/edit/main/github-pages/docs/{path}", authors: ("Myriad-Dreamin", "7mile"), language: "en", - summary: [ // begin of summary + summary: [ + // begin of summary #prefix-chapter("introduction.typ")[Introduction] = User Guide - #chapter("guide/installation.typ", section: "1")[Installation] diff --git a/tests/snapshots/assets__check_file@cetz-manual.typ-120.snap b/tests/snapshots/assets__check_file@cetz-manual.typ-120.snap index 8be5a3d..ba4beee 100644 --- a/tests/snapshots/assets__check_file@cetz-manual.typ-120.snap +++ b/tests/snapshots/assets__check_file@cetz-manual.typ-120.snap @@ -51,13 +51,15 @@ The name CeTZ is a recursive acronym for "CeTZ, ein Typst Zeichenpaket" (german = Usage This is the minimal starting point: -#pad(left: 1em)[```typ +#pad(left: 1em)[ + ```typ #import "@preview/cetz:0.2.1" #cetz.canvas({ import cetz.draw: * ... }) - ```] + ``` +] Note that draw functions are imported inside the scope of the `canvas` block. This is recommended as some draw functions override Typst's function's such as `line`. #show raw.where(block: false): it => if it.text.starts-with("<") and it.text.ends-with(">") { @@ -155,7 +157,9 @@ You can style draw elements by passing the relevant named arguments to their dra "stroke", ("none", "auto", "length", "color", "dictionary", "stroke"), default: black + 1pt, - [How to stroke the border or the path of the draw element. See Typst's line documentation for more details: https://typst.app/docs/reference/visualize/line/#parameters-stroke], + [ + How to stroke the border or the path of the draw element. See Typst's line documentation for more details: https://typst.app/docs/reference/visualize/line/#parameters-stroke + ], ) ```example @@ -249,7 +253,7 @@ Marks are arrow tips that can be added to the end of path based elements that su } ( raw(name), - name-to-mnemonic.at(name, default: ([],)).join([, ]), + name-to-mnemonic.at(name, default: ([],)).join([,]), cetz.canvas(cetz.draw.line((), (1, 0), mark: (end: name))), ) } @@ -279,17 +283,23 @@ line(..c) #doc-style.show-parameter-block( "symbol", ("none", "string", "array", "dictionary"), - [This option sets the mark to draw when using the `mark` draw function, or applies styling to both mark ends of path based elements. The mark's name or shorthand can be given, multiple marks can be drawn by passing an array of names or shorthands. When `none` no marks will be drawn. A style `dictionary` can be given instead of a `string` to override styling for that particular mark, just make sure to still give the mark name using the `symbol` key otherwise nothing will be drawn!. ], + [ + This option sets the mark to draw when using the `mark` draw function, or applies styling to both mark ends of path based elements. The mark's name or shorthand can be given, multiple marks can be drawn by passing an array of names or shorthands. When `none` no marks will be drawn. A style `dictionary` can be given instead of a `string` to override styling for that particular mark, just make sure to still give the mark name using the `symbol` key otherwise nothing will be drawn!. + ], ) #doc-style.show-parameter-block( "start", ("none", "string", "array", "dictionary"), - [This option sets the mark to draw at the start of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function.], + [ + This option sets the mark to draw at the start of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function. + ], ) #doc-style.show-parameter-block( "end", ("none", "string", "array", "dictionary"), - [This option sets the mark to draw at the end of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function.], + [ + This option sets the mark to draw at the end of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function. + ], ) #doc-style.show-parameter-block( @@ -307,7 +317,9 @@ line(..c) #doc-style.show-parameter-block( "inset", "number", - [It specifies a distance by which something inside the arrow tip is set inwards; for the Stealth arrow tip it is the distance by which the back angle is moved inwards.], + [ + It specifies a distance by which something inside the arrow tip is set inwards; for the Stealth arrow tip it is the distance by which the back angle is moved inwards. + ], default: 0.05cm, ) #doc-style.show-parameter-block( @@ -325,20 +337,26 @@ line(..c) #doc-style.show-parameter-block( "flex", "boolean", - [Only applicable when marks are used on curves such as bezier and hobby. If true, the mark will point along the secant of the curve. If false, the tangent at the marks tip is used.], + [ + Only applicable when marks are used on curves such as bezier and hobby. If true, the mark will point along the secant of the curve. If false, the tangent at the marks tip is used. + ], default: true, ) #doc-style.show-parameter-block( "position-samples", "integer", - [Only applicable when marks are used on curves such as bezier and hobby. The maximum number of samples to use for calculating curve positions. A higher number gives better results but may slow down compilation.], + [ + Only applicable when marks are used on curves such as bezier and hobby. The maximum number of samples to use for calculating curve positions. A higher number gives better results but may slow down compilation. + ], default: 30, ) #doc-style.show-parameter-block( "pos", ("number", "ratio"), - [Overrides the mark's position along a path. A number will move it an absolute distance, while a ratio will be a distance relative to the length of the path. Note that this may be removed in the future in preference of a different method.], + [ + Overrides the mark's position along a path. A number will move it an absolute distance, while a ratio will be a distance relative to the length of the path. Note that this may be removed in the future in preference of a different method. + ], ) #doc-style.show-parameter-block( @@ -377,13 +395,17 @@ line(..c) #doc-style.show-parameter-block( "shorten-to", ("integer", "auto", "none"), - [Which mark to shorten the path to when multiple marks are given. `auto` will shorten to the last mark, `none` will shorten to the first mark (effectively disabling path shortening). An integer can be given to select the mark's index.], + [ + Which mark to shorten the path to when multiple marks are given. `auto` will shorten to the last mark, `none` will shorten to the first mark (effectively disabling path shortening). An integer can be given to select the mark's index. + ], ) #doc-style.show-parameter-block( "transform-shape", "bool", - [When false marks will not be streched/affected by the current transformation, marks will be placed after the path is transformed.], + [ + When false marks will not be streched/affected by the current transformation, marks will be placed after the path is transformed. + ], default: true, ) @@ -466,13 +488,17 @@ Defines a point that is `radius` distance away from the origin at the given `ang #doc-style.show-parameter-block( "angle", "angle", - [The angle of the coordinate. An angle of `0deg` is to the right, a degree of `90deg` is upward. See https://typst.app/docs/reference/layout/angle/ for details.], + [ + The angle of the coordinate. An angle of `0deg` is to the right, a degree of `90deg` is upward. See https://typst.app/docs/reference/layout/angle/ for details. + ], show-default: false, ) #doc-style.show-parameter-block( "radius", ("number", "tuple"), - [The distance from the origin. An array can be given, in the form `(x, y)` to define the `x` and `y` radii of an ellipse instead of a circle.], + [ + The distance from the origin. An array can be given, in the form `(x, y)` to define the `x` and `y` radii of an ellipse instead of a circle. + ], show-default: false, ) @@ -493,7 +519,9 @@ In the barycentric coordinate system a point is expressed as the linear combinat #doc-style.show-parameter-block( "bary", "dictionary", - [A dictionary where the key is a named element and the value is a ``. The `center` anchor of the named element is used as $v$ and the value is used as $a$.], + [ + A dictionary where the key is a named element and the value is a ``. The `center` anchor of the named element is used as $v$ and the value is used as $a$. + ], show-default: false, ) @@ -542,7 +570,9 @@ Defines a point relative to a named element using anchors, see @anchors. #doc-style.show-parameter-block( "anchor", ("number", "angle", "string", "ratio", "none"), - [The anchor of the element. Strings are named anchors, angles are border anchors and numbers and ratios are path anchors. If not given the default anchor will be used, on most elements this is `center` but it can be different.], + [ + The anchor of the element. Strings are named anchors, angles are border anchors and numbers and ratios are path anchors. If not given the default anchor will be used, on most elements this is `center` but it can be different. + ], ) You can also use implicit syntax of a dot separated string in the form `"name.anchor"` @@ -654,7 +684,9 @@ An angle can also be given for the general meaning: "First consider the line fro #doc-style.show-parameter-block( "angle", "angle", - [Angle between $arrow("AB")$ and $arrow("AP")$, where $P$ is the resulting coordinate. This can be used to get the _normal_ for a tangent between two points.], + [ + Angle between $arrow("AB")$ and $arrow("AP")$, where $P$ is the resulting coordinate. This can be used to get the _normal_ for a tangent between two points. + ], default: 0deg, ) @@ -823,97 +855,68 @@ plot.plot( "orientation", ("direction"), default: ttb, - [ - The direction the legend items get laid out to. - ], + [The direction the legend items get laid out to.], ) #doc-style.show-parameter-block( "default-position", ("string", "coordinate"), default: "legend.north-east", - [ - The default position the legend gets placed at. - ], + [The default position the legend gets placed at.], ) #doc-style.show-parameter-block( "layer", ("number"), default: 1, - [ - The layer index the legend gets drawn at, see on-layer. - ], + [The layer index the legend gets drawn at, see on-layer.], ) #doc-style.show-parameter-block( "fill", ("paint"), default: rgb(255, 255, 255, 200), - [ - The legends frame background color. - ], -) -#doc-style.show-parameter-block( - "stroke", - ("stroke"), - default: black, - [ - The legends frame stroke style. - ], + [The legends frame background color.], ) +#doc-style.show-parameter-block("stroke", ("stroke"), default: black, [The legends frame stroke style.]) #doc-style.show-parameter-block( "padding", ("float"), default: .1, - [ - The legends frame padding, that is the distance added between its items and its frame. - ], + [The legends frame padding, that is the distance added between its items and its frame.], ) #doc-style.show-parameter-block( "offset", ("tuple"), default: (0, 0), - [ - An offset tuple (x and y coordinates) to add to the legends position. - ], + [An offset tuple (x and y coordinates) to add to the legends position.], ) #doc-style.show-parameter-block( "spacing", ("number"), default: .1, - [ - The spacing between the legend position and its frame. - ], + [The spacing between the legend position and its frame.], ) #doc-style.show-parameter-block( "item.spacing", ("number"), default: .05, - [ - The spacing between two legend items in canvas units. - ], + [The spacing between two legend items in canvas units.], ) #doc-style.show-parameter-block( "item.preview.width", ("number"), default: .75, - [ - The width of a legend items preview picture, a small preview of the graph the legend item belongs to. - ], + [The width of a legend items preview picture, a small preview of the graph the legend item belongs to.], ) #doc-style.show-parameter-block( "item.preview.height", ("number"), default: .3, - [ - The height of a legend items preview picture. - ], + [The height of a legend items preview picture.], ) #doc-style.show-parameter-block( "item.preview.margin", ("number"), default: .1, - [ - Margin between the preview picture and the item label. - ], + [Margin between the preview picture and the item label.], ) #doc-style.parse-show-module("/src/lib/plot/line.typ") @@ -1135,24 +1138,30 @@ All path decoration functions support the following style keys: "rest", [``], default: "LINE", - [If set to `"LINE"`, generate lines between the paths start/end and - the decorations start/end if the path is _not closed_.], + [ + If set to `"LINE"`, generate lines between the paths start/end and + the decorations start/end if the path is _not closed_. + ], ) #def-arg("width", [``], default: 1, [Width or thickness of the decoration.]) #def-arg( "segments", [``], default: 10, - [Number of repetitions/phases to generate. - This key is ignored if `segment-length` is set != `none`.], + [ + Number of repetitions/phases to generate. + This key is ignored if `segment-length` is set != `none`. + ], ) #def-arg("segment-length", [`none` or ``], default: none, [Length of one repetition/phase of the decoration.]) #def-arg( "align", [`"START"`, `"MID"`, `"END"`], default: "START", - [Alignment of the decoration on the path _if `segment-length` is set_ and - the decoration does not fill up the full range between start and stop.], + [ + Alignment of the decoration on the path _if `segment-length` is set_ and + the decoration does not fill up the full range between start and stop. + ], ) #doc-style.parse-show-module("/src/lib/decorations/path.typ") diff --git a/tests/snapshots/assets__check_file@cetz-manual.typ-40.snap b/tests/snapshots/assets__check_file@cetz-manual.typ-40.snap index 29fff1c..e50dfea 100644 --- a/tests/snapshots/assets__check_file@cetz-manual.typ-40.snap +++ b/tests/snapshots/assets__check_file@cetz-manual.typ-40.snap @@ -60,13 +60,15 @@ The name CeTZ is a recursive acronym for "CeTZ, ein Typst Zeichenpaket" (german = Usage This is the minimal starting point: -#pad(left: 1em)[```typ +#pad(left: 1em)[ + ```typ #import "@preview/cetz:0.2.1" #cetz.canvas({ import cetz.draw: * ... }) - ```] + ``` +] Note that draw functions are imported inside the scope of the `canvas` block. This is recommended as some draw functions override Typst's function's such as `line`. #show raw.where( @@ -184,7 +186,9 @@ You can style draw elements by passing the relevant named arguments to their dra "stroke", ), default: black + 1pt, - [How to stroke the border or the path of the draw element. See Typst's line documentation for more details: https://typst.app/docs/reference/visualize/line/#parameters-stroke], + [ + How to stroke the border or the path of the draw element. See Typst's line documentation for more details: https://typst.app/docs/reference/visualize/line/#parameters-stroke + ], ) ```example @@ -287,7 +291,7 @@ Marks are arrow tips that can be added to the end of path based elements that su name-to-mnemonic.at( name, default: ([],), - ).join([, ]), + ).join([,]), cetz.canvas( cetz.draw.line( (), @@ -328,7 +332,9 @@ line(..c) "array", "dictionary", ), - [This option sets the mark to draw when using the `mark` draw function, or applies styling to both mark ends of path based elements. The mark's name or shorthand can be given, multiple marks can be drawn by passing an array of names or shorthands. When `none` no marks will be drawn. A style `dictionary` can be given instead of a `string` to override styling for that particular mark, just make sure to still give the mark name using the `symbol` key otherwise nothing will be drawn!. ], + [ + This option sets the mark to draw when using the `mark` draw function, or applies styling to both mark ends of path based elements. The mark's name or shorthand can be given, multiple marks can be drawn by passing an array of names or shorthands. When `none` no marks will be drawn. A style `dictionary` can be given instead of a `string` to override styling for that particular mark, just make sure to still give the mark name using the `symbol` key otherwise nothing will be drawn!. + ], ) #doc-style.show-parameter-block( "start", @@ -338,7 +344,9 @@ line(..c) "array", "dictionary", ), - [This option sets the mark to draw at the start of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function.], + [ + This option sets the mark to draw at the start of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function. + ], ) #doc-style.show-parameter-block( "end", @@ -348,80 +356,106 @@ line(..c) "array", "dictionary", ), - [This option sets the mark to draw at the end of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function.], + [ + This option sets the mark to draw at the end of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function. + ], ) #doc-style.show-parameter-block( "length", "number", - [The size of the mark in the direction it is pointing.], + [ + The size of the mark in the direction it is pointing. + ], default: 0.2cm, ) #doc-style.show-parameter-block( "width", "number", - [The size of the mark along the normal of its direction.], + [ + The size of the mark along the normal of its direction. + ], default: 0.15cm, ) #doc-style.show-parameter-block( "inset", "number", - [It specifies a distance by which something inside the arrow tip is set inwards; for the Stealth arrow tip it is the distance by which the back angle is moved inwards.], + [ + It specifies a distance by which something inside the arrow tip is set inwards; for the Stealth arrow tip it is the distance by which the back angle is moved inwards. + ], default: 0.05cm, ) #doc-style.show-parameter-block( "scale", "float", - [A factor that is applied to the mark's length, width and inset.], + [ + A factor that is applied to the mark's length, width and inset. + ], default: 1, ) #doc-style.show-parameter-block( "sep", "number", - [The distance between multiple marks along their path.], + [ + The distance between multiple marks along their path. + ], default: 0.1cm, ) #doc-style.show-parameter-block( "flex", "boolean", - [Only applicable when marks are used on curves such as bezier and hobby. If true, the mark will point along the secant of the curve. If false, the tangent at the marks tip is used.], + [ + Only applicable when marks are used on curves such as bezier and hobby. If true, the mark will point along the secant of the curve. If false, the tangent at the marks tip is used. + ], default: true, ) #doc-style.show-parameter-block( "position-samples", "integer", - [Only applicable when marks are used on curves such as bezier and hobby. The maximum number of samples to use for calculating curve positions. A higher number gives better results but may slow down compilation.], + [ + Only applicable when marks are used on curves such as bezier and hobby. The maximum number of samples to use for calculating curve positions. A higher number gives better results but may slow down compilation. + ], default: 30, ) #doc-style.show-parameter-block( "pos", ("number", "ratio"), - [Overrides the mark's position along a path. A number will move it an absolute distance, while a ratio will be a distance relative to the length of the path. Note that this may be removed in the future in preference of a different method.], + [ + Overrides the mark's position along a path. A number will move it an absolute distance, while a ratio will be a distance relative to the length of the path. Note that this may be removed in the future in preference of a different method. + ], ) #doc-style.show-parameter-block( "offset", ("number", "ratio"), - [Like `pos` but it moves the position of the mark instead of overriding it.], + [ + Like `pos` but it moves the position of the mark instead of overriding it. + ], ) #doc-style.show-parameter-block( "slant", "ratio", - [How much to slant the mark relative to the axis of the arrow. 0% means no slant 100% slants at 45 degrees], + [ + How much to slant the mark relative to the axis of the arrow. 0% means no slant 100% slants at 45 degrees + ], default: 0%, ) #doc-style.show-parameter-block( "harpoon", "boolean", - [When true only the top half of the mark is drawn.], + [ + When true only the top half of the mark is drawn. + ], default: false, ) #doc-style.show-parameter-block( "flip", "boolean", - [When true the mark is flipped along its axis.], + [ + When true the mark is flipped along its axis. + ], default: false, ) #doc-style.show-parameter-block( @@ -433,26 +467,34 @@ line(..c) #doc-style.show-parameter-block( "xy-up", "vector", - [The direction which is "up" for use when drawing 2D marks.], + [ + The direction which is "up" for use when drawing 2D marks. + ], default: (0, 0, 1), ) #doc-style.show-parameter-block( "z-up", "vector", - [The direction which is "up" for use when drawing 3D marks.], + [ + The direction which is "up" for use when drawing 3D marks. + ], default: (0, 1, 0), ) #doc-style.show-parameter-block( "shorten-to", ("integer", "auto", "none"), - [Which mark to shorten the path to when multiple marks are given. `auto` will shorten to the last mark, `none` will shorten to the first mark (effectively disabling path shortening). An integer can be given to select the mark's index.], + [ + Which mark to shorten the path to when multiple marks are given. `auto` will shorten to the last mark, `none` will shorten to the first mark (effectively disabling path shortening). An integer can be given to select the mark's index. + ], ) #doc-style.show-parameter-block( "transform-shape", "bool", - [When false marks will not be streched/affected by the current transformation, marks will be placed after the path is transformed.], + [ + When false marks will not be streched/affected by the current transformation, marks will be placed after the path is transformed. + ], default: true, ) @@ -488,19 +530,25 @@ Defines a point `x` units right, `y` units upward, and `z` units away. "x", "number", default: 0, - [The number of units in the `x` direction.], + [ + The number of units in the `x` direction. + ], ) #doc-style.show-parameter-block( "y", "number", default: 0, - [The number of units in the `y` direction.], + [ + The number of units in the `y` direction. + ], ) #doc-style.show-parameter-block( "z", "number", default: 0, - [The number of units in the `z` direction.], + [ + The number of units in the `z` direction. + ], ) The implicit form can be given as an array of two or three ``s, as in `(x,y)` and `(x,y,z)`. @@ -561,13 +609,17 @@ Defines a point that is `radius` distance away from the origin at the given `ang #doc-style.show-parameter-block( "angle", "angle", - [The angle of the coordinate. An angle of `0deg` is to the right, a degree of `90deg` is upward. See https://typst.app/docs/reference/layout/angle/ for details.], + [ + The angle of the coordinate. An angle of `0deg` is to the right, a degree of `90deg` is upward. See https://typst.app/docs/reference/layout/angle/ for details. + ], show-default: false, ) #doc-style.show-parameter-block( "radius", ("number", "tuple"), - [The distance from the origin. An array can be given, in the form `(x, y)` to define the `x` and `y` radii of an ellipse instead of a circle.], + [ + The distance from the origin. An array can be given, in the form `(x, y)` to define the `x` and `y` radii of an ellipse instead of a circle. + ], show-default: false, ) @@ -588,7 +640,9 @@ In the barycentric coordinate system a point is expressed as the linear combinat #doc-style.show-parameter-block( "bary", "dictionary", - [A dictionary where the key is a named element and the value is a ``. The `center` anchor of the named element is used as $v$ and the value is used as $a$.], + [ + A dictionary where the key is a named element and the value is a ``. The `center` anchor of the named element is used as $v$ and the value is used as $a$. + ], show-default: false, ) @@ -631,7 +685,9 @@ Defines a point relative to a named element using anchors, see @anchors. #doc-style.show-parameter-block( "name", "string", - [The name of the element that you wish to use to specify a coordinate.], + [ + The name of the element that you wish to use to specify a coordinate. + ], show-default: false, ) #doc-style.show-parameter-block( @@ -643,7 +699,9 @@ Defines a point relative to a named element using anchors, see @anchors. "ratio", "none", ), - [The anchor of the element. Strings are named anchors, angles are border anchors and numbers and ratios are path anchors. If not given the default anchor will be used, on most elements this is `center` but it can be different.], + [ + The anchor of the element. Strings are named anchors, angles are border anchors and numbers and ratios are path anchors. If not given the default anchor will be used, on most elements this is `center` but it can be different. + ], ) You can also use implicit syntax of a dot separated string in the form `"name.anchor"` @@ -673,20 +731,26 @@ This system allows you to compute the point that lies tangent to a shape. In det #doc-style.show-parameter-block( "element", "string", - [The name of the element on whose border the tangent should lie.], + [ + The name of the element on whose border the tangent should lie. + ], show-default: false, ) #doc-style.show-parameter-block( show-default: false, "point", "coordinate", - [The point through which the tangent should go.], + [ + The point through which the tangent should go. + ], ) #doc-style.show-parameter-block( show-default: false, "solution", "integer", - [Which solution should be used if there are more than one.], + [ + Which solution should be used if there are more than one. + ], ) A special algorithm is needed in order to compute the tangent for a given shape. Currently it does this by assuming the distance between the center and top anchor (See @anchors) is the radius of a circle. @@ -711,13 +775,17 @@ Can be used to find the intersection of a vertical line going through a point $p show-default: false, "horizontal", "coordinate", - [The coordinate through which the horizontal line passes.], + [ + The coordinate through which the horizontal line passes. + ], ) #doc-style.show-parameter-block( show-default: false, "vertical", "coordinate", - [The coordinate through which the vertical line passes.], + [ + The coordinate through which the vertical line passes. + ], ) You can use the implicit syntax of `(horizontal, "-|", vertical)` or `(vertical, "|-", horizontal)` @@ -765,7 +833,9 @@ An angle can also be given for the general meaning: "First consider the line fro #doc-style.show-parameter-block( "angle", "angle", - [Angle between $arrow("AB")$ and $arrow("AP")$, where $P$ is the resulting coordinate. This can be used to get the _normal_ for a tangent between two points.], + [ + Angle between $arrow("AB")$ and $arrow("AP")$, where $P$ is the resulting coordinate. This can be used to get the _normal_ for a tangent between two points. + ], default: 0deg, ) @@ -962,17 +1032,13 @@ plot.plot( "fill", ("paint"), default: rgb(255, 255, 255, 200), - [ - The legends frame background color. - ], + [The legends frame background color.], ) #doc-style.show-parameter-block( "stroke", ("stroke"), default: black, - [ - The legends frame stroke style. - ], + [The legends frame stroke style.], ) #doc-style.show-parameter-block( "padding", @@ -1289,46 +1355,59 @@ All path decoration functions support the following style keys: "start", [`` or ``], default: 0%, - [Absolute or relative start of the decoration on the path.], + [ + Absolute or relative start of the decoration on the path. + ], ) #def-arg( "stop", [`` or ``], default: 100%, - [Absolute or relative end of the decoration on the path.], + [ + Absolute or relative end of the decoration on the path. + ], ) #def-arg( "rest", [``], default: "LINE", - [If set to `"LINE"`, generate lines between the paths start/end and - the decorations start/end if the path is _not closed_.], + [ + If set to `"LINE"`, generate lines between the paths start/end and + the decorations start/end if the path is _not closed_. + ], ) #def-arg( "width", [``], default: 1, - [Width or thickness of the decoration.], + [Width or thickness of the decoration. + ], ) #def-arg( "segments", [``], default: 10, - [Number of repetitions/phases to generate. - This key is ignored if `segment-length` is set != `none`.], + [ + Number of repetitions/phases to generate. + This key is ignored if `segment-length` is set != `none`. + ], ) #def-arg( "segment-length", [`none` or ``], default: none, - [Length of one repetition/phase of the decoration.], + [ + Length of one repetition/phase of the decoration. + ], ) #def-arg( "align", [`"START"`, `"MID"`, `"END"`], default: "START", - [Alignment of the decoration on the path _if `segment-length` is set_ and - the decoration does not fill up the full range between start and stop.], + [ + Alignment of the decoration on the path _if `segment-length` is set_ and + the decoration does not fill up the full range between start and stop. + ], ) #doc-style.parse-show-module( diff --git a/tests/snapshots/assets__check_file@cetz-manual.typ-80.snap b/tests/snapshots/assets__check_file@cetz-manual.typ-80.snap index ec0b1d8..3ae20cf 100644 --- a/tests/snapshots/assets__check_file@cetz-manual.typ-80.snap +++ b/tests/snapshots/assets__check_file@cetz-manual.typ-80.snap @@ -51,13 +51,15 @@ The name CeTZ is a recursive acronym for "CeTZ, ein Typst Zeichenpaket" (german = Usage This is the minimal starting point: -#pad(left: 1em)[```typ +#pad(left: 1em)[ + ```typ #import "@preview/cetz:0.2.1" #cetz.canvas({ import cetz.draw: * ... }) - ```] + ``` +] Note that draw functions are imported inside the scope of the `canvas` block. This is recommended as some draw functions override Typst's function's such as `line`. #show raw.where(block: false): it => if it.text.starts-with( @@ -162,7 +164,9 @@ You can style draw elements by passing the relevant named arguments to their dra "stroke", ("none", "auto", "length", "color", "dictionary", "stroke"), default: black + 1pt, - [How to stroke the border or the path of the draw element. See Typst's line documentation for more details: https://typst.app/docs/reference/visualize/line/#parameters-stroke], + [ + How to stroke the border or the path of the draw element. See Typst's line documentation for more details: https://typst.app/docs/reference/visualize/line/#parameters-stroke + ], ) ```example @@ -256,7 +260,7 @@ Marks are arrow tips that can be added to the end of path based elements that su } ( raw(name), - name-to-mnemonic.at(name, default: ([],)).join([, ]), + name-to-mnemonic.at(name, default: ([],)).join([,]), cetz.canvas(cetz.draw.line((), (1, 0), mark: (end: name))), ) } @@ -286,17 +290,23 @@ line(..c) #doc-style.show-parameter-block( "symbol", ("none", "string", "array", "dictionary"), - [This option sets the mark to draw when using the `mark` draw function, or applies styling to both mark ends of path based elements. The mark's name or shorthand can be given, multiple marks can be drawn by passing an array of names or shorthands. When `none` no marks will be drawn. A style `dictionary` can be given instead of a `string` to override styling for that particular mark, just make sure to still give the mark name using the `symbol` key otherwise nothing will be drawn!. ], + [ + This option sets the mark to draw when using the `mark` draw function, or applies styling to both mark ends of path based elements. The mark's name or shorthand can be given, multiple marks can be drawn by passing an array of names or shorthands. When `none` no marks will be drawn. A style `dictionary` can be given instead of a `string` to override styling for that particular mark, just make sure to still give the mark name using the `symbol` key otherwise nothing will be drawn!. + ], ) #doc-style.show-parameter-block( "start", ("none", "string", "array", "dictionary"), - [This option sets the mark to draw at the start of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function.], + [ + This option sets the mark to draw at the start of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function. + ], ) #doc-style.show-parameter-block( "end", ("none", "string", "array", "dictionary"), - [This option sets the mark to draw at the end of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function.], + [ + This option sets the mark to draw at the end of a path based element. It will override all options of the `symbol` key and will not effect marks drawn using the `mark` draw function. + ], ) #doc-style.show-parameter-block( @@ -314,7 +324,9 @@ line(..c) #doc-style.show-parameter-block( "inset", "number", - [It specifies a distance by which something inside the arrow tip is set inwards; for the Stealth arrow tip it is the distance by which the back angle is moved inwards.], + [ + It specifies a distance by which something inside the arrow tip is set inwards; for the Stealth arrow tip it is the distance by which the back angle is moved inwards. + ], default: 0.05cm, ) #doc-style.show-parameter-block( @@ -332,20 +344,26 @@ line(..c) #doc-style.show-parameter-block( "flex", "boolean", - [Only applicable when marks are used on curves such as bezier and hobby. If true, the mark will point along the secant of the curve. If false, the tangent at the marks tip is used.], + [ + Only applicable when marks are used on curves such as bezier and hobby. If true, the mark will point along the secant of the curve. If false, the tangent at the marks tip is used. + ], default: true, ) #doc-style.show-parameter-block( "position-samples", "integer", - [Only applicable when marks are used on curves such as bezier and hobby. The maximum number of samples to use for calculating curve positions. A higher number gives better results but may slow down compilation.], + [ + Only applicable when marks are used on curves such as bezier and hobby. The maximum number of samples to use for calculating curve positions. A higher number gives better results but may slow down compilation. + ], default: 30, ) #doc-style.show-parameter-block( "pos", ("number", "ratio"), - [Overrides the mark's position along a path. A number will move it an absolute distance, while a ratio will be a distance relative to the length of the path. Note that this may be removed in the future in preference of a different method.], + [ + Overrides the mark's position along a path. A number will move it an absolute distance, while a ratio will be a distance relative to the length of the path. Note that this may be removed in the future in preference of a different method. + ], ) #doc-style.show-parameter-block( @@ -357,7 +375,9 @@ line(..c) #doc-style.show-parameter-block( "slant", "ratio", - [How much to slant the mark relative to the axis of the arrow. 0% means no slant 100% slants at 45 degrees], + [ + How much to slant the mark relative to the axis of the arrow. 0% means no slant 100% slants at 45 degrees + ], default: 0%, ) #doc-style.show-parameter-block( @@ -394,13 +414,17 @@ line(..c) #doc-style.show-parameter-block( "shorten-to", ("integer", "auto", "none"), - [Which mark to shorten the path to when multiple marks are given. `auto` will shorten to the last mark, `none` will shorten to the first mark (effectively disabling path shortening). An integer can be given to select the mark's index.], + [ + Which mark to shorten the path to when multiple marks are given. `auto` will shorten to the last mark, `none` will shorten to the first mark (effectively disabling path shortening). An integer can be given to select the mark's index. + ], ) #doc-style.show-parameter-block( "transform-shape", "bool", - [When false marks will not be streched/affected by the current transformation, marks will be placed after the path is transformed.], + [ + When false marks will not be streched/affected by the current transformation, marks will be placed after the path is transformed. + ], default: true, ) @@ -503,13 +527,17 @@ Defines a point that is `radius` distance away from the origin at the given `ang #doc-style.show-parameter-block( "angle", "angle", - [The angle of the coordinate. An angle of `0deg` is to the right, a degree of `90deg` is upward. See https://typst.app/docs/reference/layout/angle/ for details.], + [ + The angle of the coordinate. An angle of `0deg` is to the right, a degree of `90deg` is upward. See https://typst.app/docs/reference/layout/angle/ for details. + ], show-default: false, ) #doc-style.show-parameter-block( "radius", ("number", "tuple"), - [The distance from the origin. An array can be given, in the form `(x, y)` to define the `x` and `y` radii of an ellipse instead of a circle.], + [ + The distance from the origin. An array can be given, in the form `(x, y)` to define the `x` and `y` radii of an ellipse instead of a circle. + ], show-default: false, ) @@ -530,7 +558,9 @@ In the barycentric coordinate system a point is expressed as the linear combinat #doc-style.show-parameter-block( "bary", "dictionary", - [A dictionary where the key is a named element and the value is a ``. The `center` anchor of the named element is used as $v$ and the value is used as $a$.], + [ + A dictionary where the key is a named element and the value is a ``. The `center` anchor of the named element is used as $v$ and the value is used as $a$. + ], show-default: false, ) @@ -579,7 +609,9 @@ Defines a point relative to a named element using anchors, see @anchors. #doc-style.show-parameter-block( "anchor", ("number", "angle", "string", "ratio", "none"), - [The anchor of the element. Strings are named anchors, angles are border anchors and numbers and ratios are path anchors. If not given the default anchor will be used, on most elements this is `center` but it can be different.], + [ + The anchor of the element. Strings are named anchors, angles are border anchors and numbers and ratios are path anchors. If not given the default anchor will be used, on most elements this is `center` but it can be different. + ], ) You can also use implicit syntax of a dot separated string in the form `"name.anchor"` @@ -701,7 +733,9 @@ An angle can also be given for the general meaning: "First consider the line fro #doc-style.show-parameter-block( "angle", "angle", - [Angle between $arrow("AB")$ and $arrow("AP")$, where $P$ is the resulting coordinate. This can be used to get the _normal_ for a tangent between two points.], + [ + Angle between $arrow("AB")$ and $arrow("AP")$, where $P$ is the resulting coordinate. This can be used to get the _normal_ for a tangent between two points. + ], default: 0deg, ) @@ -870,41 +904,31 @@ plot.plot( "orientation", ("direction"), default: ttb, - [ - The direction the legend items get laid out to. - ], + [The direction the legend items get laid out to.], ) #doc-style.show-parameter-block( "default-position", ("string", "coordinate"), default: "legend.north-east", - [ - The default position the legend gets placed at. - ], + [The default position the legend gets placed at.], ) #doc-style.show-parameter-block( "layer", ("number"), default: 1, - [ - The layer index the legend gets drawn at, see on-layer. - ], + [The layer index the legend gets drawn at, see on-layer.], ) #doc-style.show-parameter-block( "fill", ("paint"), default: rgb(255, 255, 255, 200), - [ - The legends frame background color. - ], + [The legends frame background color.], ) #doc-style.show-parameter-block( "stroke", ("stroke"), default: black, - [ - The legends frame stroke style. - ], + [The legends frame stroke style.], ) #doc-style.show-parameter-block( "padding", @@ -918,25 +942,19 @@ plot.plot( "offset", ("tuple"), default: (0, 0), - [ - An offset tuple (x and y coordinates) to add to the legends position. - ], + [An offset tuple (x and y coordinates) to add to the legends position.], ) #doc-style.show-parameter-block( "spacing", ("number"), default: .1, - [ - The spacing between the legend position and its frame. - ], + [The spacing between the legend position and its frame.], ) #doc-style.show-parameter-block( "item.spacing", ("number"), default: .05, - [ - The spacing between two legend items in canvas units. - ], + [The spacing between two legend items in canvas units.], ) #doc-style.show-parameter-block( "item.preview.width", @@ -950,17 +968,13 @@ plot.plot( "item.preview.height", ("number"), default: .3, - [ - The height of a legend items preview picture. - ], + [The height of a legend items preview picture.], ) #doc-style.show-parameter-block( "item.preview.margin", ("number"), default: .1, - [ - Margin between the preview picture and the item label. - ], + [Margin between the preview picture and the item label.], ) #doc-style.parse-show-module("/src/lib/plot/line.typ") @@ -1192,8 +1206,10 @@ All path decoration functions support the following style keys: "rest", [``], default: "LINE", - [If set to `"LINE"`, generate lines between the paths start/end and - the decorations start/end if the path is _not closed_.], + [ + If set to `"LINE"`, generate lines between the paths start/end and + the decorations start/end if the path is _not closed_. + ], ) #def-arg( "width", @@ -1205,8 +1221,10 @@ All path decoration functions support the following style keys: "segments", [``], default: 10, - [Number of repetitions/phases to generate. - This key is ignored if `segment-length` is set != `none`.], + [ + Number of repetitions/phases to generate. + This key is ignored if `segment-length` is set != `none`. + ], ) #def-arg( "segment-length", @@ -1218,8 +1236,10 @@ All path decoration functions support the following style keys: "align", [`"START"`, `"MID"`, `"END"`], default: "START", - [Alignment of the decoration on the path _if `segment-length` is set_ and - the decoration does not fill up the full range between start and stop.], + [ + Alignment of the decoration on the path _if `segment-length` is set_ and + the decoration does not fill up the full range between start and stop. + ], ) #doc-style.parse-show-module("/src/lib/decorations/path.typ") diff --git a/tests/snapshots/assets__check_file@simple-paper.typ-120.snap b/tests/snapshots/assets__check_file@simple-paper.typ-120.snap index 89faec0..335a817 100644 --- a/tests/snapshots/assets__check_file@simple-paper.typ-120.snap +++ b/tests/snapshots/assets__check_file@simple-paper.typ-120.snap @@ -114,9 +114,7 @@ input_file: tests/assets/simple-paper.typ #v(2pt) #h(2em) *摘要:* #abstract - #if keywords != () [ - *关键字:* #keywords.join(";") - ] + #if keywords != () [*关键字:* #keywords.join(";")] #v(2pt) ] @@ -128,12 +126,9 @@ input_file: tests/assets/simple-paper.typ #let problem(body) = { problem-counter.step() set enum(numbering: "(1)") - block( - fill: rgb(241, 241, 255), - inset: 8pt, - radius: 2pt, - width: 100%, - )[*题目 #problem-counter.display().* #h(0.75em) #body] + block(fill: rgb(241, 241, 255), inset: 8pt, radius: 2pt, width: 100%)[ + *题目 #problem-counter.display().* #h(0.75em) #body + ] } #let solution(body) = { diff --git a/tests/snapshots/assets__check_file@simple-paper.typ-40.snap b/tests/snapshots/assets__check_file@simple-paper.typ-40.snap index bdc3671..2c2a6df 100644 --- a/tests/snapshots/assets__check_file@simple-paper.typ-40.snap +++ b/tests/snapshots/assets__check_file@simple-paper.typ-40.snap @@ -113,17 +113,15 @@ input_file: tests/assets/simple-paper.typ #v(12pt) #set text(font: caption-font) #it - #par()[#text(size: 0.0em)[#h( - 0.0em, - )]] + #par()[#text(size: 0.0em)[#h(0.0em)] + ] #v(12pt) ] show image: it => [ #it - #par()[#text(size: 0.0em)[#h( - 0.0em, - )]] + #par()[#text(size: 0.0em)[#h(0.0em)] + ] ] show table: it => [ @@ -175,13 +173,14 @@ input_file: tests/assets/simple-paper.typ inset: 8pt, radius: 2pt, width: 100%, - )[*题目 #problem-counter.display().* #h(0.75em) #body] + )[ + *题目 #problem-counter.display().* #h(0.75em) #body + ] } #let solution(body) = { set enum(numbering: "(1)") - block( - inset: 8pt, - width: 100%, - )[*解答.* #h(0.75em) #body] + block(inset: 8pt, width: 100%)[ + *解答.* #h(0.75em) #body + ] } diff --git a/tests/snapshots/assets__check_file@simple-paper.typ-80.snap b/tests/snapshots/assets__check_file@simple-paper.typ-80.snap index 2a53d6a..345d3c9 100644 --- a/tests/snapshots/assets__check_file@simple-paper.typ-80.snap +++ b/tests/snapshots/assets__check_file@simple-paper.typ-80.snap @@ -118,9 +118,7 @@ input_file: tests/assets/simple-paper.typ #v(2pt) #h(2em) *摘要:* #abstract - #if keywords != () [ - *关键字:* #keywords.join(";") - ] + #if keywords != () [*关键字:* #keywords.join(";")] #v(2pt) ] @@ -132,12 +130,9 @@ input_file: tests/assets/simple-paper.typ #let problem(body) = { problem-counter.step() set enum(numbering: "(1)") - block( - fill: rgb(241, 241, 255), - inset: 8pt, - radius: 2pt, - width: 100%, - )[*题目 #problem-counter.display().* #h(0.75em) #body] + block(fill: rgb(241, 241, 255), inset: 8pt, radius: 2pt, width: 100%)[ + *题目 #problem-counter.display().* #h(0.75em) #body + ] } #let solution(body) = { diff --git a/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-120.snap b/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-120.snap index 1f4b7ea..afbe185 100644 --- a/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-120.snap +++ b/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-120.snap @@ -3,14 +3,17 @@ source: tests/assets.rs expression: doc_string input_file: tests/assets/typstfmt/124-block-comment.typ --- - #let test(..args) = ( +#let test(..args) = ( // do something ) -#test(a: "test test test test test", b: "test test test test test")[ /* test */ +#test(a: "test test test test test", b: "test test test test test")[ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test -][ /* test */ +][ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test -][ /* test */ +][ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test ] diff --git a/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-40.snap b/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-40.snap index bf5a28a..7338fc7 100644 --- a/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-40.snap +++ b/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-40.snap @@ -3,7 +3,7 @@ source: tests/assets.rs expression: doc_string input_file: tests/assets/typstfmt/124-block-comment.typ --- - #let test( +#let test( ..args, ) = ( // do something @@ -12,10 +12,13 @@ input_file: tests/assets/typstfmt/124-block-comment.typ #test( a: "test test test test test", b: "test test test test test", -)[ /* test */ +)[ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test -][ /* test */ +][ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test -][ /* test */ +][ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test ] diff --git a/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-80.snap b/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-80.snap index 1f4b7ea..afbe185 100644 --- a/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-80.snap +++ b/tests/snapshots/assets__check_file@typstfmt-124-block-comment.typ-80.snap @@ -3,14 +3,17 @@ source: tests/assets.rs expression: doc_string input_file: tests/assets/typstfmt/124-block-comment.typ --- - #let test(..args) = ( +#let test(..args) = ( // do something ) -#test(a: "test test test test test", b: "test test test test test")[ /* test */ +#test(a: "test test test test test", b: "test test test test test")[ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test -][ /* test */ +][ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test -][ /* test */ +][ + /* test */ test test test test test test test test test test test test test test test test test test test test test test test test ] diff --git a/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-120.snap b/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-120.snap index 970ba04..683dcfa 100644 --- a/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-120.snap +++ b/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-120.snap @@ -25,4 +25,5 @@ if b { ////////////////////// // This is a banner // ////////////////////// - ```] + ``` +] diff --git a/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-40.snap b/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-40.snap index 970ba04..683dcfa 100644 --- a/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-40.snap +++ b/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-40.snap @@ -25,4 +25,5 @@ if b { ////////////////////// // This is a banner // ////////////////////// - ```] + ``` +] diff --git a/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-80.snap b/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-80.snap index 970ba04..683dcfa 100644 --- a/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-80.snap +++ b/tests/snapshots/assets__check_file@typstfmt-136-raw.typ-80.snap @@ -25,4 +25,5 @@ if b { ////////////////////// // This is a banner // ////////////////////// - ```] + ``` +] diff --git a/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-120.snap b/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-120.snap index cb1a295..59da0f9 100644 --- a/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-120.snap +++ b/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-120.snap @@ -9,4 +9,6 @@ We have next things: - thing 2; - thing 3. -#f[Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim aeque doleamus animo, cum corpore dolemus, fieri.] +#f[ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim aeque doleamus animo, cum corpore dolemus, fieri. +] diff --git a/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-40.snap b/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-40.snap index cb1a295..59da0f9 100644 --- a/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-40.snap +++ b/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-40.snap @@ -9,4 +9,6 @@ We have next things: - thing 2; - thing 3. -#f[Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim aeque doleamus animo, cum corpore dolemus, fieri.] +#f[ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim aeque doleamus animo, cum corpore dolemus, fieri. +] diff --git a/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-80.snap b/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-80.snap index cb1a295..59da0f9 100644 --- a/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-80.snap +++ b/tests/snapshots/assets__check_file@typstfmt-82-non-converge-list.typ-80.snap @@ -9,4 +9,6 @@ We have next things: - thing 2; - thing 3. -#f[Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim aeque doleamus animo, cum corpore dolemus, fieri.] +#f[ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim aeque doleamus animo, cum corpore dolemus, fieri. +] diff --git a/tests/snapshots/assets__check_file@undergraduate-math.typ-120.snap b/tests/snapshots/assets__check_file@undergraduate-math.typ-120.snap index 94ae066..7be1418 100644 --- a/tests/snapshots/assets__check_file@undergraduate-math.typ-120.snap +++ b/tests/snapshots/assets__check_file@undergraduate-math.typ-120.snap @@ -98,9 +98,7 @@ input_file: tests/assets/undergraduate-math.typ // Title #align( center, - link("https://github.com/johanvx/typst-undergradmath")[ - #text(large, headcolor)[*Typst Math for Undergrads*] - ], + link("https://github.com/johanvx/typst-undergradmath")[#text(large, headcolor)[*Typst Math for Undergrads*]], ) This is a Typst port with typst #sys.version of _#LaTeX Math for Undergrads_ by Jim Hefferon. @@ -751,12 +749,10 @@ Definition by cases can be easily obtained with the `cases` function. table( columns: 2, column-gutter: 1em, - [ - $ f_n = cases( + [$ f_n = cases( a &"if" n = 0, r dot f_(n - 1) &"else" - ) $ - ], + ) $], [ ``` $ f_n = cases( diff --git a/tests/snapshots/assets__check_file@undergraduate-math.typ-40.snap b/tests/snapshots/assets__check_file@undergraduate-math.typ-40.snap index 242134d..50904a1 100644 --- a/tests/snapshots/assets__check_file@undergraduate-math.typ-40.snap +++ b/tests/snapshots/assets__check_file@undergraduate-math.typ-40.snap @@ -148,10 +148,9 @@ input_file: tests/assets/undergraduate-math.typ link( "https://github.com/johanvx/typst-undergradmath", )[ - #text( - large, - headcolor, - )[*Typst Math for Undergrads*] + #text(large, headcolor)[ + *Typst Math for Undergrads* + ] ], ) @@ -169,7 +168,8 @@ The original version is available at #link("https://gitlab.com/jim.hefferon/unde table( columns: (1fr, 2fr), [!!], - [Tricky. A simpler method is needed.], + [Tricky. A simpler method is needed. + ], ), ) // #figure( @@ -787,9 +787,11 @@ Fix the size with the `lr` function. table( columns: 2, column-gutter: 1em, - [$ + [ + $ lr([sum_(k = 0)^n e^(k^2)], size: #50%) - $], + $ + ], [ ``` lr([sum_(k = 0)^n e^(k^2)], size: #50%) @@ -805,9 +807,8 @@ To have them grow with the enclosed formula, also use the `lr` function. table( columns: 2, column-gutter: 1em, - [$ - lr(angle.l i, 2^(2^i) angle.r) - $], + [$ lr(angle.l i, 2^(2^i) angle.r) $ + ], [ ``` lr(angle.l i, 2^(2^i) angle.r) @@ -849,9 +850,8 @@ The `lr` function also allows to scale unmatched delimiters and one-side fences. table( columns: 2, column-gutter: 1em, - [$ - lr(frac(dif f, dif x) |)_(x_0) - $], + [$ lr(frac(dif f, dif x) |)_(x_0) $ + ], [ ``` lr(frac(dif f, dif x) |)_(x_0) @@ -999,17 +999,21 @@ The last three here are display style. lim_(h -> 0) (f(x + h) - f(x)) / h ``` ], - [$ + [ + $ integral x^2 dif x = x^3 \/ 3 + C - $], + $ + ], [ ``` integral x^2 dif x = x^3 \/ 3 + C ``` ], - [$ + [ + $ nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z) - $], + $ + ], [ ``` nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z) @@ -1032,27 +1036,30 @@ For permutations use $n^(underline(r))$ from `n^(underline(r))` (some authors us table( columns: 2, column-gutter: 1em, - [$ + [ + $ sigma^2 = sqrt(sum(x_i - mu)^2 \/ N) - $], + $ + ], [ ``` sigma^2 = sqrt(sum(x_i - mu)^2 \/ N) ``` ], - [$ - E(X) = mu_X = sum(x_i - P(x_i)) - $], + [$ E(X) = mu_X = sum(x_i - P(x_i)) $ + ], [ ``` E(X) = mu_X = sum(x_i - P(x_i)) ``` ], - [$ + [ + $ 1 / sqrt(2 sigma^2 pi) e^(- ( x - mu )^2 / (2 sigma^2)) - $], + $ + ], [ ``` 1 / sqrt(2 sigma^2 pi) e^(- (x - mu)^2 / (2 sigma^2)) @@ -1069,7 +1076,7 @@ See also the Typst Documentation at #link("https://typst.app/docs"). #block( inset: 4pt, stroke: (top: headcolor), - text( - headcolor, - )[johanvx (https://github.com/johanvx) #h(1fr) #date], + text(headcolor)[ + johanvx (https://github.com/johanvx) #h(1fr) #date + ], ) diff --git a/tests/snapshots/assets__check_file@undergraduate-math.typ-80.snap b/tests/snapshots/assets__check_file@undergraduate-math.typ-80.snap index 2a352a1..fe63f7c 100644 --- a/tests/snapshots/assets__check_file@undergraduate-math.typ-80.snap +++ b/tests/snapshots/assets__check_file@undergraduate-math.typ-80.snap @@ -759,10 +759,12 @@ Definition by cases can be easily obtained with the `cases` function. columns: 2, column-gutter: 1em, [ - $ f_n = cases( + $ + f_n = cases( a &"if" n = 0, r dot f_(n - 1) &"else" - ) $ + ) + $ ], [ ``` @@ -872,9 +874,11 @@ The last three here are display style. integral x^2 dif x = x^3 \/ 3 + C ``` ], - [$ + [ + $ nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z) - $], + $ + ], [ ``` nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z) diff --git a/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-120.snap b/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-120.snap index 4cfcaa2..31e0890 100644 --- a/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-120.snap +++ b/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-120.snap @@ -3,6 +3,8 @@ source: tests/assets.rs expression: doc_string input_file: tests/assets/unit/markup/code-inside-content.typ --- -#[#{ +#[ + #{ let something = 0 - }\ ] + }\ +] diff --git a/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-40.snap b/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-40.snap index 4cfcaa2..31e0890 100644 --- a/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-40.snap +++ b/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-40.snap @@ -3,6 +3,8 @@ source: tests/assets.rs expression: doc_string input_file: tests/assets/unit/markup/code-inside-content.typ --- -#[#{ +#[ + #{ let something = 0 - }\ ] + }\ +] diff --git a/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-80.snap b/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-80.snap index 4cfcaa2..31e0890 100644 --- a/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-80.snap +++ b/tests/snapshots/assets__check_file@unit-markup-code-inside-content.typ-80.snap @@ -3,6 +3,8 @@ source: tests/assets.rs expression: doc_string input_file: tests/assets/unit/markup/code-inside-content.typ --- -#[#{ +#[ + #{ let something = 0 - }\ ] + }\ +] diff --git a/tests/snapshots/assets__check_file@unit-markup-content-func.typ-120.snap b/tests/snapshots/assets__check_file@unit-markup-content-func.typ-120.snap index 3a0cf0b..1b17580 100644 --- a/tests/snapshots/assets__check_file@unit-markup-content-func.typ-120.snap +++ b/tests/snapshots/assets__check_file@unit-markup-content-func.typ-120.snap @@ -6,6 +6,8 @@ input_file: tests/assets/unit/markup/content-func.typ #doc-style.show-parameter-block( "length", "number", - [The size of the mark in the direction it is pointing. The width of a legend items preview picture, a small preview of the graph the legend item belongs to.], + [ + The size of the mark in the direction it is pointing. The width of a legend items preview picture, a small preview of the graph the legend item belongs to. + ], default: 0.2cm, ) diff --git a/tests/snapshots/assets__check_file@unit-markup-content-func.typ-40.snap b/tests/snapshots/assets__check_file@unit-markup-content-func.typ-40.snap index 3a0cf0b..1b17580 100644 --- a/tests/snapshots/assets__check_file@unit-markup-content-func.typ-40.snap +++ b/tests/snapshots/assets__check_file@unit-markup-content-func.typ-40.snap @@ -6,6 +6,8 @@ input_file: tests/assets/unit/markup/content-func.typ #doc-style.show-parameter-block( "length", "number", - [The size of the mark in the direction it is pointing. The width of a legend items preview picture, a small preview of the graph the legend item belongs to.], + [ + The size of the mark in the direction it is pointing. The width of a legend items preview picture, a small preview of the graph the legend item belongs to. + ], default: 0.2cm, ) diff --git a/tests/snapshots/assets__check_file@unit-markup-content-func.typ-80.snap b/tests/snapshots/assets__check_file@unit-markup-content-func.typ-80.snap index 3a0cf0b..1b17580 100644 --- a/tests/snapshots/assets__check_file@unit-markup-content-func.typ-80.snap +++ b/tests/snapshots/assets__check_file@unit-markup-content-func.typ-80.snap @@ -6,6 +6,8 @@ input_file: tests/assets/unit/markup/content-func.typ #doc-style.show-parameter-block( "length", "number", - [The size of the mark in the direction it is pointing. The width of a legend items preview picture, a small preview of the graph the legend item belongs to.], + [ + The size of the mark in the direction it is pointing. The width of a legend items preview picture, a small preview of the graph the legend item belongs to. + ], default: 0.2cm, ) diff --git a/tests/snapshots/assets__check_file@unit-markup-content-math.typ-40.snap b/tests/snapshots/assets__check_file@unit-markup-content-math.typ-40.snap index 1160940..67cb303 100644 --- a/tests/snapshots/assets__check_file@unit-markup-content-math.typ-40.snap +++ b/tests/snapshots/assets__check_file@unit-markup-content-math.typ-40.snap @@ -4,7 +4,9 @@ expression: doc_string input_file: tests/assets/unit/markup/content-math.typ --- #( - [$ + [ + $ 111111111111111111111111111111111111111111111111111111111111111111111111111111111111 - $] + $ + ] ) diff --git a/tests/snapshots/assets__check_file@unit-markup-content-math.typ-80.snap b/tests/snapshots/assets__check_file@unit-markup-content-math.typ-80.snap index 1160940..67cb303 100644 --- a/tests/snapshots/assets__check_file@unit-markup-content-math.typ-80.snap +++ b/tests/snapshots/assets__check_file@unit-markup-content-math.typ-80.snap @@ -4,7 +4,9 @@ expression: doc_string input_file: tests/assets/unit/markup/content-math.typ --- #( - [$ + [ + $ 111111111111111111111111111111111111111111111111111111111111111111111111111111111111 - $] + $ + ] ) diff --git a/tests/snapshots/assets__check_file@unit-markup-content-space.typ-120.snap b/tests/snapshots/assets__check_file@unit-markup-content-space.typ-120.snap new file mode 100644 index 0000000..c59ae4e --- /dev/null +++ b/tests/snapshots/assets__check_file@unit-markup-content-space.typ-120.snap @@ -0,0 +1,8 @@ +--- +source: tests/assets.rs +expression: doc_string +input_file: tests/assets/unit/markup/content-space.typ +--- +#{ + show heading.where(level: 1): it => box(width: 100%)[#v(0.5em)] +} diff --git a/tests/snapshots/assets__check_file@unit-markup-content-space.typ-40.snap b/tests/snapshots/assets__check_file@unit-markup-content-space.typ-40.snap new file mode 100644 index 0000000..d2b8826 --- /dev/null +++ b/tests/snapshots/assets__check_file@unit-markup-content-space.typ-40.snap @@ -0,0 +1,10 @@ +--- +source: tests/assets.rs +expression: doc_string +input_file: tests/assets/unit/markup/content-space.typ +--- +#{ + show heading.where( + level: 1, + ): it => box(width: 100%)[#v(0.5em)] +} diff --git a/tests/snapshots/assets__check_file@unit-markup-content-space.typ-80.snap b/tests/snapshots/assets__check_file@unit-markup-content-space.typ-80.snap new file mode 100644 index 0000000..c59ae4e --- /dev/null +++ b/tests/snapshots/assets__check_file@unit-markup-content-space.typ-80.snap @@ -0,0 +1,8 @@ +--- +source: tests/assets.rs +expression: doc_string +input_file: tests/assets/unit/markup/content-space.typ +--- +#{ + show heading.where(level: 1): it => box(width: 100%)[#v(0.5em)] +} diff --git a/tests/snapshots/assets__check_file@unit-markup-raw.typ-120.snap b/tests/snapshots/assets__check_file@unit-markup-raw.typ-120.snap index f4f4782..59f27d7 100644 --- a/tests/snapshots/assets__check_file@unit-markup-raw.typ-120.snap +++ b/tests/snapshots/assets__check_file@unit-markup-raw.typ-120.snap @@ -63,10 +63,12 @@ let a = 1; ``` ] -#text[```typ +#text[ + ```typ #import "@preview/cetz:0.2.1" #cetz.canvas({ import cetz.draw: * ... }) - ```] + ``` +] diff --git a/tests/snapshots/assets__check_file@unit-markup-raw.typ-40.snap b/tests/snapshots/assets__check_file@unit-markup-raw.typ-40.snap index f4f4782..59f27d7 100644 --- a/tests/snapshots/assets__check_file@unit-markup-raw.typ-40.snap +++ b/tests/snapshots/assets__check_file@unit-markup-raw.typ-40.snap @@ -63,10 +63,12 @@ let a = 1; ``` ] -#text[```typ +#text[ + ```typ #import "@preview/cetz:0.2.1" #cetz.canvas({ import cetz.draw: * ... }) - ```] + ``` +] diff --git a/tests/snapshots/assets__check_file@unit-markup-raw.typ-80.snap b/tests/snapshots/assets__check_file@unit-markup-raw.typ-80.snap index f4f4782..59f27d7 100644 --- a/tests/snapshots/assets__check_file@unit-markup-raw.typ-80.snap +++ b/tests/snapshots/assets__check_file@unit-markup-raw.typ-80.snap @@ -63,10 +63,12 @@ let a = 1; ``` ] -#text[```typ +#text[ + ```typ #import "@preview/cetz:0.2.1" #cetz.canvas({ import cetz.draw: * ... }) - ```] + ``` +] diff --git a/tests/snapshots/assets__check_file@unit-windows.typ-120.snap b/tests/snapshots/assets__check_file@unit-windows.typ-120.snap new file mode 100644 index 0000000..22fda8a --- /dev/null +++ b/tests/snapshots/assets__check_file@unit-windows.typ-120.snap @@ -0,0 +1,12 @@ +--- +source: tests/assets.rs +expression: doc_string +input_file: tests/assets/unit/windows.typ +--- +#align( + center, + table(columns: 2, column-gutter: 1em, [$ f_n = cases( + a &"if" n = 0, + r dot f_(n - 1) &"else" + ) $]), +) diff --git a/tests/snapshots/assets__check_file@unit-windows.typ-40.snap b/tests/snapshots/assets__check_file@unit-windows.typ-40.snap new file mode 100644 index 0000000..ebc2ae8 --- /dev/null +++ b/tests/snapshots/assets__check_file@unit-windows.typ-40.snap @@ -0,0 +1,20 @@ +--- +source: tests/assets.rs +expression: doc_string +input_file: tests/assets/unit/windows.typ +--- +#align( + center, + table( + columns: 2, + column-gutter: 1em, + [ + $ + f_n = cases( + a &"if" n = 0, + r dot f_(n - 1) &"else" + ) + $ + ], + ), +) diff --git a/tests/snapshots/assets__check_file@unit-windows.typ-80.snap b/tests/snapshots/assets__check_file@unit-windows.typ-80.snap new file mode 100644 index 0000000..ebc2ae8 --- /dev/null +++ b/tests/snapshots/assets__check_file@unit-windows.typ-80.snap @@ -0,0 +1,20 @@ +--- +source: tests/assets.rs +expression: doc_string +input_file: tests/assets/unit/windows.typ +--- +#align( + center, + table( + columns: 2, + column-gutter: 1em, + [ + $ + f_n = cases( + a &"if" n = 0, + r dot f_(n - 1) &"else" + ) + $ + ], + ), +)