duat_term/area/
mod.rs

1mod iter;
2mod print_info;
3
4use std::{io::Write, sync::Arc};
5
6use crossterm::{
7    cursor, queue,
8    style::{Attribute, Attributes},
9};
10use duat_core::{
11    context::{self, Decode, Encode},
12    form::{CONTROL_CHAR_ID, Painter},
13    opts::PrintOpts,
14    session::TwoPointsPlace,
15    text::{Item, Part, SpawnId, Text, TwoPoints, txt},
16    ui::{
17        self, Caret, DynSpawnSpecs, PushSpecs,
18        traits::{CoreAccess, RawArea},
19    },
20};
21use iter::{print_iter, rev_print_iter};
22
23pub use self::print_info::PrintInfo;
24use crate::{AreaId, CStyle, Mutex, layout::Layouts, print_style, printer::Lines};
25
26#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
27#[bincode(crate = "duat_core::context::bincode")]
28pub struct Coord {
29    pub x: u32,
30    pub y: u32,
31}
32
33impl std::fmt::Debug for Coord {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
36    }
37}
38
39impl Coord {
40    pub fn new(x: u32, y: u32) -> Coord {
41        Coord { x, y }
42    }
43}
44
45#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
46#[bincode(crate = "duat_core::context::bincode")]
47pub struct Coords {
48    pub tl: Coord,
49    pub br: Coord,
50}
51
52impl Coords {
53    pub fn new(tl: Coord, br: Coord) -> Self {
54        Coords { tl, br }
55    }
56
57    pub fn width(&self) -> u32 {
58        self.br.x - self.tl.x
59    }
60
61    pub fn height(&self) -> u32 {
62        self.br.y - self.tl.y
63    }
64
65    pub fn area(&self) -> u32 {
66        self.width() * self.height()
67    }
68
69    pub fn intersects(&self, other: Self) -> bool {
70        self.tl.x < other.br.x
71            && self.br.x > other.tl.x
72            && self.tl.y < other.br.y
73            && self.br.y > other.tl.y
74    }
75}
76
77#[derive(Clone)]
78pub struct Area {
79    layouts: Layouts,
80    id: AreaId,
81    ansi_codes: Arc<Mutex<micromap::Map<CStyle, String, 16>>>,
82}
83
84impl PartialEq for Area {
85    fn eq(&self, other: &Self) -> bool {
86        self.id == other.id
87    }
88}
89
90impl Area {
91    pub(crate) fn new(id: AreaId, layouts: Layouts) -> Self {
92        Self { layouts, id, ansi_codes: Arc::default() }
93    }
94
95    fn print<'a>(
96        &self,
97        text: &Text,
98        opts: PrintOpts,
99        mut painter: Painter,
100        mut f: impl FnMut(&Caret, &Item) + 'a,
101    ) {
102        const SPACES: &[u8] = &[b' '; 3000];
103
104        fn end_line(
105            lines: &mut Lines,
106            painter: &Painter,
107            ansi_codes: &mut micromap::Map<CStyle, String, 16>,
108            len: u32,
109            max_x: u32,
110        ) {
111            let mut default_style = painter.get_default();
112            default_style.style.foreground_color = None;
113            default_style.style.underline_color = None;
114            default_style.style.attributes = Attributes::from(Attribute::Reset);
115
116            print_style(lines, default_style.style, ansi_codes);
117            if lines.coords().br.x == max_x {
118                lines.write_all(b"\x1b[0K").unwrap();
119            } else {
120                lines
121                    .write_all(&SPACES[..(lines.coords().width() - len) as usize])
122                    .unwrap();
123            }
124            lines.flush().unwrap();
125        }
126
127        let (mut lines, iter, x_shift, max_x) = {
128            let Some(coords) = self.layouts.coords_of(self.id, true) else {
129                context::warn!("This Area was already deleted");
130                return;
131            };
132
133            let max = self
134                .layouts
135                .inspect(self.id, |_, layout| layout.max_value())
136                .unwrap();
137
138            if coords.width() == 0 || coords.height() == 0 {
139                return;
140            }
141
142            let (s_points, x_shift) = {
143                let mut info = self.layouts.get_info_of(self.id).unwrap();
144                let s_points = info.start_points(coords, text, opts);
145                self.layouts.set_info_of(self.id, info);
146                (s_points, info.x_shift())
147            };
148
149            let lines = Lines::new(coords);
150            let width = opts.wrap_width(coords.width()).unwrap_or(coords.width());
151            let iter = print_iter(text, s_points, width, opts);
152
153            (lines, iter, x_shift, max.x)
154        };
155
156        let mut observed_spawns = Vec::new();
157        let spawn_id = self
158            .layouts
159            .inspect(self.id, |rect, _| rect.spawn_id())
160            .unwrap();
161        let is_active = self.id == self.layouts.get_active_id();
162
163        let mut ansi_codes = self.ansi_codes.lock().unwrap();
164        let mut style_was_set = false;
165
166        enum Cursor {
167            Main,
168            Extra,
169        }
170
171        // The y here represents the bottom of the current row of cells.
172        let tl_y = lines.coords().tl.y;
173        let mut y = tl_y;
174        let mut cursor = None;
175        let mut spawns_for_next: Vec<SpawnId> = Vec::new();
176        let mut last_len = 0;
177
178        for (caret, item) in iter {
179            let (painter, lines, ansi_codes) = (&mut painter, &mut lines, &mut ansi_codes);
180            f(&caret, &item);
181
182            let Caret { x, len, wrap } = caret;
183            let Item { part, .. } = item;
184
185            if wrap {
186                if y == lines.coords().br.y {
187                    break;
188                }
189                if y > lines.coords().tl.y {
190                    end_line(lines, painter, ansi_codes, last_len, max_x);
191                }
192                let initial_space = x.saturating_sub(x_shift).min(lines.coords().width());
193                if initial_space > 0 {
194                    let mut default_style = painter.get_default().style;
195                    default_style.attributes.set(Attribute::Reset);
196                    print_style(lines, default_style, ansi_codes);
197                    lines.write_all(&SPACES[..initial_space as usize]).unwrap();
198                }
199                y += 1;
200
201                // Resetting space to prevent erroneous printing.
202                painter.reset_prev_style();
203                style_was_set = true;
204                last_len = initial_space;
205            }
206
207            let is_contained = x + len > x_shift && x < x_shift + lines.coords().width();
208
209            match part {
210                Part::Char(char) if is_contained => {
211                    if let Some(str) = get_control_str(char) {
212                        painter.apply(CONTROL_CHAR_ID, 100);
213                        if style_was_set && let Some(style) = painter.relative_style() {
214                            print_style(lines, style, ansi_codes);
215                        }
216                        lines.write_all(str.as_bytes()).unwrap();
217                        painter.remove(CONTROL_CHAR_ID)
218                    } else {
219                        if style_was_set && let Some(style) = painter.relative_style() {
220                            print_style(lines, style, ansi_codes);
221                        }
222                        match char {
223                            '\t' => {
224                                let truncated_start = x_shift.saturating_sub(x);
225                                let truncated_end =
226                                    (x + len).saturating_sub(lines.coords().width() + x_shift);
227                                let tab_len = len - (truncated_start + truncated_end);
228                                lines.write_all(&SPACES[..tab_len as usize]).unwrap()
229                            }
230                            '\n' if opts.print_new_line => lines.write_all(b" ").unwrap(),
231                            '\n' | '\r' => {}
232                            char => {
233                                let mut bytes = [0; 4];
234                                char.encode_utf8(&mut bytes);
235                                lines.write_all(&bytes[..char.len_utf8()]).unwrap();
236                            }
237                        }
238                    }
239
240                    if let Some(cursor) = cursor.take() {
241                        match cursor {
242                            Cursor::Main => painter.remove_main_caret(),
243                            Cursor::Extra => painter.remove_extra_caret(),
244                        }
245                        if let Some(style) = painter.relative_style() {
246                            print_style(lines, style, ansi_codes)
247                        }
248                    }
249                    for id in spawns_for_next.drain(..) {
250                        observed_spawns.push(id);
251                        self.layouts.move_spawn_to(
252                            id,
253                            Coord::new(lines.coords().tl.x + x, y - 1),
254                            len,
255                        );
256                    }
257
258                    last_len = x + len - x_shift;
259                    style_was_set = false;
260                }
261                Part::Char(_) => {
262                    match cursor.take() {
263                        Some(Cursor::Main) => painter.remove_main_caret(),
264                        Some(Cursor::Extra) => painter.remove_extra_caret(),
265                        None => {}
266                    }
267                    spawns_for_next.clear();
268                }
269                Part::PushForm(id, prio) => {
270                    painter.apply(id, prio);
271                    style_was_set = true;
272                }
273                Part::PopForm(id) => {
274                    painter.remove(id);
275                    style_was_set = true;
276                }
277                Part::MainCaret => {
278                    if let Some(shape) = painter.main_cursor()
279                        && is_active
280                    {
281                        lines.show_real_cursor();
282                        queue!(lines, shape, cursor::SavePosition).unwrap();
283                    } else {
284                        cursor = Some(Cursor::Main);
285                        lines.hide_real_cursor();
286                        painter.apply_main_cursor();
287                        style_was_set = true;
288                    }
289                }
290                Part::ExtraCaret => {
291                    cursor = Some(Cursor::Extra);
292                    painter.apply_extra_cursor();
293                    style_was_set = true;
294                }
295                Part::Spacer => {
296                    let truncated_start = x_shift.saturating_sub(x).min(len);
297                    let truncated_end = (x + len)
298                        .saturating_sub(lines.coords().width().saturating_sub(x_shift))
299                        .min(len);
300                    let spacer_len = len - (truncated_start + truncated_end);
301                    lines.write_all(&SPACES[0..spacer_len as usize]).unwrap();
302                    last_len = (x + len)
303                        .saturating_sub(x_shift)
304                        .min(lines.coords().width());
305                }
306                Part::ResetState => print_style(lines, painter.reset(), ansi_codes),
307                Part::SpawnedWidget(id) => spawns_for_next.push(id),
308                Part::ToggleStart(_) | Part::ToggleEnd(_) => {
309                    todo!("Toggles have not been implemented yet.")
310                }
311                Part::AlignLeft | Part::AlignCenter | Part::AlignRight => {}
312            }
313        }
314
315        end_line(&mut lines, &painter, &mut ansi_codes, last_len, max_x);
316
317        for _ in 0..lines.coords().br.y - y {
318            end_line(&mut lines, &painter, &mut ansi_codes, 0, max_x);
319        }
320
321        let spawns = text.get_spawned_ids();
322
323        self.layouts
324            .send_lines(self.id, spawn_id, lines, spawns, &observed_spawns);
325    }
326}
327
328impl RawArea for Area {
329    type Cache = PrintInfo;
330    type PrintInfo = PrintInfo;
331
332    /////////// Modification
333
334    fn push(
335        &self,
336        _: CoreAccess,
337        specs: PushSpecs,
338        on_files: bool,
339        cache: PrintInfo,
340    ) -> Option<(Area, Option<Area>)> {
341        let (child, parent) = self.layouts.push(self.id, specs, on_files, cache)?;
342
343        Some((
344            Self::new(child, self.layouts.clone()),
345            parent.map(|parent| Self::new(parent, self.layouts.clone())),
346        ))
347    }
348
349    fn delete(&self, _: CoreAccess) -> (bool, Vec<Self>) {
350        let (do_rm_window, rm_areas) = self.layouts.delete(self.id);
351        (
352            do_rm_window,
353            rm_areas
354                .into_iter()
355                .map(|id| Self::new(id, self.layouts.clone()))
356                .collect(),
357        )
358    }
359
360    fn swap(&self, _: CoreAccess, rhs: &Self) -> bool {
361        self.layouts.swap(self.id, rhs.id)
362    }
363
364    fn spawn(
365        &self,
366        _: CoreAccess,
367        spawn_id: SpawnId,
368        specs: DynSpawnSpecs,
369        cache: Self::Cache,
370    ) -> Option<Self> {
371        Some(Self::new(
372            self.layouts
373                .spawn_on_widget(self.id, spawn_id, specs, cache)?,
374            self.layouts.clone(),
375        ))
376    }
377
378    fn set_width(&self, _: CoreAccess, width: f32) -> Result<(), Text> {
379        if self
380            .layouts
381            .set_constraints(self.id, Some(width), None, None)
382        {
383            Ok(())
384        } else {
385            Err(txt!("Couldn't set the width to [a]{width}"))
386        }
387    }
388
389    fn set_height(&self, _: CoreAccess, height: f32) -> Result<(), Text> {
390        if self
391            .layouts
392            .set_constraints(self.id, None, Some(height), None)
393        {
394            Ok(())
395        } else {
396            Err(txt!("Couldn't set the height to [a]{height}"))
397        }
398    }
399
400    fn hide(&self, _: CoreAccess) -> Result<(), Text> {
401        if self
402            .layouts
403            .set_constraints(self.id, None, None, Some(true))
404        {
405            Ok(())
406        } else {
407            Err(txt!("Couldn't hide the Area"))
408        }
409    }
410
411    fn reveal(&self, _: CoreAccess) -> Result<(), Text> {
412        if self
413            .layouts
414            .set_constraints(self.id, None, None, Some(false))
415        {
416            Ok(())
417        } else {
418            Err(txt!("Couldn't reveal the Area"))
419        }
420    }
421
422    fn width_of_text(&self, _: CoreAccess, opts: PrintOpts, text: &Text) -> Result<f32, Text> {
423        let max = self
424            .layouts
425            .inspect(self.id, |_, layout| layout.max_value())
426            .ok_or_else(|| txt!("This Area was already deleted"))?;
427
428        let iter = iter::print_iter(text, TwoPoints::default(), max.x, opts);
429
430        // It can be None if there is total concalment.
431        Ok(iter.map(|(c, _)| c.x + c.len).max().unwrap_or(0) as f32)
432    }
433
434    fn scroll_ver(&self, _: CoreAccess, text: &Text, by: i32, opts: PrintOpts) {
435        if by == 0 {
436            return;
437        }
438
439        let Some(coords) = self.layouts.coords_of(self.id, false) else {
440            context::warn!("This Area was already deleted");
441            return;
442        };
443
444        if coords.width() == 0 || coords.height() == 0 {
445            return;
446        }
447
448        let mut info = self.layouts.get_info_of(self.id).unwrap();
449        info.scroll_ver(by, coords, text, opts);
450        self.layouts.set_info_of(self.id, info);
451    }
452
453    ////////// Printing
454
455    fn scroll_around_points(&self, _: CoreAccess, text: &Text, points: TwoPoints, opts: PrintOpts) {
456        let Some(coords) = self.layouts.coords_of(self.id, false) else {
457            context::warn!("This Area was already deleted");
458            return;
459        };
460
461        if coords.width() == 0 || coords.height() == 0 {
462            return;
463        }
464
465        let mut info = self.layouts.get_info_of(self.id).unwrap();
466        info.scroll_around(points.real, coords, text, opts);
467        self.layouts.set_info_of(self.id, info);
468    }
469
470    fn scroll_to_points(&self, _: CoreAccess, text: &Text, points: TwoPoints, opts: PrintOpts) {
471        let Some(coords) = self.layouts.coords_of(self.id, false) else {
472            context::warn!("This Area was already deleted");
473            return;
474        };
475
476        if coords.width() == 0 || coords.height() == 0 {
477            return;
478        }
479
480        let mut info = self.layouts.get_info_of(self.id).unwrap();
481        info.scroll_to_points(points, coords, text, opts);
482        self.layouts.set_info_of(self.id, info);
483    }
484
485    fn set_as_active(&self, _: CoreAccess) {
486        self.layouts.set_active_id(self.id);
487    }
488
489    fn print(&self, _: CoreAccess, text: &Text, opts: PrintOpts, painter: Painter) {
490        self.print(text, opts, painter, |_, _| {})
491    }
492
493    fn print_with<'a>(
494        &self,
495        _: CoreAccess,
496        text: &Text,
497        opts: PrintOpts,
498        painter: Painter,
499        f: impl FnMut(&Caret, &Item) + 'a,
500    ) {
501        self.print(text, opts, painter, f)
502    }
503
504    ////////// Queries
505
506    fn set_print_info(&self, _: CoreAccess, info: Self::PrintInfo) {
507        self.layouts.set_info_of(self.id, info);
508    }
509
510    fn print_iter<'a>(
511        &self,
512        ca: CoreAccess,
513        text: &'a Text,
514        points: TwoPoints,
515        opts: PrintOpts,
516    ) -> impl Iterator<Item = (Caret, Item)> + 'a {
517        let width = (self.bottom_right(ca).x - self.top_left(ca).x) as u32;
518        print_iter(text, points, width, opts)
519    }
520
521    fn rev_print_iter<'a>(
522        &self,
523        ca: CoreAccess,
524        text: &'a Text,
525        points: TwoPoints,
526        opts: PrintOpts,
527    ) -> impl Iterator<Item = (Caret, Item)> + 'a {
528        let width = (self.bottom_right(ca).x - self.top_left(ca).x) as u32;
529        rev_print_iter(text, points, opts.wrap_width(width).unwrap_or(width), opts)
530    }
531
532    fn has_changed(&self, _: CoreAccess) -> bool {
533        self.layouts
534            .inspect(self.id, |rect, layout| rect.has_changed(layout))
535            .unwrap_or(false)
536    }
537
538    fn is_master_of(&self, _: CoreAccess, other: &Self) -> bool {
539        if other.id == self.id {
540            return true;
541        }
542
543        let mut parent_id = other.id;
544
545        self.layouts.inspect(self.id, |_, layout| {
546            while let Some((_, parent)) = layout.get_parent(parent_id) {
547                parent_id = parent.id();
548                if parent.id() == self.id {
549                    break;
550                }
551            }
552        });
553
554        parent_id == self.id
555    }
556
557    fn get_cluster_master(&self, _: CoreAccess) -> Option<Self> {
558        let id = self
559            .layouts
560            .inspect(self.id, |_, layout| layout.get_cluster_master(self.id))??;
561
562        Some(Self {
563            layouts: self.layouts.clone(),
564            id,
565            ansi_codes: Arc::default(),
566        })
567    }
568
569    fn cache(&self, _: CoreAccess) -> Option<Self::Cache> {
570        let info = self.layouts.get_info_of(self.id)?.for_caching();
571        Some(info)
572    }
573
574    fn top_left(&self, _: CoreAccess) -> ui::Coord {
575        self.layouts
576            .coords_of(self.id, false)
577            .map(|coords| ui::Coord {
578                x: coords.tl.x as f32,
579                y: coords.tl.y as f32,
580            })
581            .unwrap_or_default()
582    }
583
584    fn bottom_right(&self, _: CoreAccess) -> ui::Coord {
585        self.layouts
586            .coords_of(self.id, false)
587            .map(|coords| ui::Coord {
588                x: coords.br.x as f32,
589                y: coords.br.y as f32,
590            })
591            .unwrap_or_default()
592    }
593
594    fn coord_at_points(
595        &self,
596        _: CoreAccess,
597        text: &Text,
598        points: TwoPoints,
599        opts: PrintOpts,
600    ) -> Option<ui::Coord> {
601        let Some(coords) = self.layouts.coords_of(self.id, false) else {
602            context::warn!("This Area was already deleted");
603            return None;
604        };
605
606        if coords.width() == 0 || coords.height() == 0 {
607            return None;
608        }
609
610        let (s_points, x_shift) = {
611            let mut info = self.layouts.get_info_of(self.id).unwrap();
612            let s_points = info.start_points(coords, text, opts);
613            self.layouts.set_info_of(self.id, info);
614            (s_points, info.x_shift())
615        };
616
617        let mut row = coords.tl.y;
618        for (caret, item) in print_iter(text, s_points, coords.width(), opts) {
619            row += caret.wrap as u32;
620
621            if row > coords.br.y {
622                break;
623            }
624
625            if item.points() == points && item.part.is_char() {
626                if caret.x >= x_shift && caret.x <= x_shift + coords.width() {
627                    return Some(ui::Coord {
628                        x: (coords.tl.x + caret.x - x_shift) as f32,
629                        y: (row - 1) as f32,
630                    });
631                } else {
632                    break;
633                }
634            }
635        }
636
637        None
638    }
639
640    fn points_at_coord(
641        &self,
642        _: CoreAccess,
643        text: &Text,
644        coord: ui::Coord,
645        opts: PrintOpts,
646    ) -> Option<TwoPointsPlace> {
647        let Some(coords) = self.layouts.coords_of(self.id, false) else {
648            context::warn!("This Area was already deleted");
649            return None;
650        };
651
652        if coords.width() == 0 || coords.height() == 0 {
653            return None;
654        } else if !(coords.tl.x..coords.br.x).contains(&(coord.x as u32))
655            || !(coords.tl.y..coords.br.y).contains(&(coord.y as u32))
656        {
657            context::warn!("Coordinate not contained in area");
658            return None;
659        }
660
661        let (s_points, x_shift) = {
662            let mut info = self.layouts.get_info_of(self.id).unwrap();
663            let s_points = info.start_points(coords, text, opts);
664            self.layouts.set_info_of(self.id, info);
665            (s_points, info.x_shift())
666        };
667
668        let mut row = coords.tl.y;
669        let mut backup = None;
670        for (caret, item) in print_iter(text, s_points, coords.width(), opts) {
671            row += caret.wrap as u32;
672
673            if row > coord.y as u32 + 1 {
674                return backup;
675            } else if row == coord.y as u32 + 1
676                && let Some(col) = caret.x.checked_sub(x_shift)
677            {
678                if (coords.tl.x + col..coords.tl.x + col + caret.len).contains(&(coord.x as u32)) {
679                    return Some(TwoPointsPlace::Within(item.points()));
680                } else if coords.tl.x + col >= coord.x as u32 {
681                    return backup;
682                }
683            }
684
685            if item.part.is_char() {
686                backup = Some(TwoPointsPlace::AheadOf(item.points()));
687            }
688        }
689
690        None
691    }
692
693    fn start_points(&self, _: CoreAccess, text: &Text, opts: PrintOpts) -> TwoPoints {
694        let Some(coords) = self.layouts.coords_of(self.id, false) else {
695            context::warn!("This Area was already deleted");
696            return Default::default();
697        };
698
699        let mut info = self.layouts.get_info_of(self.id).unwrap();
700        let start_points = info.start_points(coords, text, opts);
701        self.layouts.set_info_of(self.id, info);
702
703        start_points
704    }
705
706    fn end_points(&self, _: CoreAccess, text: &Text, opts: PrintOpts) -> TwoPoints {
707        let Some(coords) = self.layouts.coords_of(self.id, false) else {
708            context::warn!("This Area was already deleted");
709            return Default::default();
710        };
711
712        let mut info = self.layouts.get_info_of(self.id).unwrap();
713        let end_points = info.end_points(coords, text, opts);
714        self.layouts.set_info_of(self.id, info);
715
716        end_points
717    }
718
719    fn get_print_info(&self, _: CoreAccess) -> Self::PrintInfo {
720        self.layouts.get_info_of(self.id).unwrap_or_default()
721    }
722
723    fn is_active(&self, _: CoreAccess) -> bool {
724        self.layouts.get_active_id() == self.id
725    }
726}
727
728const fn get_control_str(char: char) -> Option<&'static str> {
729    match char {
730        '\0' => Some("^@"),
731        '\u{01}' => Some("^A"),
732        '\u{02}' => Some("^B"),
733        '\u{03}' => Some("^C"),
734        '\u{04}' => Some("^D"),
735        '\u{05}' => Some("^E"),
736        '\u{06}' => Some("^F"),
737        '\u{07}' => Some("^G"),
738        '\u{08}' => Some("^H"),
739        '\u{0b}' => Some("^K"),
740        '\u{0c}' => Some("^L"),
741        '\u{0e}' => Some("^N"),
742        '\u{0f}' => Some("^O"),
743        '\u{10}' => Some("^P"),
744        '\u{11}' => Some("^Q"),
745        '\u{12}' => Some("^R"),
746        '\u{13}' => Some("^S"),
747        '\u{14}' => Some("^T"),
748        '\u{15}' => Some("^U"),
749        '\u{16}' => Some("^V"),
750        '\u{17}' => Some("^W"),
751        '\u{18}' => Some("^X"),
752        '\u{19}' => Some("^Y"),
753        '\u{1a}' => Some("^Z"),
754        '\u{1b}' => Some("^["),
755        '\u{1c}' => Some("^\\"),
756        '\u{1d}' => Some("^]"),
757        '\u{1e}' => Some("^^"),
758        '\u{1f}' => Some("^_"),
759        '\u{80}' => Some("<80>"),
760        '\u{81}' => Some("<81>"),
761        '\u{82}' => Some("<82>"),
762        '\u{83}' => Some("<83>"),
763        '\u{84}' => Some("<84>"),
764        '\u{85}' => Some("<85>"),
765        '\u{86}' => Some("<86>"),
766        '\u{87}' => Some("<87>"),
767        '\u{88}' => Some("<88>"),
768        '\u{89}' => Some("<89>"),
769        '\u{8a}' => Some("<8a>"),
770        '\u{8b}' => Some("<8b>"),
771        '\u{8c}' => Some("<8c>"),
772        '\u{8d}' => Some("<8d>"),
773        '\u{8e}' => Some("<8e>"),
774        '\u{8f}' => Some("<8f>"),
775        '\u{90}' => Some("<90>"),
776        '\u{91}' => Some("<91>"),
777        '\u{92}' => Some("<92>"),
778        '\u{93}' => Some("<93>"),
779        '\u{94}' => Some("<94>"),
780        '\u{95}' => Some("<95>"),
781        '\u{96}' => Some("<96>"),
782        '\u{97}' => Some("<97>"),
783        '\u{98}' => Some("<98>"),
784        '\u{99}' => Some("<99>"),
785        '\u{9a}' => Some("<9a>"),
786        '\u{9b}' => Some("<9b>"),
787        '\u{9c}' => Some("<9c>"),
788        '\u{9d}' => Some("<9d>"),
789        '\u{9e}' => Some("<9e>"),
790        '\u{9f}' => Some("<9f>"),
791        _ => None,
792    }
793}