1pub mod context;
2pub mod prelude;
3pub mod style;
4
5mod args;
6mod code_chain;
7mod code_flow;
8mod code_list;
9mod code_misc;
10mod comment;
11mod func_call;
12mod import;
13mod layout;
14mod markup;
15mod math;
16mod math_align;
17mod parened_expr;
18mod table;
19mod text;
20mod util;
21
22pub use context::{Context, Mode};
23use prelude::*;
24use style::{is_multiline_flavored, FoldStyle};
25use typst_syntax::{ast::*, SyntaxNode};
26
27use crate::{ext::StrExt, AttrStore, Config, Error};
28
29pub struct PrettyPrinter<'a> {
30 config: Config,
31 attr_store: AttrStore,
32 arena: Arena<'a>,
33}
34
35impl<'a> PrettyPrinter<'a> {
36 pub fn new(config: Config, attr_store: AttrStore) -> Self {
37 Self {
38 config,
39 attr_store,
40 arena: Arena::new(),
41 }
42 }
43
44 pub fn config(&self) -> &Config {
45 &self.config
46 }
47
48 fn get_fold_style(&self, ctx: Context, node: impl AstNode<'a>) -> FoldStyle {
49 self.get_fold_style_untyped(ctx, node.to_untyped())
50 }
51
52 fn get_fold_style_untyped(&self, ctx: Context, node: &'a SyntaxNode) -> FoldStyle {
53 let is_multiline = is_multiline_flavored(node);
54 if ctx.break_suppressed {
55 return if is_multiline {
56 FoldStyle::Fit
57 } else {
58 FoldStyle::Always
59 };
60 }
61 if is_multiline {
62 FoldStyle::Never
63 } else {
64 FoldStyle::Fit
65 }
66 }
67}
68
69impl<'a> PrettyPrinter<'a> {
71 pub(crate) fn indent(&'a self, doc: ArenaDoc<'a>) -> ArenaDoc<'a> {
72 doc.nest(self.config.tab_spaces as isize)
73 }
74
75 pub(crate) fn block_indent(&'a self, doc: ArenaDoc<'a>) -> ArenaDoc<'a> {
76 self.indent(self.arena.line_() + doc) + self.arena.line_()
77 }
78}
79
80impl<'a> PrettyPrinter<'a> {
81 fn check_disabled(&'a self, node: &'a SyntaxNode) -> Option<ArenaDoc<'a>> {
82 if self.attr_store.is_format_disabled(node) {
83 Some(self.convert_verbatim_untyped(node))
84 } else {
85 None
86 }
87 }
88
89 fn convert_verbatim(&'a self, node: impl AstNode<'a>) -> ArenaDoc<'a> {
91 self.convert_verbatim_untyped(node.to_untyped())
92 }
93
94 fn convert_verbatim_untyped(&'a self, node: &'a SyntaxNode) -> ArenaDoc<'a> {
96 let text = node.clone().into_text();
97 if !text.has_linebreak() {
98 return self.arena.text(text.to_string());
99 }
100 self.arena
102 .intersperse(
103 node.clone().into_text().lines().map(str::to_string),
104 self.arena.hardline(),
105 )
106 .dedent_to_root()
107 }
108
109 fn convert_trivia(&'a self, node: impl AstNode<'a>) -> ArenaDoc<'a> {
111 self.convert_trivia_untyped(node.to_untyped())
112 }
113
114 fn convert_trivia_untyped(&'a self, node: &'a SyntaxNode) -> ArenaDoc<'a> {
116 self.arena.text(node.text().as_str())
117 }
118
119 pub fn try_convert_with_mode(
120 &'a self,
121 node: &'a SyntaxNode,
122 mode: Mode,
123 ) -> Result<ArenaDoc<'a>, Error> {
124 let ctx = Context::default().with_mode(mode);
125 let doc = if let Some(markup) = node.cast() {
126 self.convert_markup(ctx, markup)
127 } else if let Some(code) = node.cast() {
128 self.convert_code(ctx, code)
129 } else if let Some(math) = node.cast() {
130 self.convert_math(ctx, math)
131 } else if let Some(expr) = node.cast() {
132 self.convert_expr(ctx, expr)
133 } else if let Some(pattern) = node.cast() {
134 self.convert_pattern(ctx, pattern)
135 } else {
136 return Err(Error::SyntaxError);
137 };
138 Ok(doc)
139 }
140
141 pub fn convert_expr(&'a self, ctx: Context, expr: Expr<'a>) -> ArenaDoc<'a> {
142 if let Some(res) = self.check_disabled(expr.to_untyped()) {
143 return res;
144 }
145 self.convert_expr_impl(ctx, expr)
146 }
147
148 fn convert_expr_impl(&'a self, ctx: Context, expr: Expr<'a>) -> ArenaDoc<'a> {
149 match expr {
150 Expr::Text(t) => self.convert_text(t),
151 Expr::Space(s) => self.convert_space(ctx, s),
152 Expr::Linebreak(b) => self.convert_trivia(b),
153 Expr::Parbreak(b) => self.convert_parbreak(b),
154 Expr::Escape(e) => self.convert_trivia(e),
155 Expr::Shorthand(s) => self.convert_trivia(s),
156 Expr::SmartQuote(s) => self.convert_trivia(s),
157 Expr::Strong(s) => self.convert_strong(ctx, s),
158 Expr::Emph(e) => self.convert_emph(ctx, e),
159 Expr::Raw(r) => self.convert_raw(ctx, r),
160 Expr::Link(l) => self.convert_trivia(l),
161 Expr::Label(l) => self.convert_trivia(l),
162 Expr::Ref(r) => self.convert_ref(ctx, r),
163 Expr::Heading(h) => self.convert_heading(ctx, h),
164 Expr::List(l) => self.convert_list_item(ctx, l),
165 Expr::Enum(e) => self.convert_enum_item(ctx, e),
166 Expr::Term(t) => self.convert_term_item(ctx, t),
167 Expr::Equation(e) => self.convert_equation(ctx, e),
168 Expr::Math(m) => self.convert_math(ctx, m),
169 Expr::MathText(math_text) => self.convert_trivia(math_text),
170 Expr::MathIdent(mi) => self.convert_trivia(mi),
171 Expr::MathAlignPoint(map) => self.convert_trivia(map),
172 Expr::MathDelimited(md) => self.convert_math_delimited(ctx, md),
173 Expr::MathAttach(ma) => self.convert_math_attach(ctx, ma),
174 Expr::MathPrimes(mp) => self.convert_math_primes(ctx, mp),
175 Expr::MathFrac(mf) => self.convert_math_frac(ctx, mf),
176 Expr::MathRoot(mr) => self.convert_math_root(ctx, mr),
177 Expr::MathShorthand(ms) => self.convert_trivia(ms),
178 Expr::Ident(i) => self.convert_ident(i),
179 Expr::None(_) => self.convert_literal("none"),
180 Expr::Auto(_) => self.convert_literal("auto"),
181 Expr::Bool(b) => self.convert_trivia(b),
182 Expr::Int(i) => self.convert_trivia(i),
183 Expr::Float(f) => self.convert_trivia(f),
184 Expr::Numeric(n) => self.convert_trivia(n),
185 Expr::Str(s) => self.convert_trivia(s),
186 Expr::Code(c) => self.convert_code_block(ctx, c),
187 Expr::Content(c) => self.convert_content_block(ctx, c),
188 Expr::Parenthesized(p) => self.convert_parenthesized(ctx, p),
189 Expr::Array(a) => self.convert_array(ctx, a),
190 Expr::Dict(d) => self.convert_dict(ctx, d),
191 Expr::Unary(u) => self.convert_unary(ctx, u),
192 Expr::Binary(b) => self.convert_binary(ctx, b),
193 Expr::FieldAccess(fa) => self.convert_field_access(ctx, fa),
194 Expr::FuncCall(fc) => self.convert_func_call(ctx, fc),
195 Expr::Closure(c) => self.convert_closure(ctx, c),
196 Expr::Let(l) => self.convert_let_binding(ctx, l),
197 Expr::DestructAssign(da) => self.convert_destruct_assignment(ctx, da),
198 Expr::Set(s) => self.convert_set_rule(ctx, s),
199 Expr::Show(s) => self.convert_show_rule(ctx, s),
200 Expr::Contextual(c) => self.convert_contextual(ctx, c),
201 Expr::Conditional(c) => self.convert_conditional(ctx, c),
202 Expr::While(w) => self.convert_while_loop(ctx, w),
203 Expr::For(f) => self.convert_for_loop(ctx, f),
204 Expr::Import(i) => self.convert_import(ctx, i),
205 Expr::Include(i) => self.convert_include(ctx, i),
206 Expr::Break(_) => self.convert_literal("break"),
207 Expr::Continue(_) => self.convert_literal("continue"),
208 Expr::Return(r) => self.convert_return(ctx, r),
209 }
210 }
211
212 fn convert_literal(&'a self, literal: &'a str) -> ArenaDoc<'a> {
213 self.arena.text(literal)
214 }
215}