debug3/
algorithm.rs

1// From: https://github.com/dtolnay/prettyplease/blob/1b841376d1caf0b233a7ee5d2f5ec0278c0f553b/src/algorithm.rs
2
3// Adapted from https://github.com/rust-lang/rust/blob/1.57.0/compiler/rustc_ast_pretty/src/pp.rs.
4// See "Algorithm notes" in the crate-level rustdoc for prettyplease.
5
6use crate::ring::RingBuffer;
7use crate::{MARGIN, MIN_SPACE};
8use std::borrow::Cow;
9use std::cmp;
10use std::collections::VecDeque;
11use std::convert::TryFrom;
12use std::iter;
13
14#[derive(Clone, Copy, PartialEq)]
15pub(crate) enum Breaks {
16    Consistent,
17    Inconsistent,
18}
19
20#[derive(Clone, Copy, Default)]
21pub(crate) struct BreakToken {
22    pub(crate) offset: isize,
23    pub(crate) blank_space: usize,
24    pub(crate) pre_break: Option<char>,
25    pub(crate) post_break: Option<char>,
26    pub(crate) no_break: Option<char>,
27    pub(crate) if_nonempty: bool,
28    pub(crate) never_break: bool,
29}
30
31#[derive(Clone, Copy)]
32pub(crate) struct BeginToken {
33    pub(crate) offset: isize,
34    pub(crate) breaks: Breaks,
35}
36
37#[derive(Clone)]
38pub(crate) enum Token {
39    String(Cow<'static, str>),
40    Break(BreakToken),
41    Begin(BeginToken),
42    End,
43}
44
45#[derive(Copy, Clone)]
46enum PrintFrame {
47    Fits(Breaks),
48    Broken(usize, Breaks),
49}
50
51pub(crate) const SIZE_INFINITY: isize = 0xffff;
52
53/// A target for formatting.
54///
55/// Users do not construct `Formatter`s directly; a mutable reference to one is
56/// passed to the `fmt` method of [`crate::Debug`]
57///
58/// To interact with a `Formatter`, you'll call various methods to change the
59/// various options related to formatting. For examples, please see the
60/// documentation of the methods defined on `Formatter` below.
61pub struct Formatter {
62    out: String,
63    // Number of spaces left on line
64    space: isize,
65    // Ring-buffer of tokens and calculated sizes
66    buf: RingBuffer<BufEntry>,
67    // Total size of tokens already printed
68    left_total: isize,
69    // Total size of tokens enqueued, including printed and not yet printed
70    right_total: isize,
71    // Holds the ring-buffer index of the Begin that started the current block,
72    // possibly with the most recent Break after that Begin (if there is any) on
73    // top of it. Values are pushed and popped on the back of the queue using it
74    // like stack, and elsewhere old values are popped from the front of the
75    // queue as they become irrelevant due to the primary ring-buffer advancing.
76    scan_stack: VecDeque<usize>,
77    // Stack of blocks-in-progress being flushed by print
78    print_stack: Vec<PrintFrame>,
79    // Level of indentation of current line
80    indent: usize,
81    // Buffered indentation to avoid writing trailing whitespace
82    pending_indentation: usize,
83}
84
85#[derive(Clone)]
86struct BufEntry {
87    token: Token,
88    size: isize,
89}
90
91impl Formatter {
92    pub(crate) fn new() -> Self {
93        Formatter {
94            out: String::new(),
95            space: MARGIN,
96            buf: RingBuffer::new(),
97            left_total: 0,
98            right_total: 0,
99            scan_stack: VecDeque::new(),
100            print_stack: Vec::new(),
101            indent: 0,
102            pending_indentation: 0,
103        }
104    }
105
106    pub(crate) fn eof(mut self) -> String {
107        if !self.scan_stack.is_empty() {
108            self.check_stack(0);
109            self.advance_left();
110        }
111        self.out
112    }
113
114    pub(crate) fn scan_begin(&mut self, token: BeginToken) {
115        if self.scan_stack.is_empty() {
116            self.left_total = 1;
117            self.right_total = 1;
118            self.buf.clear();
119        }
120        let right = self.buf.push(BufEntry {
121            token: Token::Begin(token),
122            size: -self.right_total,
123        });
124        self.scan_stack.push_back(right);
125    }
126
127    pub(crate) fn scan_end(&mut self) {
128        if self.scan_stack.is_empty() {
129            self.print_end();
130        } else {
131            if !self.buf.is_empty() {
132                if let Token::Break(break_token) = self.buf.last().token {
133                    if self.buf.len() >= 2 {
134                        if let Token::Begin(_) = self.buf.second_last().token {
135                            self.buf.pop_last();
136                            self.buf.pop_last();
137                            self.scan_stack.pop_back();
138                            self.scan_stack.pop_back();
139                            self.right_total -= break_token.blank_space as isize;
140                            return;
141                        }
142                    }
143                    if break_token.if_nonempty {
144                        self.buf.pop_last();
145                        self.scan_stack.pop_back();
146                        self.right_total -= break_token.blank_space as isize;
147                    }
148                }
149            }
150            let right = self.buf.push(BufEntry {
151                token: Token::End,
152                size: -1,
153            });
154            self.scan_stack.push_back(right);
155        }
156    }
157
158    pub(crate) fn scan_break(&mut self, token: BreakToken) {
159        if self.scan_stack.is_empty() {
160            self.left_total = 1;
161            self.right_total = 1;
162            self.buf.clear();
163        } else {
164            self.check_stack(0);
165        }
166        let right = self.buf.push(BufEntry {
167            token: Token::Break(token),
168            size: -self.right_total,
169        });
170        self.scan_stack.push_back(right);
171        self.right_total += token.blank_space as isize;
172    }
173
174    pub(crate) fn scan_string(&mut self, string: Cow<'static, str>) {
175        if self.scan_stack.is_empty() {
176            self.print_string(string);
177        } else {
178            let len = string.len() as isize;
179            self.buf.push(BufEntry {
180                token: Token::String(string),
181                size: len,
182            });
183            self.right_total += len;
184            self.check_stream();
185        }
186    }
187
188    pub(crate) fn offset(&mut self, offset: isize) {
189        match &mut self.buf.last_mut().token {
190            Token::Break(token) => token.offset += offset,
191            Token::Begin(_) => {}
192            Token::String(_) | Token::End => unreachable!(),
193        }
194    }
195
196    pub(crate) fn end_with_max_width(&mut self, max: isize) {
197        let mut depth = 1;
198        for &index in self.scan_stack.iter().rev() {
199            let entry = &self.buf[index];
200            match entry.token {
201                Token::Begin(_) => {
202                    depth -= 1;
203                    if depth == 0 {
204                        if entry.size < 0 {
205                            let actual_width = entry.size + self.right_total;
206                            if actual_width > max {
207                                self.buf.push(BufEntry {
208                                    token: Token::String(Cow::Borrowed("")),
209                                    size: SIZE_INFINITY,
210                                });
211                                self.right_total += SIZE_INFINITY;
212                            }
213                        }
214                        break;
215                    }
216                }
217                Token::End => depth += 1,
218                Token::Break(_) => {}
219                Token::String(_) => unreachable!(),
220            }
221        }
222        self.scan_end();
223    }
224
225    fn check_stream(&mut self) {
226        while self.right_total - self.left_total > self.space {
227            if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
228                self.scan_stack.pop_front().unwrap();
229                self.buf.first_mut().size = SIZE_INFINITY;
230            }
231
232            self.advance_left();
233
234            if self.buf.is_empty() {
235                break;
236            }
237        }
238    }
239
240    fn advance_left(&mut self) {
241        while self.buf.first().size >= 0 {
242            let left = self.buf.pop_first();
243
244            match left.token {
245                Token::String(string) => {
246                    self.left_total += left.size;
247                    self.print_string(string);
248                }
249                Token::Break(token) => {
250                    self.left_total += token.blank_space as isize;
251                    self.print_break(token, left.size);
252                }
253                Token::Begin(token) => self.print_begin(token, left.size),
254                Token::End => self.print_end(),
255            }
256
257            if self.buf.is_empty() {
258                break;
259            }
260        }
261    }
262
263    fn check_stack(&mut self, mut depth: usize) {
264        while let Some(&index) = self.scan_stack.back() {
265            let entry = &mut self.buf[index];
266            match entry.token {
267                Token::Begin(_) => {
268                    if depth == 0 {
269                        break;
270                    }
271                    self.scan_stack.pop_back().unwrap();
272                    entry.size += self.right_total;
273                    depth -= 1;
274                }
275                Token::End => {
276                    self.scan_stack.pop_back().unwrap();
277                    entry.size = 1;
278                    depth += 1;
279                }
280                Token::Break(_) => {
281                    self.scan_stack.pop_back().unwrap();
282                    entry.size += self.right_total;
283                    if depth == 0 {
284                        break;
285                    }
286                }
287                Token::String(_) => unreachable!(),
288            }
289        }
290    }
291
292    fn get_top(&self) -> PrintFrame {
293        const OUTER: PrintFrame = PrintFrame::Broken(0, Breaks::Inconsistent);
294        self.print_stack.last().map_or(OUTER, PrintFrame::clone)
295    }
296
297    fn print_begin(&mut self, token: BeginToken, size: isize) {
298        if cfg!(prettyplease_debug) {
299            self.out.push(match token.breaks {
300                Breaks::Consistent => '«',
301                Breaks::Inconsistent => '‹',
302            });
303            if cfg!(prettyplease_debug_indent) {
304                self.out
305                    .extend(token.offset.to_string().chars().map(|ch| match ch {
306                        '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉']
307                            [(ch as u8 - b'0') as usize],
308                        '-' => '₋',
309                        _ => unreachable!(),
310                    }));
311            }
312        }
313        if size > self.space {
314            self.print_stack
315                .push(PrintFrame::Broken(self.indent, token.breaks));
316            self.indent = usize::try_from(self.indent as isize + token.offset).unwrap();
317        } else {
318            self.print_stack.push(PrintFrame::Fits(token.breaks));
319        }
320    }
321
322    fn print_end(&mut self) {
323        let breaks = match self.print_stack.pop().unwrap() {
324            PrintFrame::Broken(indent, breaks) => {
325                self.indent = indent;
326                breaks
327            }
328            PrintFrame::Fits(breaks) => breaks,
329        };
330        if cfg!(prettyplease_debug) {
331            self.out.push(match breaks {
332                Breaks::Consistent => '»',
333                Breaks::Inconsistent => '›',
334            });
335        }
336    }
337
338    fn print_break(&mut self, token: BreakToken, size: isize) {
339        let fits = token.never_break
340            || match self.get_top() {
341                PrintFrame::Fits(..) => true,
342                PrintFrame::Broken(.., Breaks::Consistent) => false,
343                PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space,
344            };
345        if fits {
346            self.pending_indentation += token.blank_space;
347            self.space -= token.blank_space as isize;
348            if let Some(no_break) = token.no_break {
349                self.out.push(no_break);
350                self.space -= no_break.len_utf8() as isize;
351            }
352            if cfg!(prettyplease_debug) {
353                self.out.push('·');
354            }
355        } else {
356            if let Some(pre_break) = token.pre_break {
357                self.out.push(pre_break);
358            }
359            if cfg!(prettyplease_debug) {
360                self.out.push('·');
361            }
362            self.out.push('\n');
363            let indent = self.indent as isize + token.offset;
364            self.pending_indentation = usize::try_from(indent).unwrap();
365            self.space = cmp::max(MARGIN - indent, MIN_SPACE);
366            if let Some(post_break) = token.post_break {
367                self.print_indent();
368                self.out.push(post_break);
369                self.space -= post_break.len_utf8() as isize;
370            }
371        }
372    }
373
374    fn print_string(&mut self, string: Cow<'static, str>) {
375        self.print_indent();
376        self.out.push_str(&string);
377        self.space -= string.len() as isize;
378    }
379
380    fn print_indent(&mut self) {
381        self.out.reserve(self.pending_indentation);
382        self.out
383            .extend(iter::repeat(' ').take(self.pending_indentation));
384        self.pending_indentation = 0;
385    }
386}
387
388impl Default for Formatter {
389    fn default() -> Self {
390        Self::new()
391    }
392}