ziyy_core/parser/word_parser/
mod.rs1use 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 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 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 }
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}