1use crate::format_element::{Document, FormatElement, InternedDocument, LineMode, TextMetrics};
2
3#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
4pub enum IndentStyle {
5 #[default]
6 Tab,
7 Space,
8}
9
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
11pub enum LineEnding {
12 #[default]
13 Lf,
14 CrLf,
15}
16
17impl LineEnding {
18 const fn as_str(self) -> &'static str {
19 match self {
20 Self::Lf => "\n",
21 Self::CrLf => "\r\n",
22 }
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct PrinterOptions {
28 pub indent_style: IndentStyle,
29 pub indent_width: u8,
30 pub line_width: u16,
31 pub line_ending: LineEnding,
32}
33
34impl Default for PrinterOptions {
35 fn default() -> Self {
36 Self {
37 indent_style: IndentStyle::Tab,
38 indent_width: 4,
39 line_width: 80,
40 line_ending: LineEnding::Lf,
41 }
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Printed {
47 code: String,
48}
49
50impl Printed {
51 #[must_use]
52 pub fn as_code(&self) -> &str {
53 &self.code
54 }
55
56 #[must_use]
57 pub fn into_code(self) -> String {
58 self.code
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct PrintError {
64 message: String,
65}
66
67impl std::fmt::Display for PrintError {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 f.write_str(&self.message)
70 }
71}
72
73impl std::error::Error for PrintError {}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76enum PrintMode {
77 Flat,
78 Expanded,
79}
80
81#[derive(Debug, Clone)]
82pub struct Printer {
83 options: PrinterOptions,
84}
85
86impl Printer {
87 #[must_use]
88 pub fn new(options: PrinterOptions) -> Self {
89 Self { options }
90 }
91
92 pub fn print(&self, document: &Document) -> Result<Printed, PrintError> {
93 self.print_with_capacity(document, 0)
94 }
95
96 pub fn print_with_capacity(
97 &self,
98 document: &Document,
99 output_capacity: usize,
100 ) -> Result<Printed, PrintError> {
101 let mut output = String::with_capacity(output_capacity);
102 let mut state = PrinterState::new(self.options);
103 let mut queue = PrintQueue::new(document.as_slice());
104 let mut mode_stack = Vec::new();
105
106 while let Some(element) = queue.next(&mut state, &mut mode_stack) {
107 match element {
108 FormatElement::Token(text) => state.push_token(text, &mut output),
109 FormatElement::Text(text) => {
110 state.push_text(text.as_str(), text.metrics(), &mut output);
111 }
112 FormatElement::Space => state.push_token(" ", &mut output),
113 FormatElement::Line(line_mode) => {
114 let mode = mode_stack.last().copied().unwrap_or(PrintMode::Expanded);
115 match (mode, line_mode) {
116 (PrintMode::Flat, LineMode::Soft) => {}
117 (PrintMode::Flat, LineMode::SoftOrSpace) => {
118 state.push_token(" ", &mut output);
119 }
120 (_, LineMode::Hard) | (PrintMode::Expanded, _) => {
121 state.push_newline(&mut output);
122 }
123 }
124 }
125 FormatElement::Indent(document) => {
126 queue.push(document.as_slice(), None, 1, &mut state, &mut mode_stack);
127 }
128 FormatElement::Group(document) => {
129 let mode = if self.fits(document.as_slice(), PrintMode::Flat, state.column) {
130 PrintMode::Flat
131 } else {
132 PrintMode::Expanded
133 };
134 queue.push(
135 document.as_slice(),
136 Some(mode),
137 0,
138 &mut state,
139 &mut mode_stack,
140 );
141 }
142 FormatElement::BestFit { flat, expanded } => {
143 let (variant, mode) =
144 if self.fits(flat.as_slice(), PrintMode::Flat, state.column) {
145 (flat.as_slice(), PrintMode::Flat)
146 } else {
147 (expanded.as_slice(), PrintMode::Expanded)
148 };
149 queue.push(variant, Some(mode), 0, &mut state, &mut mode_stack);
150 }
151 FormatElement::Verbatim(text) => {
152 state.push_text(text.as_str(), text.metrics(), &mut output);
153 }
154 }
155 }
156
157 Ok(Printed { code: output })
158 }
159
160 fn fits(&self, document: &[FormatElement], mode: PrintMode, column: usize) -> bool {
161 let mut width = column;
162 let mut queue = MeasureQueue::new(document, mode);
163 let line_width = usize::from(self.options.line_width);
164
165 while let Some((element, mode)) = queue.next() {
166 match element {
167 FormatElement::Token(text) => width += text.len(),
168 FormatElement::Text(text) => {
169 if let Some(single_line_width) = text.metrics().single_line_width() {
170 width += single_line_width;
171 } else {
172 width += text.metrics().first_line_width();
173 return width <= line_width;
174 }
175 }
176 FormatElement::Space => width += 1,
177 FormatElement::Line(line_mode) => match (mode, line_mode) {
178 (PrintMode::Flat, LineMode::Soft) => {}
179 (PrintMode::Flat, LineMode::SoftOrSpace) => width += 1,
180 (_, LineMode::Hard) | (PrintMode::Expanded, _) => return width <= line_width,
181 },
182 FormatElement::Indent(document) => queue.push(document, None),
183 FormatElement::Group(document) => queue.push(document, Some(PrintMode::Flat)),
184 FormatElement::BestFit { flat, .. } => queue.push(flat, Some(PrintMode::Flat)),
185 FormatElement::Verbatim(text) => {
186 if let Some(single_line_width) = text.metrics().single_line_width() {
187 width += single_line_width;
188 } else {
189 width += text.metrics().first_line_width();
190 return width <= line_width;
191 }
192 }
193 }
194
195 if width > line_width {
196 return false;
197 }
198 }
199
200 true
201 }
202}
203
204#[derive(Debug, Clone)]
205struct PrintFrame<'a> {
206 elements: &'a [FormatElement],
207 index: usize,
208 mode: Option<PrintMode>,
209 indent_delta: usize,
210}
211
212#[derive(Debug, Clone)]
213struct PrintQueue<'a> {
214 frames: Vec<PrintFrame<'a>>,
215}
216
217impl<'a> PrintQueue<'a> {
218 fn new(elements: &'a [FormatElement]) -> Self {
219 Self {
220 frames: vec![PrintFrame {
221 elements,
222 index: 0,
223 mode: None,
224 indent_delta: 0,
225 }],
226 }
227 }
228
229 fn push(
230 &mut self,
231 elements: &'a [FormatElement],
232 mode: Option<PrintMode>,
233 indent_delta: usize,
234 state: &mut PrinterState,
235 mode_stack: &mut Vec<PrintMode>,
236 ) {
237 if indent_delta > 0 {
238 state.indent_level += indent_delta;
239 }
240 if let Some(mode) = mode {
241 mode_stack.push(mode);
242 }
243 self.frames.push(PrintFrame {
244 elements,
245 index: 0,
246 mode,
247 indent_delta,
248 });
249 }
250
251 fn next(
252 &mut self,
253 state: &mut PrinterState,
254 mode_stack: &mut Vec<PrintMode>,
255 ) -> Option<&'a FormatElement> {
256 loop {
257 let frame = self.frames.last_mut()?;
258 if frame.index < frame.elements.len() {
259 let element = &frame.elements[frame.index];
260 frame.index += 1;
261 return Some(element);
262 }
263
264 let frame = self.frames.pop()?;
265 state.indent_level = state.indent_level.saturating_sub(frame.indent_delta);
266 if frame.mode.is_some() {
267 mode_stack.pop();
268 }
269 }
270 }
271}
272
273#[derive(Debug, Clone)]
274struct MeasureFrame<'a> {
275 elements: &'a [FormatElement],
276 index: usize,
277 mode: Option<PrintMode>,
278}
279
280#[derive(Debug, Clone)]
281struct MeasureQueue<'a> {
282 frames: Vec<MeasureFrame<'a>>,
283 mode_stack: Vec<PrintMode>,
284}
285
286impl<'a> MeasureQueue<'a> {
287 fn new(elements: &'a [FormatElement], mode: PrintMode) -> Self {
288 Self {
289 frames: vec![MeasureFrame {
290 elements,
291 index: 0,
292 mode: Some(mode),
293 }],
294 mode_stack: vec![mode],
295 }
296 }
297
298 fn push(&mut self, document: &'a InternedDocument, mode: Option<PrintMode>) {
299 if let Some(mode) = mode {
300 self.mode_stack.push(mode);
301 }
302 self.frames.push(MeasureFrame {
303 elements: document.as_slice(),
304 index: 0,
305 mode,
306 });
307 }
308
309 fn next(&mut self) -> Option<(&'a FormatElement, PrintMode)> {
310 loop {
311 let frame = self.frames.last_mut()?;
312 if frame.index < frame.elements.len() {
313 let element = &frame.elements[frame.index];
314 frame.index += 1;
315 let mode = self
316 .mode_stack
317 .last()
318 .copied()
319 .unwrap_or(PrintMode::Expanded);
320 return Some((element, mode));
321 }
322
323 let frame = self.frames.pop()?;
324 if frame.mode.is_some() {
325 self.mode_stack.pop();
326 }
327 }
328 }
329}
330
331#[derive(Debug, Clone, Copy)]
332struct PrinterState {
333 options: PrinterOptions,
334 indent_level: usize,
335 column: usize,
336 line_has_content: bool,
337}
338
339impl PrinterState {
340 fn new(options: PrinterOptions) -> Self {
341 Self {
342 options,
343 indent_level: 0,
344 column: 0,
345 line_has_content: false,
346 }
347 }
348
349 fn push_token(&mut self, text: &str, output: &mut String) {
350 if !self.line_has_content {
351 self.push_indent(output);
352 }
353 output.push_str(text);
354 self.column += text.len();
355 self.line_has_content = true;
356 }
357
358 fn push_text(&mut self, text: &str, metrics: TextMetrics, output: &mut String) {
359 if !self.line_has_content {
360 self.push_indent(output);
361 }
362
363 output.push_str(text);
364 if let Some(width) = metrics.single_line_width() {
365 self.column += width;
366 self.line_has_content = true;
367 } else {
368 self.column = if metrics.ends_with_newline() {
369 0
370 } else {
371 metrics.last_line_width()
372 };
373 self.line_has_content = !metrics.ends_with_newline();
374 }
375 }
376
377 fn push_newline(&mut self, output: &mut String) {
378 output.push_str(self.options.line_ending.as_str());
379 self.column = 0;
380 self.line_has_content = false;
381 }
382
383 fn push_indent(&mut self, output: &mut String) {
384 if self.line_has_content || self.indent_level == 0 {
385 return;
386 }
387
388 match self.options.indent_style {
389 IndentStyle::Tab => {
390 for _ in 0..self.indent_level {
391 output.push('\t');
392 }
393 }
394 IndentStyle::Space => {
395 for _ in 0..(self.indent_level * usize::from(self.options.indent_width)) {
396 output.push(' ');
397 }
398 }
399 }
400
401 self.column += self.indent_width();
402 self.line_has_content = true;
403 }
404
405 fn indent_width(&self) -> usize {
406 self.indent_level * usize::from(self.options.indent_width)
407 }
408}