git_igitt/util/
ctrl_chars.rs1use std::fmt;
2
3use muncher::Muncher;
4use tui::style::{Color, Style};
5use tui::text::Text;
6
7#[derive(Clone, Debug, Default)]
8pub struct CtrlChunk {
9 ctrl: Vec<String>,
10 text: String,
11}
12
13impl CtrlChunk {
14 pub fn text(text: String) -> Self {
15 Self {
16 ctrl: Vec::new(),
17 text,
18 }
19 }
20
21 pub fn parse(munch: &mut Muncher) -> Self {
22 munch.reset_peek();
23 if munch.seek(1) == Some("\x1B") {
24 munch.eat();
25 }
26
27 let text_or_ctrl = munch.eat_until(|c| *c == '\x1B').collect::<String>();
28
29 if text_or_ctrl.is_empty() {
30 return Self {
31 ctrl: Vec::new(),
32 text: String::new(),
33 };
34 }
35
36 munch.reset_peek();
37
38 if munch.seek(4) == Some("\x1B[0m") {
39 let _ = munch.eat_until(|c| *c == 'm');
41 munch.eat();
42
43 let mut ctrl_chars = Vec::new();
44 loop {
45 let ctrl_text = text_or_ctrl.splitn(2, 'm').collect::<Vec<_>>();
46
47 let mut ctrl = vec![ctrl_text[0].replace('[', "")];
48 if ctrl[0].contains(';') {
49 ctrl = ctrl[0].split(';').map(|s| s.to_string()).collect();
50 }
51 ctrl_chars.extend(ctrl);
52 if ctrl_text[1].contains('\x1B') {
53 continue;
54 } else {
55 let mut text = ctrl_text[1].to_string();
56
57 let ws = munch.eat_until(|c| !c.is_whitespace()).collect::<String>();
58 text.push_str(&ws);
59
60 return Self {
61 ctrl: ctrl_chars,
62 text,
63 };
64 }
65 }
66 } else {
67 Self {
69 ctrl: Vec::new(),
70 text: text_or_ctrl,
71 }
72 }
73 }
74 pub fn into_text<'a>(self) -> Text<'a> {
75 let mut style = Style::default();
76 if self.ctrl.len() > 2 {
77 match &self.ctrl[2] {
78 ctrl if ctrl == "0" => {
79 style = style.fg(Color::Black);
80 }
81 ctrl if ctrl == "1" => {
82 style = style.fg(Color::Red);
83 }
84 ctrl if ctrl == "2" => {
85 style = style.fg(Color::Green);
86 }
87 ctrl if ctrl == "3" => {
88 style = style.fg(Color::Yellow);
89 }
90 ctrl if ctrl == "4" => {
91 style = style.fg(Color::Blue);
92 }
93 ctrl if ctrl == "5" => {
94 style = style.fg(Color::Magenta);
95 }
96 ctrl if ctrl == "6" => {
97 style = style.fg(Color::Cyan);
98 }
99 ctrl if ctrl == "7" => {
100 style = style.fg(Color::White);
101 }
102 ctrl if ctrl == "8" => {
104 style = style.fg(Color::DarkGray);
105 }
106 ctrl if ctrl == "9" => {
107 style = style.fg(Color::LightRed);
108 }
109 ctrl if ctrl == "10" => {
110 style = style.fg(Color::LightGreen);
111 }
112 ctrl if ctrl == "11" => {
113 style = style.fg(Color::LightYellow);
114 }
115 ctrl if ctrl == "12" => {
116 style = style.fg(Color::LightBlue);
117 }
118 ctrl if ctrl == "13" => {
119 style = style.fg(Color::LightMagenta);
120 }
121 ctrl if ctrl == "14" => {
122 style = style.fg(Color::LightCyan);
123 }
124 ctrl if ctrl == "15" => {
127 style = style.fg(Color::White);
128 }
129 _ => return Text::raw(self.text),
131 }
132 } else {
133 return Text::raw(self.text);
134 }
135 Text::styled(self.text, style)
136 }
137}
138
139impl fmt::Display for CtrlChunk {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 let ctrl_code = self
142 .ctrl
143 .iter()
144 .map(|c| {
145 if c == "38;5;" {
146 format!("\x1B]{}", c)
147 } else {
148 format!("\x1B[{}", c)
149 }
150 })
151 .collect::<String>();
152 if ctrl_code.is_empty() && self.text.is_empty() {
153 Ok(())
154 } else {
155 write!(f, "{}{}", ctrl_code, self.text)
156 }
157 }
158}
159
160#[derive(Clone, Debug, Default)]
161pub struct CtrlChars {
162 parsed: Vec<CtrlChunk>,
163}
164
165impl fmt::Display for CtrlChars {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 let text = self
168 .parsed
169 .iter()
170 .map(CtrlChunk::to_string)
171 .collect::<String>();
172 write!(f, "{}", text)
173 }
174}
175
176impl CtrlChars {
177 pub fn parse(input: &str) -> Self {
178 let mut parsed = Vec::new();
179
180 let mut munch = Muncher::new(input);
181 let pre_ctrl = munch.eat_until(|c| *c == '\x1B').collect::<String>();
182 parsed.push(CtrlChunk::text(pre_ctrl));
183
184 loop {
185 if munch.is_done() {
186 break;
187 } else {
188 parsed.push(CtrlChunk::parse(&mut munch))
189 }
190 }
191 Self { parsed }
192 }
193
194 pub fn into_text<'a>(self) -> Vec<Text<'a>> {
195 self.parsed.into_iter().map(CtrlChunk::into_text).collect()
196 }
197}