Skip to content

Commit f68c9e1

Browse files
authored
fix: correct handling comments directly in equations and spaces in math (#252)
* fix: correct handling comments directly in equations * fix: indent and linebreak in mat
1 parent 544b22b commit f68c9e1

25 files changed

+904
-94
lines changed

crates/typstyle-core/src/pretty/code_list.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ impl<'a> PrettyPrinter<'a> {
103103
add_trailing_sep_single: is_explicit,
104104
add_trailing_sep_always: ends_with_comma,
105105
delim: if is_explicit { ("(", ")") } else { ("", "") },
106+
tight_delim: !is_explicit,
107+
no_indent: !is_explicit,
106108
..Default::default()
107109
})
108110
}

crates/typstyle-core/src/pretty/flow.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub struct FlowStylist<'a> {
5959
printer: &'a PrettyPrinter<'a>,
6060
doc: ArenaDoc<'a>,
6161
space_after: bool,
62+
at_line_start: bool,
6263
}
6364

6465
impl<'a> FlowStylist<'a> {
@@ -67,6 +68,7 @@ impl<'a> FlowStylist<'a> {
6768
doc: printer.arena.nil(),
6869
printer,
6970
space_after: false,
71+
at_line_start: true,
7072
}
7173
}
7274

@@ -75,7 +77,9 @@ impl<'a> FlowStylist<'a> {
7577
if node.kind() == SyntaxKind::BlockComment {
7678
self.push_doc(doc, true, true);
7779
} else {
78-
self.space_after = true;
80+
if !self.at_line_start {
81+
self.space_after = true;
82+
}
7983
self.push_doc(doc, true, false);
8084
}
8185
}
@@ -86,6 +90,11 @@ impl<'a> FlowStylist<'a> {
8690
}
8791
self.doc += doc;
8892
self.space_after = space_after;
93+
self.at_line_start = false;
94+
}
95+
96+
pub fn enter_new_line(&mut self) {
97+
self.at_line_start = true;
8998
}
9099

