layered_nlp/ll_line/
display.rs

1use super::*;
2use unicode_width::UnicodeWidthStr;
3
4pub struct LLLineDisplay<'a> {
5    ll_line: &'a LLLine,
6    include_attrs: Vec<(LRange, String)>,
7}
8
9// 0,  1,     2,   3, - LRange indexes
10// 0,  1,     5,   6, - LLToken::pos_starts_at indexes
11// 1,  5,     6,   8, - LLToken::pos_ends_at indexes
12// $   1000   .    00
13//                ╰NATN
14//            ╰PUNC
15//     ╰NATN
16// ╰PUNC
17//     ╰────────────╯ Amount()
18// ╰────────────────╯ Money($, Num)
19//
20// 0,  1,     2,   3, - LRange indexes
21// 0,  1,     5,   6, - LLToken::pos_starts_at indexes
22// 1,  5,     6,   8, - LLToken::pos_ends_at indexes
23// _   1000   .    00    ;    123
24//                            ╰NATN
25//                       ╰PUNC
26//                 ╰NATN
27//            ╰PUNC
28//     ╰NATN
29// ╰SPACE
30//     ╰────────────╯ Amount(..)
31//                            ╰─╯ Amount(..)
32impl<'a> std::fmt::Display for LLLineDisplay<'a> {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        const SPACE_PADDING: usize = 2;
35        let mut token_idx_to_start_display_char_idx = Vec::new();
36        let mut token_idx_to_end_display_char_idx = Vec::new();
37        // write opening display text
38        let mut opening_line = String::new();
39        {
40            // for skipping padding at beginning
41            let mut is_first = true;
42            for ll_token in self.ll_line.ll_tokens.iter() {
43                if is_first {
44                    is_first = false;
45                } else {
46                    opening_line.extend(std::iter::repeat(' ').take(SPACE_PADDING));
47                }
48
49                token_idx_to_start_display_char_idx.push(UnicodeWidthStr::width(&*opening_line));
50
51                match &ll_token.token {
52                    LToken::Text(text, _) => {
53                        opening_line.push_str(&text);
54                    }
55                    LToken::Value { .. } => {
56                        write!(&mut opening_line, "<>")?;
57                    }
58                }
59
60                token_idx_to_end_display_char_idx.push(UnicodeWidthStr::width(&*opening_line));
61            }
62        }
63
64        f.write_str(&opening_line)?;
65
66        // ex:
67        //     ╰────────────╯ Amount(..)
68        //                            ╰─╯ Amount(..)
69        for ((starts_at_token_idx, ends_at_token_idx), debug_value) in self.include_attrs.iter() {
70            f.write_char('\n')?;
71
72            let start_char_idx = token_idx_to_start_display_char_idx[*starts_at_token_idx];
73            for _ in 0..start_char_idx {
74                f.write_char(' ')?;
75            }
76
77            f.write_char('╰')?;
78
79            let end_char_idx = token_idx_to_end_display_char_idx[*ends_at_token_idx];
80            let char_len = end_char_idx - start_char_idx;
81            for _ in (start_char_idx + 1)..end_char_idx.saturating_sub(1) {
82                f.write_char('─')?;
83            }
84
85            if char_len > 1 {
86                f.write_char('╯')?;
87            }
88
89            f.write_str(&debug_value)?;
90        }
91
92        Ok(())
93    }
94}
95
96impl<'a> LLLineDisplay<'a> {
97    pub fn new(ll_line: &'a LLLine) -> Self {
98        LLLineDisplay {
99            ll_line,
100            include_attrs: Vec::new(),
101        }
102    }
103    // TODO consider making this method take and return `self`
104    pub fn include<T: 'static + std::fmt::Debug>(&mut self) {
105        for ll_range in self.ll_line.attrs.ranges.get::<T>() {
106            for debug_value in self
107                .ll_line
108                .attrs
109                .values
110                .get(ll_range)
111                .into_iter()
112                .flat_map(|type_bucket| type_bucket.get_debug::<T>())
113                .rev()
114            {
115                self.include_attrs.push((*ll_range, debug_value));
116            }
117        }
118    }
119    /// Takes self
120    pub fn with<T: 'static + std::fmt::Debug>(mut self) -> Self {
121        self.include::<T>();
122        self
123    }
124}