ziyy_core/parser/word_parser/
mod.rs

1use std::borrow::Cow;
2
3use super::ansi::{DuoEffect, Effect};
4use super::chunk::{Chunk, ChunkData};
5use super::color::{Ansi256, Color, Rgb};
6use super::tag_parser::tag::{Tag, TagType};
7use crate::common::Span;
8use crate::error::Error;
9use crate::scanner::GenericScanner;
10use crate::splitter::fragment::Fragment;
11use scanner::Scanner;
12use token::Token;
13mod scanner;
14mod token;
15
16pub static WORD_PARSER: WordParser = WordParser;
17
18macro_rules! shrink {
19    ($num:expr) => {{
20        if $num > 255.0 {
21            255u8
22        } else if $num < 0.0 {
23            0u8
24        } else {
25            $num as u8
26        }
27    }};
28}
29
30pub struct WordParser;
31
32impl Default for WordParser {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl WordParser {
39    pub fn new() -> Self {
40        Self
41    }
42
43    pub fn parse<'a>(&self, source: Fragment<'a>) -> Vec<Result<Chunk<'a>, Error>> {
44        let mut scanner = Scanner::new(source);
45        let tokens = scanner.scan_tokens();
46
47        let mut chunks = vec![];
48
49        let mut i = 0;
50        let len = tokens.len();
51
52        loop {
53            if i >= len {
54                break;
55            }
56
57            let c = tokens[i].literal;
58
59            if c == '\x1b' && tokens[i + 1].literal == '[' {
60                let g = i;
61                i += 2;
62                // Handle escape
63                let h = i;
64
65                if !matches!(tokens[i].literal, '\x30'..='\x39' | '\x3b' | '\x40'..='\x7e') {
66                    while i < len && tokens[i].literal != '\x1b' {
67                        i += 1;
68                    }
69                    let word = tokens[g..i].to_string();
70                    chunks.push(Ok(Chunk {
71                        data: ChunkData::Word(Cow::Owned(word)),
72                        span: tokens[g..i].to_span(),
73                    }));
74
75                    break;
76                }
77
78                while i < len && !matches!(tokens[i].literal, '\x40'..='\x7e') {
79                    i += 1;
80                }
81
82                if tokens[i].literal == 'm' {
83                    // Handle escape sequence
84                    let escape_sequence = tokens[h..i].to_string();
85
86                    if let Ok(tag) = self.ansi_to_tag(escape_sequence) {
87                        chunks.push(Ok(Chunk {
88                            data: ChunkData::Tag(tag),
89                            span: tokens[g..i].to_span(),
90                        }));
91                    }
92                } else {
93                    while i < len && tokens[i].literal != '\x1b' {
94                        i += 1;
95                    }
96                    let word = tokens[h..i].to_string();
97                    chunks.push(Ok(Chunk {
98                        data: ChunkData::Word(Cow::Owned(word)),
99                        span: tokens[h..i].to_span(),
100                    }));
101                }
102                i += 1;
103
104                continue;
105            } else {
106                let h = i;
107                while i < len && tokens[i].literal != '\x1b' {
108                    i += 1;
109                }
110                let word = tokens[h..i].to_string();
111                chunks.push(Ok(Chunk {
112                    data: ChunkData::Word(Cow::Owned(word)),
113                    span: tokens[h..i].to_span(),
114                }))
115            }
116            // Handle normal character
117            // i += 1
118        }
119
120        chunks
121    }
122
123    fn ansi_to_tag(&self, source: String) -> Result<Tag, i8> {
124        let mut parts = source
125            .split(';')
126            .map(|x| x.replace("4:", "4.1"))
127            .map(|x| {
128                if x.is_empty() {
129                    0.0
130                } else {
131                    x.parse::<f64>().unwrap_or(-1.0)
132                }
133            })
134            .peekable();
135
136        let mut tag = Tag::default();
137        loop {
138            let num = parts.next();
139
140            let num = match num {
141                Some(n) => n,
142                None => break,
143            };
144
145            match num {
146                0.0 => tag.clear_all(),
147
148                1.0 => {
149                    tag.set_brightness(DuoEffect::A);
150                }
151                2.0 => {
152                    tag.set_brightness(DuoEffect::B);
153                }
154                22.0 => {
155                    let num = parts.peek();
156                    if let Some(num) = num {
157                        tag.set_brightness(match num {
158                            1.0 => {
159                                parts.next();
160                                DuoEffect::BA
161                            }
162                            2.0 => {
163                                parts.next();
164                                DuoEffect::AB
165                            }
166                            _ => DuoEffect::E,
167                        });
168                    } else {
169                        tag.set_brightness(DuoEffect::E);
170                    }
171                }
172
173                3.0 => {
174                    tag.set_italics(Effect::Apply);
175                }
176                23.0 => {
177                    tag.set_italics(Effect::Clear);
178                }
179
180                4.0 => {
181                    tag.set_under(DuoEffect::A);
182                }
183                21.0 => {
184                    tag.set_under(DuoEffect::B);
185                }
186                24.0 => {
187                    let num = parts.peek();
188                    if let Some(num) = num {
189                        tag.set_under(match num {
190                            4.0 => {
191                                parts.next();
192                                DuoEffect::BA
193                            }
194                            21.0 => {
195                                parts.next();
196                                DuoEffect::AB
197                            }
198                            _ => DuoEffect::E,
199                        });
200                    } else {
201                        tag.set_under(DuoEffect::E);
202                    }
203                }
204
205                5.0 => {
206                    tag.set_blink(Effect::Apply);
207                }
208                25.0 => {
209                    tag.set_blink(Effect::Clear);
210                }
211
212                7.0 => {
213                    tag.set_negative(Effect::Apply);
214                }
215                27.0 => {
216                    tag.set_negative(Effect::Clear);
217                }
218
219                8.0 => {
220                    tag.set_hidden(Effect::Apply);
221                }
222                28.0 => {
223                    tag.set_hidden(Effect::Clear);
224                }
225
226                9.0 => {
227                    tag.set_strike(Effect::Apply);
228                }
229                29.0 => {
230                    tag.set_strike(Effect::Clear);
231                }
232
233                30.0..=37.0 | 39.0 | 90.0..=97.0 => tag.set_fg_color(Color::four_bit(shrink!(num))),
234                40.0..=47.0 | 49.0 | 100.0..=107.0 => {
235                    tag.set_bg_color(Color::four_bit(shrink!(num)))
236                }
237
238                38.0 => {
239                    let num = parts.next().ok_or(0)?;
240                    if num == 2.0 {
241                        let r = parts.next().ok_or(0)?;
242                        let g = parts.next().ok_or(0)?;
243                        let b = parts.next().ok_or(0)?;
244                        tag.set_fg_color(Color::Rgb(Rgb(38, shrink!(r), shrink!(g), shrink!(b))));
245                    }
246
247                    if num == 5.0 {
248                        let fixed = parts.next().ok_or(0)?;
249                        tag.set_fg_color(Color::Ansi256(Ansi256(38, shrink!(fixed))));
250                    }
251                }
252
253                48.0 => {
254                    let num = parts.next().ok_or(0)?;
255                    if num == 2.0 {
256                        let r = parts.next().ok_or(0)?;
257                        let g = parts.next().ok_or(0)?;
258                        let b = parts.next().ok_or(0)?;
259                        tag.set_fg_color(Color::Rgb(Rgb(48, shrink!(r), shrink!(g), shrink!(b))));
260                    }
261                    if num == 5.0 {
262                        let fixed = parts.next().ok_or(0)?;
263                        tag.set_fg_color(Color::Ansi256(Ansi256(48, shrink!(fixed))));
264                    }
265                }
266                _ => {}
267            }
268        }
269
270        tag.set_name("$ansi".to_string());
271        tag.r#type = TagType::Open;
272
273        Ok(tag)
274    }
275}
276
277trait Transform {
278    fn to_string(&self) -> String;
279    fn to_span(&self) -> Span;
280}
281
282impl Transform for [Token] {
283    fn to_string(&self) -> String {
284        let mut text = String::with_capacity(self.len());
285
286        for token in self {
287            text.push(token.literal)
288        }
289
290        text
291    }
292
293    fn to_span(&self) -> Span {
294        let mut span = Span::inserted();
295        for token in self {
296            if span == Span::inserted() {
297                span = token.span;
298            } else {
299                span += token.span;
300            }
301        }
302
303        span
304    }
305}