1use core::mem::take;
2
3use crate::alloc::prelude::*;
4use crate::alloc::{self, VecDeque};
5use crate::ast::{Kind, Span};
6use crate::compile::{Error, ErrorKind, FmtOptions, Result, WithSpan};
7use crate::grammar::{Ignore, Node, Tree};
8use crate::{Diagnostics, SourceId};
9
10use super::{INDENT, NL, NL_CHAR, WS};
11
12pub(super) enum Comments {
14 Line,
16 Prefix,
18 Suffix,
20 Infix,
23}
24
25#[derive(Clone, Copy, Debug)]
26struct Comment {
27 span: Span,
28 before: usize,
29 line: bool,
30}
31
32#[repr(transparent)]
34pub(super) struct Source(str);
35
36impl Source {
37 fn new(source: &str) -> &Self {
38 unsafe { &*(source as *const str as *const Self) }
40 }
41
42 pub(super) fn get(&self, span: Span) -> Result<&str> {
44 let Some(source) = self.0.get(span.range()) else {
45 return Err(Error::new(span, ErrorKind::BadSpan { len: self.0.len() }));
46 };
47
48 Ok(source)
49 }
50
51 pub(super) fn is_at_least(&self, span: Span, mut count: usize) -> Result<bool> {
54 let source = self.get(span)?;
55
56 for c in source.chars() {
57 if c.is_whitespace() {
58 continue;
59 }
60
61 let Some(c) = count.checked_sub(1) else {
62 return Ok(true);
63 };
64
65 count = c;
66 }
67
68 Ok(false)
69 }
70}
71
72#[repr(transparent)]
74pub(super) struct Buffer(String);
75
76impl Buffer {
77 fn new(output: &mut String) -> &mut Self {
78 unsafe { &mut *(output as *mut String as *mut Self) }
80 }
81
82 #[inline]
83 fn is_empty(&self) -> bool {
84 self.0.is_empty()
85 }
86
87 #[inline]
88 fn str(&mut self, s: &str) -> alloc::Result<()> {
89 self.0.try_push_str(s)
90 }
91
92 fn lines(&mut self, indent: usize, lines: usize) -> alloc::Result<()> {
93 if lines == 0 {
94 return Ok(());
95 }
96
97 for _ in 0..lines {
98 self.0.try_push_str(NL)?;
99 }
100
101 for _ in 0..indent {
102 self.0.try_push_str(INDENT)?;
103 }
104
105 Ok(())
106 }
107}
108
109pub(crate) struct Formatter<'a> {
111 span: Span,
112 pub(super) source: &'a Source,
113 source_id: SourceId,
114 o: &'a mut Buffer,
115 pub(super) options: &'a FmtOptions,
116 diagnostics: &'a mut Diagnostics,
117 comments: VecDeque<Comment>,
118 lines: usize,
119 use_lines: bool,
120 ws: bool,
121 indent: usize,
122}
123
124impl<'a> Formatter<'a> {
125 pub(super) fn new(
127 span: Span,
128 source: &'a str,
129 source_id: SourceId,
130 o: &'a mut String,
131 options: &'a FmtOptions,
132 diagnostics: &'a mut Diagnostics,
133 ) -> Self {
134 Self {
135 span,
136 source: Source::new(source),
137 source_id,
138 o: Buffer::new(o),
139 options,
140 diagnostics,
141 comments: VecDeque::new(),
142 lines: 0,
143 use_lines: false,
144 ws: false,
145 indent: 0,
146 }
147 }
148
149 pub(crate) fn ignore(&mut self, node: Node<'a>) -> Result<()> {
151 self.process_comments(node.walk_from())?;
152 Ok(())
153 }
154
155 pub(crate) fn write_owned(&mut self, node: Node<'a>) -> Result<()> {
157 self.flush_whitespace(false)?;
158 self.write_node(&node)?;
159 self.process_comments(node.walk_from())?;
160 Ok(())
161 }
162
163 pub(crate) fn write_raw(&mut self, node: Node<'a>) -> Result<()> {
165 self.write_node(&node)?;
166 self.process_comments(node.walk_from())?;
167 Ok(())
168 }
169
170 pub(crate) fn lit(&mut self, s: &str) -> Result<()> {
172 self.flush_whitespace(true)?;
175 self.o.str(s).with_span(self.span)?;
176 Ok(())
177 }
178
179 pub(super) fn comments(&mut self, comments: Comments) -> Result<()> {
181 if self.comments.is_empty() {
182 return Ok(());
183 }
184
185 match comments {
186 Comments::Line => {
187 self.comments_line(false)?;
188 }
189 Comments::Prefix | Comments::Suffix => {
190 self.comments_ws(
195 matches!(comments, Comments::Suffix),
196 matches!(comments, Comments::Prefix),
197 )?;
198 }
199 Comments::Infix => {
200 self.comments_ws(false, false)?;
201 }
202 }
203
204 Ok(())
205 }
206
207 pub(super) fn indent(&mut self, indent: isize) -> Result<()> {
209 if indent != 0 {
210 self.indent = self.checked_indent(indent)?;
211 }
212
213 Ok(())
214 }
215
216 pub(crate) fn nl(&mut self, lines: usize) -> Result<()> {
224 if lines == 0 {
225 return Ok(());
226 }
227
228 self.comments_line(true)?;
229
230 if self.lines == 0 {
232 self.lines = lines;
233 }
234
235 self.use_lines = true;
237 Ok(())
238 }
239
240 pub(super) fn ws(&mut self) -> Result<()> {
247 self.comments_ws(true, false)?;
248 self.ws = true;
249 Ok(())
250 }
251
252 pub(super) fn flush_prefix_comments(&mut self, tree: &'a Tree) -> Result<()> {
254 self.process_comments(tree.walk())?;
255 self.comments(Comments::Line)?;
256 self.use_lines = self.lines > 0;
257 Ok(())
258 }
259
260 fn comments_line(&mut self, same_line: bool) -> Result<()> {
262 while let Some(c) = self.comments.front() {
263 if same_line && c.before != 0 {
264 break;
265 }
266
267 if !self.o.is_empty() {
268 if c.before == 0 {
269 self.o.str(WS).with_span(c.span)?;
270 } else {
271 self.o
272 .lines(self.indent, c.before.min(2))
273 .with_span(c.span)?;
274 }
275 }
276
277 let source = self.source.get(c.span)?;
278 let source = if c.line { source.trim_end() } else { source };
279 self.o.str(source).with_span(c.span)?;
280
281 _ = self.comments.pop_front();
282 }
283
284 Ok(())
285 }
286
287 fn comments_ws(&mut self, prefix: bool, suffix: bool) -> Result<()> {
289 if self.comments.is_empty() {
290 return Ok(());
291 }
292
293 let mut any = false;
294
295 while let Some(c) = self.comments.front() {
296 if c.line {
297 break;
298 }
299
300 if (prefix || any) && !self.o.is_empty() {
301 self.o.str(WS).with_span(c.span)?;
302 }
303
304 let source = self.source.get(c.span)?;
305 self.o.str(source).with_span(c.span)?;
306
307 any = true;
308
309 _ = self.comments.pop_front();
310 }
311
312 if suffix && any {
313 self.o.str(WS).with_span(self.span)?;
314 }
315
316 Ok(())
317 }
318
319 fn process_comments<I>(&mut self, iter: I) -> Result<()>
320 where
321 I: IntoIterator<Item = Node<'a>>,
322 {
323 for node in iter {
324 if !node.has_children() && !self.write_comment(node)? {
325 break;
326 }
327 }
328
329 Ok(())
330 }
331
332 fn write_comment(&mut self, node: Node<'a>) -> Result<bool> {
333 let span = node.span();
334
335 match node.kind() {
336 Kind::Comment | Kind::MultilineComment(..) => {
337 self.comments
338 .try_push_back(Comment {
339 span,
340 before: take(&mut self.lines),
341 line: matches!(node.kind(), Kind::Comment),
342 })
343 .with_span(span)?;
344
345 Ok(true)
346 }
347 Kind::Whitespace => {
348 let source = self.source.get(span)?;
349 let count = source.chars().filter(|c| *c == NL_CHAR).count();
350
351 if self.lines == 0 {
352 self.lines = count;
353 }
354
355 Ok(true)
356 }
357 _ => Ok(false),
358 }
359 }
360
361 fn checked_indent(&mut self, level: isize) -> Result<usize> {
362 let Some(indent) = self.indent.checked_add_signed(level) else {
363 return Err(Error::new(
364 self.span,
365 ErrorKind::BadIndent {
366 level,
367 indent: self.indent,
368 },
369 ));
370 };
371
372 Ok(indent)
373 }
374
375 fn write_node(&mut self, node: &Node<'_>) -> Result<()> {
376 let source = self.source.get(node.span())?;
377 self.span = node.span();
378 self.o.str(source).with_span(self.span)?;
379 Ok(())
380 }
381
382 pub(crate) fn flush_whitespace(&mut self, preserve: bool) -> Result<()> {
383 if self.use_lines && self.lines > 0 {
384 self.o.lines(self.indent, self.lines.min(2))?;
385 self.ws = false;
386 self.use_lines = false;
387 self.lines = 0;
388 }
389
390 if self.ws {
391 self.o.str(WS).with_span(self.span)?;
392 self.ws = false;
393 }
394
395 if !preserve {
396 self.lines = 0;
397 self.use_lines = false;
398 }
399
400 Ok(())
401 }
402}
403
404impl<'a> Ignore<'a> for Formatter<'a> {
405 fn error(&mut self, error: Error) -> alloc::Result<()> {
406 self.diagnostics.error(self.source_id, error)
407 }
408
409 fn ignore(&mut self, node: Node<'a>) -> Result<()> {
410 Formatter::ignore(self, node)
411 }
412}