91100
pub fn into_doc(self) -> ArenaDoc<'a> {
@@ -130,6 +139,7 @@ impl<'a> PrettyPrinter<'a> {
130139
&& child.text().has_linebreak()
131140
{
132141
flow.push_doc(self.arena.hardline(), false, false);
142+
flow.enter_new_line();
133143
} else if child.kind() == SyntaxKind::Hash {
134144
flow.push_doc(self.arena.text("#"), true, false);
135145
peek_hash = true;

crates/typstyle-core/src/pretty/list.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub struct ListStyle {
3939
pub separator: &'static str,
4040
/// The delimiter of the list.
4141
pub delim: (&'static str, &'static str),
42+
/// Whether can add linebreaks inside the delimiters.
43+
pub tight_delim: bool,
4244
/// Whether to add an addition space inside the delimiters if the list is flat.
4345
pub add_delim_space: bool,
4446
/// Whether a trailing separator is need if the list contains only one item.
@@ -51,19 +53,23 @@ pub struct ListStyle {
5153
pub omit_delim_flat: bool,
5254
/// Whether can omit the delimiter if the list is empty.
5355
pub omit_delim_empty: bool,
56+
/// Whether not to indent the items.
57+
pub no_indent: bool,
5458
}
5559

5660
impl Default for ListStyle {
5761
fn default() -> Self {
5862
Self {
5963
separator: ",",
6064
delim: ("(", ")"),
65+
tight_delim: false,
6166
add_delim_space: false,
6267
add_trailing_sep_single: false,
6368
add_trailing_sep_always: false,
6469
omit_delim_single: false,
6570
omit_delim_flat: false,
6671
omit_delim_empty: false,
72+
no_indent: false,
6773
}
6874
}
6975
}
@@ -289,19 +295,27 @@ impl<'a> ListStylist<'a> {
289295
};
290296
match fold_style {
291297
FoldStyle::Never => {
292-
let mut inner = arena.nil();
293-
for item in self.items.into_iter() {
298+
let mut inner = if sty.tight_delim {
299+
arena.nil()
300+
} else {
301+
arena.hardline()
302+
};
303+
for (i, item) in self.items.into_iter().enumerate() {
294304
match item {
295305
Item::Comment(cmt) => inner += cmt + arena.hardline(),
296306
Item::Commented { body, after } => {
297-
inner += body + sep.clone() + after + arena.hardline();
307+
inner += body + sep.clone() + after;
308+
if !sty.tight_delim || i + 1 != self.item_count {
309+
inner += arena.hardline();
310+
}
298311
}
299312
Item::Linebreak(n) => inner += arena.hardline().repeat_n(n),
300313
}
301314
}
302-
(arena.hardline() + inner)
303-
.nest(indent as isize)
304-
.enclose(delim.0, delim.1)
315+
if !sty.no_indent {
316+
inner = inner.nest(indent as isize);
317+
}
318+
inner.enclose(delim.0, delim.1)
305319
}
306320
FoldStyle::Always => {
307321
let mut inner = arena.nil();
@@ -342,15 +356,25 @@ impl<'a> ListStylist<'a> {
342356
body,
343357
after: Option::None,
344358
} => {
345-
let follow = if !is_last
359+
let follow = if is_last && sty.tight_delim {
360+
arena.nil()
361+
} else if !is_last
346362
|| sty.add_trailing_sep_always
347363
|| is_single && sty.add_trailing_sep_single
348364
{
349365
sep.clone()
350366
} else {
351367
sep.clone().flat_alt(arena.nil())
352368
};
353-
let ln = if is_last { arena.line_() } else { arena.line() };
369+
let ln = if is_last {
370+
if sty.tight_delim {
371+
arena.nil()
372+
} else {
373+
arena.line_()
374+
}
375+
} else {
376+
arena.line()
377+
};
354378
inner += body + follow + ln;
355379
}
356380
Item::Commented {
@@ -366,7 +390,15 @@ impl<'a> ListStylist<'a> {
366390
} else {
367391
after
368392
};
369-
let ln = if is_last { arena.line_() } else { arena.line() };
393+
let ln = if is_last {
394+
if sty.tight_delim {
395+
arena.nil()
396+
} else {
397+
arena.line_()
398+
}
399+
} else {
400+
arena.line()
401+
};
370402
inner += body + follow_break.flat_alt(follow_flat) + ln;
371403
}
372404
Item::Linebreak(n) => inner += arena.line().repeat_n(n),
@@ -375,7 +407,9 @@ impl<'a> ListStylist<'a> {
375407
if is_single && sty.omit_delim_single {
376408
inner.group()
377409
} else {
378-
inner = (arena.line_() + inner).nest(indent as isize);
410+
if !sty.no_indent {
411+
inner = (arena.line_() + inner).nest(indent as isize);
412+
}
379413
if sty.omit_delim_flat {
380414
inner
381415
.enclose(

crates/typstyle-core/src/pretty/math.rs

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,67 @@
11
use pretty::DocAllocator;
2-
use typst_syntax::{ast::*, SyntaxKind};
2+
use typst_syntax::{ast::*, SyntaxKind, SyntaxNode};
33

44
use crate::ext::StrExt;
55

6-
use super::{flow::FlowItem, ArenaDoc, Mode, PrettyPrinter};
6+
use super::{
7+
flow::FlowItem,
8+
list::{ListStyle, ListStylist},
9+
style::FoldStyle,
10+
ArenaDoc, Mode, PrettyPrinter,
11+
};
712

813
impl<'a> PrettyPrinter<'a> {
914
pub(super) fn convert_equation(&'a self, equation: Equation<'a>) -> ArenaDoc<'a> {
1015
let _g = self.with_mode(Mode::Math);
1116

12-
let body = self.convert_flow_like(equation.to_untyped(), |child| {
13-
if let Some(math) = child.cast::<Math>() {
14-
let has_trailing_linebreak = (math.exprs().last())
15-
.is_some_and(|expr| matches!(expr, Expr::Linebreak(_)))
16-
&& (equation.to_untyped().children().nth_back(1))
17-
.is_some_and(|it| it.kind() == SyntaxKind::Space)
18-
&& (equation.to_untyped().children().nth_back(2))
19-
.is_some_and(|it| it.kind() == SyntaxKind::Math);
20-
let body = self.convert_math(math);
21-
let body = if !equation.block() && has_trailing_linebreak {
22-
body + self.arena.space()
23-
} else {
24-
body
25-
};
26-
FlowItem::spaced(body)
27-
} else {
28-
FlowItem::none()
17+
let is_block = equation.block();
18+
19+
let convert_math_padded = |child: &'a SyntaxNode| {
20+
let math = child.cast::<Math>()?;
21+
if math.to_untyped().children().len() == 0 {
22+
return Option::None;
2923
}
30-
});
24+
let has_trailing_linebreak = (math.exprs().last())
25+
.is_some_and(|expr| matches!(expr, Expr::Linebreak(_)))
26+
&& (equation.to_untyped().children().nth_back(1))
27+
.is_some_and(|it| it.kind() == SyntaxKind::Space)
28+
&& (equation.to_untyped().children().nth_back(2))
29+
.is_some_and(|it| it.kind() == SyntaxKind::Math);
30+
let body = self.convert_math(math);
31+
let body = if !is_block && has_trailing_linebreak {
32+
body + self.arena.space()
33+
} else {
34+
body
35+
};
36+
Some(body)
37+
};
3138

32-
let doc = if equation.block() {
33-
if self.is_break_suppressed() {
34-
(self.arena.space() + body).nest(self.config.tab_spaces as isize)
35-
+ self.arena.space()
39+
if is_block {
40+
let fold_style = if self.is_break_suppressed() {
41+
FoldStyle::Always
3642
} else if self.attr_store.is_multiline(equation.to_untyped()) {
37-
(self.arena.hardline() + body).nest(self.config.tab_spaces as isize)
38-
+ self.arena.hardline()
43+
FoldStyle::Never
3944
} else {
40-
((self.arena.line() + body).nest(self.config.tab_spaces as isize)
41-
+ self.arena.line())
42-
.group()
43-
}
45+
FoldStyle::Fit
46+
};
47+
ListStylist::new(self)
48+
.with_fold_style(fold_style)
49+
.process_list_impl(equation.to_untyped(), convert_math_padded)
50+
.print_doc(ListStyle {
51+
separator: "",
52+
delim: ("$", "$"),
53+
add_delim_space: is_block,
54+
..Default::default()
55+
})
4456
} else {
45-
body.nest(self.config.tab_spaces as isize)
46-
};
47-
doc.enclose("$", "$")
57+
self.convert_flow_like(equation.to_untyped(), |child| {
58+
convert_math_padded(child)
59+
.map(FlowItem::spaced)
60+
.unwrap_or(FlowItem::none())
61+
})
62+
.nest(self.config.tab_spaces as isize)
63+
.enclose("$", "$")
64+
}
4865
}
4966

5067
pub(super) fn convert_math(&'a self, math: Math<'a>) -> ArenaDoc<'a> {
@@ -132,6 +149,8 @@ impl<'a> PrettyPrinter<'a> {
132149
self.convert_flow_like(math_attach.to_untyped(), |node| {
133150
if let Some(expr) = node.cast::<Expr>() {
134151
FlowItem::tight(self.convert_expr(expr))
152+
} else if node.kind() == SyntaxKind::Space {
153+
FlowItem::none()
135154
} else {
136155
FlowItem::tight(self.convert_verbatim_untyped(node))
137156
}
@@ -158,6 +177,8 @@ impl<'a> PrettyPrinter<'a> {
158177
self.convert_flow_like(math_root.to_untyped(), |node| {
159178
if let Some(expr) = node.cast::<Expr>() {
160179
FlowItem::tight(self.convert_expr(expr))
180+
} else if node.kind() == SyntaxKind::Space {
181+
FlowItem::none()
161182
} else {
162183
FlowItem::tight(self.convert_verbatim_untyped(node))
163184
}

crates/typstyle-core/src/pretty/mod.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,6 @@ impl<'a> PrettyPrinter<'a> {
8080
}
8181
}
8282

83-
#[allow(dead_code)]
84-
fn check_unformattable(&'a self, node: &'a SyntaxNode) -> Option<ArenaDoc<'a>> {
85-
if self.attr_store.is_unformattable(node) {
86-
Some(self.format_disabled(node))
87-
} else {
88-
None
89-
}
90-
}
91-
9283
fn format_disabled(&'a self, node: &'a SyntaxNode) -> ArenaDoc<'a> {
9384
self.arena.text(node.clone().into_text().to_string())
9485
}

0 commit comments

Comments
 (0)