Skip to main content

luaur_ast/records/
string_writer.rs

1use crate::records::position::Position;
2use alloc::string::String;
3
4// C++ `StringWriter : Writer`. `Writer` is an abstract base (a Rust trait), so
5// `StringWriter` *implements* it rather than embedding it — the `impl Writer for
6// StringWriter` lives with the PrettyPrinter methods; the inherent methods below
7// are the overrides.
8#[derive(Debug, Clone)]
9pub struct StringWriter {
10    pub(crate) ss: String,
11    pub(crate) pos: Position,
12    pub(crate) last_char: char,
13}
14
15impl StringWriter {
16    pub(crate) fn str(&self) -> &String {
17        &self.ss
18    }
19
20    pub(crate) fn advance(&mut self, newPos: &Position) {
21        while self.pos.line < newPos.line {
22            self.newline();
23        }
24
25        if self.pos.column < newPos.column {
26            let count = (newPos.column - self.pos.column) as usize;
27            self.write(&" ".repeat(count));
28        }
29    }
30
31    pub(crate) fn maybe_space(&mut self, newPos: &Position, reserve: i32) {
32        if self.pos.column + (reserve as u32) < newPos.column {
33            self.space();
34        }
35    }
36
37    pub(crate) fn newline(&mut self) {
38        self.ss.push('\n');
39        self.pos.column = 0;
40        self.pos.line += 1;
41        self.last_char = '\n';
42    }
43
44    pub(crate) fn space(&mut self) {
45        self.ss.push(' ');
46        self.pos.column += 1;
47        self.last_char = ' ';
48    }
49
50    pub(crate) fn write_multiline(&mut self, s: &str) {
51        if s.is_empty() {
52            return;
53        }
54
55        self.ss.push_str(s);
56        self.last_char = s.chars().last().unwrap_or('\0');
57
58        let mut index = 0;
59        let mut numLines = 0;
60        let bytes = s.as_bytes();
61        for (i, &b) in bytes.iter().enumerate() {
62            if b == b'\n' {
63                numLines += 1;
64                index = i + 1;
65            }
66        }
67
68        self.pos.line += numLines as u32;
69        if numLines > 0 {
70            self.pos.column = (s.len() - index) as u32;
71        } else {
72            self.pos.column += s.len() as u32;
73        }
74    }
75
76    pub(crate) fn write(&mut self, s: &str) {
77        if s.is_empty() {
78            return;
79        }
80
81        self.ss.push_str(s);
82        self.pos.column += s.len() as u32;
83        self.last_char = s.chars().last().unwrap_or('\0');
84    }
85
86    pub(crate) fn write_char(&mut self, c: char) {
87        self.ss.push(c);
88        self.pos.column += 1;
89        self.last_char = c;
90    }
91
92    pub(crate) fn identifier(&mut self, s: &str) {
93        if s.is_empty() {
94            return;
95        }
96
97        if crate::records::string_writer::is_identifier_char(self.last_char) {
98            self.space();
99        }
100
101        self.write(s);
102    }
103
104    pub(crate) fn keyword(&mut self, s: &str) {
105        if s.is_empty() {
106            return;
107        }
108
109        if crate::records::string_writer::is_identifier_char(self.last_char) {
110            self.space();
111        }
112
113        self.write(s);
114    }
115
116    pub(crate) fn symbol(&mut self, s: &str) {
117        self.write(s);
118    }
119
120    pub(crate) fn literal(&mut self, s: &str) {
121        if s.is_empty() {
122            return;
123        } else if crate::records::string_writer::is_identifier_char(self.last_char)
124            && s.chars().next().map_or(false, |c| c.is_ascii_digit())
125        {
126            self.space();
127        }
128
129        self.write(s);
130    }
131
132    pub(crate) fn string(&mut self, s: &str) {
133        let mut quote = '\'';
134        if s.contains('\'') {
135            quote = '\"';
136        }
137
138        self.write_char(quote);
139        self.write(&luaur_common::functions::escape::escape(s, false));
140        self.write_char(quote);
141    }
142
143    pub(crate) fn source_string(
144        &mut self,
145        s: &str,
146        quote_style: crate::enums::quote_style_cst::QuoteStyle,
147        block_depth: u32,
148    ) {
149        use crate::enums::quote_style_cst::QuoteStyle;
150        if quote_style == QuoteStyle::QuotedRaw {
151            let blocks = "=".repeat(block_depth as usize);
152            self.write_char('[');
153            self.write(&blocks);
154            self.write_char('[');
155            self.write_multiline(s);
156            self.write_char(']');
157            self.write(&blocks);
158            self.write_char(']');
159        } else {
160            debug_assert!(block_depth == 0);
161
162            let quote = match quote_style {
163                QuoteStyle::QuotedDouble => '"',
164                QuoteStyle::QuotedSingle => '\'',
165                QuoteStyle::QuotedInterp => '`',
166                _ => {
167                    debug_assert!(false, "Unhandled quote type");
168                    '"'
169                }
170            };
171
172            self.write_char(quote);
173            self.write_multiline(s);
174            self.write_char(quote);
175        }
176    }
177}
178
179#[inline]
180fn is_identifier_char(c: char) -> bool {
181    c.is_ascii_alphanumeric() || c == '_'
182}