typstyle_core/pretty/
comment.rs1use typst_syntax::{SyntaxKind, SyntaxNode};
2
3use super::{prelude::*, Context, PrettyPrinter};
4
5impl<'a> PrettyPrinter<'a> {
6 pub(super) fn convert_comment(&'a self, _ctx: Context, node: &'a SyntaxNode) -> ArenaDoc<'a> {
7 comment(&self.arena, node)
8 }
9}
10
11enum CommentStyle {
12 Plain,
13 Bullet,
14}
15
16pub fn comment<'a>(arena: &'a Arena<'a>, node: &'a SyntaxNode) -> ArenaDoc<'a> {
18 if node.kind() == SyntaxKind::LineComment {
19 line_comment(arena, node).as_line_suffix()
20 } else if node.kind() == SyntaxKind::BlockComment {
21 block_comment(arena, node)
22 } else {
23 unreachable!("the node should not be a comment node!")
24 }
25}
26
27pub fn line_comment<'a>(arena: &'a Arena<'a>, node: &'a SyntaxNode) -> ArenaDoc<'a> {
28 arena.text(node.text().as_str())
29}
30
31pub fn block_comment<'a>(arena: &'a Arena<'a>, node: &'a SyntaxNode) -> ArenaDoc<'a> {
33 let line_num = node.text().lines().count();
35 if line_num == 0 {
36 return arena.text(node.text().as_str());
37 }
38 let text = node.text().as_str();
40 let style = get_comment_style(text);
41 match style {
42 CommentStyle::Plain => align_multiline(arena, text),
43 CommentStyle::Bullet => align_multiline_simple(arena, text),
44 }
45}
46
47fn get_comment_style(text: &str) -> CommentStyle {
48 if text
49 .lines()
50 .skip(1)
51 .all(|line| line.trim_start().starts_with('*'))
52 {
53 CommentStyle::Bullet } else {
55 CommentStyle::Plain }
57}
58
59fn get_follow_leading(text: &str) -> Option<usize> {
62 text.lines()
63 .skip(1)
64 .map(|line| line.chars().position(|c| c != ' ').unwrap_or(usize::MAX))
65 .min()
66}
67
68fn align_multiline<'a>(arena: &'a Arena<'a>, text: &'a str) -> ArenaDoc<'a> {
70 let leading = get_follow_leading(text).unwrap();
71 let mut doc = arena.nil();
72 for (i, line) in text.lines().enumerate() {
73 if i == 0 {
74 doc += line;
75 } else {
76 doc += arena.hardline();
77 if line.len() > leading {
78 doc += &line[leading..]; } }
81 }
82 doc.align()
83}
84
85fn align_multiline_simple<'a>(arena: &'a Arena<'a>, text: &'a str) -> ArenaDoc<'a> {
87 let mut doc = arena.nil();
88 for (i, line) in text.lines().enumerate() {
89 if i > 0 {
90 doc += arena.hardline();
91 }
92 doc += line.trim_start();
93 }
94 doc.nest(1).align()
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_align() {
103 let cmt = "/* 0
104 --- 1
105 -- 2
106 --- 3
107 -- 4 */";
108 let arena = Arena::new();
109 let leading = get_follow_leading(cmt).unwrap();
110 assert_eq!(leading, 4);
111 let doc = arena.text("lorem ipsum") + arena.space() + align_multiline(&arena, cmt);
112 let result = doc.print(80).to_string();
113 assert_eq!(
115 result,
116 "lorem ipsum /* 0
117 --- 1
118 -- 2
119 --- 3
120 -- 4 */"
121 );
122 }
123
124 #[test]
125 fn test_align2() {
126 let cmt = "/* 0
127 * 1
128 * 2
129 * 3
130 */";
131 let arena = Arena::new();
132 let doc = arena.text("lorem ipsum") + arena.space() + align_multiline_simple(&arena, cmt);
133 let result = doc.print(80).to_string();
134 assert_eq!(
136 result,
137 "lorem ipsum /* 0
138 * 1
139 * 2
140 * 3
141 */"
142 );
143 }
144}