forge_fmt/pp/
convenience.rs

1use super::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token};
2use std::borrow::Cow;
3
4impl Printer {
5    /// "raw box"
6    pub fn rbox(&mut self, indent: isize, breaks: Breaks) {
7        self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks });
8    }
9
10    /// Inconsistent breaking box
11    pub fn ibox(&mut self, indent: isize) {
12        self.rbox(indent, Breaks::Inconsistent);
13    }
14
15    /// Consistent breaking box
16    pub fn cbox(&mut self, indent: isize) {
17        self.rbox(indent, Breaks::Consistent);
18    }
19
20    pub fn visual_align(&mut self) {
21        self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent });
22    }
23
24    pub fn break_offset(&mut self, n: usize, off: isize) {
25        self.scan_break(BreakToken { offset: off, blank_space: n, ..BreakToken::default() });
26    }
27
28    pub fn end(&mut self) {
29        self.scan_end();
30    }
31
32    pub fn eof(mut self) -> String {
33        self.scan_eof();
34        self.out
35    }
36
37    pub fn word(&mut self, w: impl Into<Cow<'static, str>>) {
38        self.scan_string(w.into());
39    }
40
41    fn spaces(&mut self, n: usize) {
42        self.break_offset(n, 0);
43    }
44
45    pub fn zerobreak(&mut self) {
46        self.spaces(0);
47    }
48
49    pub fn space(&mut self) {
50        self.spaces(1);
51    }
52
53    pub fn hardbreak(&mut self) {
54        self.spaces(SIZE_INFINITY as usize);
55    }
56
57    pub fn last_token_is_neverbreak(&self) -> bool {
58        if let Some(token) = self.last_token() {
59            return token.is_neverbreak();
60        }
61
62        false
63    }
64
65    pub fn last_token_is_break(&self) -> bool {
66        if let Some(token) = self.last_token() {
67            return matches!(token, Token::Break(_));
68        }
69        false
70    }
71
72    pub fn last_token_is_space(&self) -> bool {
73        if let Some(token) = self.last_token()
74            && token.is_space()
75        {
76            return true;
77        }
78
79        self.out.ends_with(" ")
80    }
81
82    pub fn is_beginning_of_line(&self) -> bool {
83        match self.last_token() {
84            Some(last_token) => last_token.is_hardbreak(),
85            None => self.out.is_empty() || self.out.ends_with('\n'),
86        }
87    }
88
89    /// Attempts to identify whether the current position is:
90    ///   1. the beginning of a line (empty)
91    ///   2. a line with only indentation (just whitespaces)
92    ///
93    /// NOTE: this is still an educated guess, based on a heuristic.
94    pub fn is_bol_or_only_ind(&self) -> bool {
95        for i in self.buf.index_range().rev() {
96            let token = &self.buf[i].token;
97            if token.is_hardbreak() {
98                return true;
99            }
100            if Self::token_has_non_whitespace_content(token) {
101                return false;
102            }
103        }
104
105        let last_line =
106            if let Some(pos) = self.out.rfind('\n') { &self.out[pos + 1..] } else { &self.out[..] };
107
108        last_line.trim().is_empty()
109    }
110
111    fn token_has_non_whitespace_content(token: &Token) -> bool {
112        match token {
113            Token::String(s) => !s.trim().is_empty(),
114            Token::Break(BreakToken { pre_break: Some(s), .. }) => !s.trim().is_empty(),
115            _ => false,
116        }
117    }
118
119    pub(crate) fn hardbreak_tok_offset(offset: isize) -> Token {
120        Token::Break(BreakToken {
121            offset,
122            blank_space: SIZE_INFINITY as usize,
123            ..BreakToken::default()
124        })
125    }
126
127    pub fn hardbreak_if_nonempty(&mut self) {
128        self.scan_break(BreakToken {
129            blank_space: SIZE_INFINITY as usize,
130            if_nonempty: true,
131            ..BreakToken::default()
132        });
133    }
134
135    pub fn neverbreak(&mut self) {
136        self.scan_break(BreakToken { never_break: true, ..BreakToken::default() });
137    }
138}
139
140impl Token {
141    pub(crate) fn is_neverbreak(&self) -> bool {
142        if let Self::Break(BreakToken { never_break, .. }) = *self {
143            return never_break;
144        }
145        false
146    }
147
148    pub(crate) fn is_hardbreak(&self) -> bool {
149        if let Self::Break(BreakToken { blank_space, never_break, .. }) = *self {
150            return blank_space == SIZE_INFINITY as usize && !never_break;
151        }
152        false
153    }
154
155    pub(crate) fn is_space(&self) -> bool {
156        match self {
157            Self::Break(BreakToken { offset, blank_space, .. }) => {
158                *offset == 0 && *blank_space == 1
159            }
160            Self::String(s) => s.ends_with(' '),
161            _ => false,
162        }
163    }
164}