1use crate::{DEBUG, DEBUG_INDENT};
5use ring::RingBuffer;
6use std::{borrow::Cow, cmp, collections::VecDeque, iter};
7
8mod convenience;
9mod helpers;
10mod ring;
11
12const MIN_SPACE: isize = 40;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum Breaks {
18 Consistent,
19 Inconsistent,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23enum IndentStyle {
24 Visual,
30 Block { offset: isize },
38}
39
40#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
41pub(crate) struct BreakToken {
42 pub(crate) offset: isize,
43 pub(crate) blank_space: usize,
44 pub(crate) pre_break: Option<&'static str>,
45 pub(crate) post_break: Option<&'static str>,
46 pub(crate) if_nonempty: bool,
47 pub(crate) never_break: bool,
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
51pub(crate) struct BeginToken {
52 indent: IndentStyle,
53 breaks: Breaks,
54}
55
56#[derive(Debug, PartialEq, Eq)]
57pub(crate) enum Token {
58 String(Cow<'static, str>),
62 Break(BreakToken),
63 Begin(BeginToken),
64 End,
65}
66
67#[derive(Copy, Clone, Debug)]
68enum PrintFrame {
69 Fits(Breaks),
70 Broken(usize, Breaks),
71}
72
73pub(crate) const SIZE_INFINITY: isize = 0xffff;
74
75#[derive(Debug)]
76pub struct Printer {
77 out: String,
78 space: isize,
80 buf: RingBuffer<BufEntry>,
82 left_total: isize,
84 right_total: isize,
86 scan_stack: VecDeque<usize>,
93 print_stack: Vec<PrintFrame>,
95 indent: usize,
97 pending_indentation: usize,
99 last_printed: Option<Token>,
102
103 margin: isize,
105 indent_config: Option<usize>,
107}
108
109#[derive(Debug)]
110pub struct BufEntry {
111 token: Token,
112 size: isize,
113}
114
115impl Printer {
116 pub fn new(margin: usize, use_tab_with_size: Option<usize>) -> Self {
117 let margin = (margin as isize).clamp(MIN_SPACE, SIZE_INFINITY - 1);
118 Self {
119 out: String::new(),
120 space: margin,
121 buf: RingBuffer::new(),
122 left_total: 0,
123 right_total: 0,
124 scan_stack: VecDeque::new(),
125 print_stack: Vec::new(),
126 indent: 0,
127 pending_indentation: 0,
128 last_printed: None,
129
130 margin,
131 indent_config: use_tab_with_size,
132 }
133 }
134
135 pub(crate) fn space_left(&self) -> usize {
155 for i in self.buf.index_range().rev() {
157 let token = &self.buf[i].token;
158
159 if let Token::Break(break_token) = token
160 && break_token.blank_space as isize >= SIZE_INFINITY
161 && !break_token.never_break
162 {
163 return self.margin as usize;
164 }
165
166 if !matches!(token, Token::End) {
168 break;
169 }
170 }
171
172 (if self.space < 0 { self.margin } else { self.space }) as usize
175 }
176
177 pub(crate) fn last_token(&self) -> Option<&Token> {
178 self.last_token_still_buffered().or(self.last_printed.as_ref())
179 }
180
181 pub(crate) fn last_token_still_buffered(&self) -> Option<&Token> {
182 if self.buf.is_empty() {
183 return None;
184 }
185 Some(&self.buf.last().token)
186 }
187
188 pub(crate) fn replace_last_token_still_buffered(&mut self, token: Token) {
190 self.buf.last_mut().token = token;
191 }
192
193 pub(crate) fn find_and_replace_last_token_still_buffered<F>(
210 &mut self,
211 new_token: Token,
212 predicate: F,
213 ) where
214 F: FnOnce(&Token) -> bool,
215 {
216 for i in self.buf.index_range().rev() {
217 let token = &self.buf[i].token;
218 if matches!(token, Token::End) {
219 continue;
221 }
222
223 if predicate(token) {
225 self.buf[i].token = new_token;
226 }
227 break;
228 }
229 }
230
231 fn scan_eof(&mut self) {
232 if !self.scan_stack.is_empty() {
233 self.check_stack(0);
234 self.advance_left();
235 }
236 }
237
238 fn scan_begin(&mut self, token: BeginToken) {
239 if self.scan_stack.is_empty() {
240 self.left_total = 1;
241 self.right_total = 1;
242 self.buf.clear();
243 }
244 let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total });
245 self.scan_stack.push_back(right);
246 }
247
248 fn scan_end(&mut self) {
249 if self.scan_stack.is_empty() {
250 self.print_end();
251 } else {
252 if !self.buf.is_empty()
253 && let Token::Break(break_token) = self.buf.last().token
254 {
255 if self.buf.len() >= 2
256 && let Token::Begin(_) = self.buf.second_last().token
257 {
258 self.buf.pop_last();
259 self.buf.pop_last();
260 self.scan_stack.pop_back();
261 self.scan_stack.pop_back();
262 self.right_total -= break_token.blank_space as isize;
263 return;
264 }
265 if break_token.if_nonempty {
266 self.buf.pop_last();
267 self.scan_stack.pop_back();
268 self.right_total -= break_token.blank_space as isize;
269 }
270 }
271 let right = self.buf.push(BufEntry { token: Token::End, size: -1 });
272 self.scan_stack.push_back(right);
273 }
274 }
275
276 pub(crate) fn scan_break(&mut self, token: BreakToken) {
277 if self.scan_stack.is_empty() {
278 self.left_total = 1;
279 self.right_total = 1;
280 self.buf.clear();
281 } else {
282 self.check_stack(0);
283 }
284 let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total });
285 self.scan_stack.push_back(right);
286 self.right_total += token.blank_space as isize;
287 }
288
289 fn scan_string(&mut self, string: Cow<'static, str>) {
290 if self.scan_stack.is_empty() {
291 self.print_string(&string);
292 } else {
293 let len = string.len() as isize;
294 self.buf.push(BufEntry { token: Token::String(string), size: len });
295 self.right_total += len;
296 self.check_stream();
297 }
298 }
299
300 #[track_caller]
301 pub(crate) fn offset(&mut self, offset: isize) {
302 if self.buf.is_empty() {
305 return;
306 }
307 match &mut self.buf.last_mut().token {
308 Token::Break(token) => token.offset += offset,
309 Token::Begin(_) | Token::String(_) => {}
310 Token::End => unreachable!(),
311 }
312 }
313
314 pub(crate) fn ends_with(&self, ch: char) -> bool {
315 for i in self.buf.index_range().rev() {
316 if let Token::String(token) = &self.buf[i].token {
317 return token.ends_with(ch);
318 }
319 }
320 self.out.ends_with(ch)
321 }
322
323 fn check_stream(&mut self) {
324 while self.right_total - self.left_total > self.space {
325 if *self.scan_stack.front().unwrap() == self.buf.index_range().start {
326 self.scan_stack.pop_front().unwrap();
327 self.buf.first_mut().size = SIZE_INFINITY;
328 }
329
330 self.advance_left();
331
332 if self.buf.is_empty() {
333 break;
334 }
335 }
336 }
337
338 fn advance_left(&mut self) {
339 while self.buf.first().size >= 0 {
340 let left = self.buf.pop_first();
341
342 match &left.token {
343 Token::String(string) => {
344 self.left_total += left.size;
345 self.print_string(string);
346 }
347 Token::Break(token) => {
348 self.left_total += token.blank_space as isize;
349 self.print_break(*token, left.size);
350 }
351 Token::Begin(token) => self.print_begin(*token, left.size),
352 Token::End => self.print_end(),
353 }
354
355 self.last_printed = Some(left.token);
356
357 if self.buf.is_empty() {
358 break;
359 }
360 }
361 }
362
363 fn check_stack(&mut self, mut depth: usize) {
364 while let Some(&index) = self.scan_stack.back() {
365 let entry = &mut self.buf[index];
366 match entry.token {
367 Token::Begin(_) => {
368 if depth == 0 {
369 break;
370 }
371 self.scan_stack.pop_back().unwrap();
372 entry.size += self.right_total;
373 depth -= 1;
374 }
375 Token::End => {
376 self.scan_stack.pop_back().unwrap();
378 entry.size = 1;
379 depth += 1;
380 }
381 _ => {
382 self.scan_stack.pop_back().unwrap();
383 entry.size += self.right_total;
384 if depth == 0 {
385 break;
386 }
387 }
388 }
389 }
390 }
391
392 fn get_top(&self) -> PrintFrame {
393 self.print_stack.last().copied().unwrap_or(PrintFrame::Broken(0, Breaks::Inconsistent))
394 }
395
396 fn print_begin(&mut self, token: BeginToken, size: isize) {
397 if DEBUG {
398 self.out.push(match token.breaks {
399 Breaks::Consistent => '«',
400 Breaks::Inconsistent => '‹',
401 });
402 if DEBUG_INDENT && let IndentStyle::Block { offset } = token.indent {
403 self.out.extend(offset.to_string().chars().map(|ch| match ch {
404 '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉']
405 [(ch as u8 - b'0') as usize],
406 '-' => '₋',
407 _ => unreachable!(),
408 }));
409 }
410 }
411
412 if size > self.space {
413 self.print_stack.push(PrintFrame::Broken(self.indent, token.breaks));
414 self.indent = match token.indent {
415 IndentStyle::Block { offset } => {
416 usize::try_from(self.indent as isize + offset).unwrap()
417 }
418 IndentStyle::Visual => (self.margin - self.space) as usize,
419 };
420 } else {
421 self.print_stack.push(PrintFrame::Fits(token.breaks));
422 }
423 }
424
425 fn print_end(&mut self) {
426 let breaks = match self.print_stack.pop().unwrap() {
427 PrintFrame::Broken(indent, breaks) => {
428 self.indent = indent;
429 breaks
430 }
431 PrintFrame::Fits(breaks) => breaks,
432 };
433 if DEBUG {
434 self.out.push(match breaks {
435 Breaks::Consistent => '»',
436 Breaks::Inconsistent => '›',
437 });
438 }
439 }
440
441 fn print_break(&mut self, token: BreakToken, size: isize) {
442 let fits = token.never_break
443 || match self.get_top() {
444 PrintFrame::Fits(..) => true,
445 PrintFrame::Broken(.., Breaks::Consistent) => false,
446 PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space,
447 };
448 if fits {
449 self.pending_indentation += token.blank_space;
450 self.space -= token.blank_space as isize;
451 if DEBUG {
452 self.out.push('·');
453 }
454 } else {
455 if let Some(pre_break) = token.pre_break {
456 self.print_indent();
457 self.out.push_str(pre_break);
458 }
459 if DEBUG {
460 self.out.push('·');
461 }
462 self.out.push('\n');
463 let indent = self.indent as isize + token.offset;
464 self.pending_indentation = usize::try_from(indent).expect("negative indentation");
465 self.space = cmp::max(self.margin - indent, MIN_SPACE);
466 if let Some(post_break) = token.post_break {
467 self.print_indent();
468 self.out.push_str(post_break);
469 self.space -= post_break.len() as isize;
470 }
471 }
472 }
473
474 fn print_string(&mut self, string: &str) {
475 self.print_indent();
476 self.out.push_str(string);
477 self.space -= string.len() as isize;
478 }
479
480 fn print_indent(&mut self) {
481 self.out.reserve(self.pending_indentation);
482 if let Some(tab_width) = self.indent_config {
483 let num_tabs = self.pending_indentation / tab_width;
484 self.out.extend(iter::repeat_n('\t', num_tabs));
485
486 let remainder = self.pending_indentation % tab_width;
487 self.out.extend(iter::repeat_n(' ', remainder));
488 } else {
489 self.out.extend(iter::repeat_n(' ', self.pending_indentation));
490 }
491 self.pending_indentation = 0;
492 }
493}