lutra_compiler/printer/
mod.rs1#![allow(unused_variables)]
2mod common;
25mod defs;
26mod expr;
27mod test;
28mod types;
29
30use crate::codespan;
31use crate::parser::lexer::{Token, TokenKind};
32use crate::pr;
33
34pub fn print_ty(ty: &pr::Ty) -> String {
35 let mut p = Printer::new(&CONFIG_NO_WRAP, None);
36 ty.print(&mut p).unwrap();
37
38 assert!(p.edits.is_empty());
39 p.buffer
40}
41
42pub fn print_source(source: &pr::Source, trivia: Option<&[Token]>) -> Vec<codespan::TextEdit> {
43 let mut p = Printer::new(&CONFIG_PRETTY, trivia);
44 p.buffer_span.source_id = source.span.source_id;
45
46 source.print(&mut p).unwrap();
47
48 p.finish_edit();
49 p.edits
50}
51
52trait PrintSource {
55 #[must_use]
56 fn print<'c>(&self, p: &mut Printer<'c>) -> Option<()>;
57
58 fn span(&self) -> Option<crate::Span>;
61}
62
63struct Printer<'c> {
64 config: &'c Config,
66
67 buffer: String,
69
70 buffer_span: crate::Span,
74
75 rem_width: u16,
77
78 indent: u16,
80
81 single_line: bool,
83
84 pending_suffix: u16,
88
89 trivia: Option<&'c [Token]>,
91
92 edits: Vec<codespan::TextEdit>,
95}
96
97struct Config {
98 indent_size: u16,
99 max_width: u16,
100}
101
102const CONFIG_PRETTY: Config = Config {
103 indent_size: 2,
104 max_width: 80,
105};
106
107const CONFIG_NO_WRAP: Config = Config {
108 indent_size: 2,
109 max_width: u16::MAX,
110};
111
112impl<'c> Printer<'c> {
113 fn new(config: &'c Config, trivia: Option<&'c [Token]>) -> Printer<'c> {
114 Printer {
115 config,
116
117 buffer: String::new(),
118 buffer_span: crate::Span {
119 source_id: 0, start: 0,
121 len: 0,
122 },
123 indent: 0,
124 rem_width: config.max_width,
125 single_line: false,
126 pending_suffix: 0,
127
128 trivia,
129 edits: Vec::new(),
130 }
131 }
132
133 #[must_use]
138 fn push<S: AsRef<str>>(&mut self, snippet: S) -> Option<()> {
139 for c in snippet.as_ref().chars() {
140 if c == '\n' {
141 self.rem_width = self.config.max_width;
142 continue;
143 }
144 if self.rem_width == 0 {
145 return None;
146 }
147 self.rem_width -= 1;
148 }
149 self.buffer += snippet.as_ref();
150 Some(())
151 }
152
153 fn push_unchecked<S: AsRef<str>>(&mut self, snippet: S) {
157 self.buffer += snippet.as_ref();
158 }
159
160 fn mark_printed(&mut self, span: &Option<crate::Span>) {
163 if let Some(span) = span {
164 self.buffer_span.set_end_of(span);
165 }
166 }
167
168 fn mark_printed_up_to(&mut self, offset: u32) {
171 self.buffer_span.set_end(offset);
172 }
173
174 fn fork(&self) -> Printer<'c> {
178 Printer {
179 config: self.config,
180 buffer: String::new(),
181 buffer_span: crate::Span {
182 source_id: self.buffer_span.source_id,
183 start: self.buffer_span.end(),
184 len: 0,
185 },
186 rem_width: self.rem_width,
187 indent: self.indent,
188 single_line: self.single_line,
189 pending_suffix: self.pending_suffix,
190
191 trivia: self.trivia,
192 edits: Vec::new(),
193 }
194 }
195
196 fn merge(&mut self, forked: Printer<'c>) {
198 self.rem_width = forked.rem_width;
199 self.trivia = forked.trivia;
200 self.buffer += &forked.buffer;
201 self.buffer_span.set_end_of(&forked.buffer_span);
202 self.edits.extend(forked.edits);
203 }
204
205 #[must_use]
208 fn consume(&mut self, width: usize) -> Option<()> {
209 self.rem_width = self.rem_width.checked_sub(width as u16)?;
210 Some(())
211 }
212
213 fn indent(&mut self) {
215 self.indent += 1;
216 }
217
218 fn dedent(&mut self) {
220 self.indent -= 1;
221 }
222
223 fn new_line(&mut self) {
226 let indent_width = self.indent * self.config.indent_size;
227 self.rem_width = self.config.max_width.saturating_sub(indent_width);
228
229 self.pending_suffix = 0;
233
234 let len_without_trailing_whitespace = self.buffer.trim_end_matches(' ').len();
235 self.buffer.truncate(len_without_trailing_whitespace);
236
237 self.buffer.push('\n');
238 self.buffer.push_str(&" ".repeat(indent_width as usize));
239 }
240
241 #[must_use]
243 fn require_single_line(&mut self, span: Option<crate::Span>) -> Option<()> {
244 self.single_line = true;
245 self.consume(self.pending_suffix as usize)?;
246 self.pending_suffix = 0;
247
248 self.validate_no_comments(span.map(|s| s.end()))?;
249 Some(())
250 }
251
252 fn take_trivia(&mut self, until: u32) -> Option<&'c Token> {
253 let trivia = self.trivia.as_mut()?;
254
255 let t = trivia.first().filter(|t| t.span.end() <= until)?;
256
257 *trivia = &trivia[1..];
258 Some(t)
259 }
260
261 fn take_trivia_comment(&mut self, until: u32) -> Option<&'c str> {
262 let trivia = self.trivia.as_mut()?;
263
264 let t = trivia.first().filter(|t| t.span.end() <= until)?;
265
266 let TokenKind::Comment(s) = &t.kind else {
267 return None;
268 };
269 *trivia = &trivia[1..];
270 Some(s)
271 }
272
273 fn take_trivia_new_line(&mut self, until: u32) -> Option<()> {
274 let trivia = self.trivia.as_mut()?;
275
276 let t = trivia.first().filter(|t| t.span.end() <= until)?;
277
278 let TokenKind::NewLine = &t.kind else {
279 return None;
280 };
281 *trivia = &trivia[1..];
282 Some(())
283 }
284
285 fn inject_trivia_leading(&mut self, until: Option<u32>) {
286 let Some(until) = until else { return };
287
288 let mut nl_count = 0;
289
290 while let Some(trivia) = self.take_trivia(until) {
291 match &trivia.kind {
292 TokenKind::NewLine => {
293 nl_count += 1;
295 if nl_count == 2 {
296 self.new_line();
297 }
298 }
299 TokenKind::Comment(comment) => {
300 self.push_unchecked("# ");
301 self.push_unchecked(comment);
302 self.new_line();
303 nl_count = 0;
304 }
305
306 _ => (),
307 }
308 }
309 }
310
311 fn inject_trivia_prev_inline(&mut self, until: Option<u32>) {
312 let Some(until) = until else { return };
313
314 while let Some(comment) = self.take_trivia_comment(until) {
315 self.push_unchecked(" # ");
316 self.push_unchecked(comment);
317 }
318 }
319
320 fn inject_trivia_trailing(&mut self, until: Option<u32>) {
321 let Some(until) = until else { return };
322
323 let mut had_empty_line = false;
324 while let Some(token) = self.take_trivia(until) {
325 let TokenKind::Comment(comment) = &token.kind else {
326 continue;
327 };
328
329 self.new_line();
330 if !had_empty_line {
331 self.new_line();
332 had_empty_line = true;
333 }
334
335 self.push_unchecked("# ");
336 self.push_unchecked(comment);
337 }
338 }
339
340 fn validate_no_comments(&mut self, until: Option<u32>) -> Option<()> {
341 let Some(until) = until else {
342 return Some(());
343 };
344
345 while let Some(trivia) = self.take_trivia(until) {
346 if let TokenKind::Comment(comment) = &trivia.kind {
347 return None;
348 }
349 }
350 Some(())
351 }
352
353 fn finish_edit(&mut self) {
357 let code = std::mem::take(&mut self.buffer);
358 self.edits.push(codespan::TextEdit {
359 span: self.buffer_span,
360 new_text: code,
361 });
362 self.buffer_span.start = self.buffer_span.end();
363 self.buffer_span.len = 0;
364 }
365}
366
367impl std::fmt::Debug for Printer<'_> {
368 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369 f.debug_struct("Printer")
370 .field("rem_width", &self.rem_width)
371 .field("single_line", &self.single_line)
372 .field("pending_suffix", &self.pending_suffix)
373 .finish()
374 }
375}
376
377impl<T> PrintSource for &T
378where
379 T: PrintSource,
380{
381 fn print<'c>(&self, p: &mut Printer<'c>) -> Option<()> {
382 T::print(self, p)
383 }
384
385 fn span(&self) -> Option<crate::Span> {
386 T::span(self)
387 }
388}