1use crate::highlighting::{Color, Style, StyleModifier};
7#[cfg(feature = "parsing")]
8use crate::parsing::ScopeStackOp;
9use std::fmt::Write;
10use std::ops::Range;
11
12#[inline]
13fn blend_fg_color(fg: Color, bg: Color) -> Color {
14 if fg.a == 0xff {
15 return fg;
16 }
17 let ratio = fg.a as u32;
18 let r = (fg.r as u32 * ratio + bg.r as u32 * (255 - ratio)) / 255;
19 let g = (fg.g as u32 * ratio + bg.g as u32 * (255 - ratio)) / 255;
20 let b = (fg.b as u32 * ratio + bg.b as u32 * (255 - ratio)) / 255;
21 Color {
22 r: r as u8,
23 g: g as u8,
24 b: b as u8,
25 a: 255,
26 }
27}
28
29pub fn as_24_bit_terminal_escaped(v: &[(Style, &str)], bg: bool) -> String {
40 let mut s: String = String::new();
41 for &(ref style, text) in v.iter() {
42 if bg {
43 write!(
44 s,
45 "\x1b[48;2;{};{};{}m",
46 style.background.r, style.background.g, style.background.b
47 )
48 .unwrap();
49 }
50 let fg = blend_fg_color(style.foreground, style.background);
51 write!(s, "\x1b[38;2;{};{};{}m{}", fg.r, fg.g, fg.b, text).unwrap();
52 }
53 s
55}
56
57const LATEX_REPLACE: [(&str, &str); 3] = [("\\", "\\\\"), ("{", "\\{"), ("}", "\\}")];
58
59pub fn as_latex_escaped(v: &[(Style, &str)]) -> String {
103 let mut s: String = String::new();
104 let mut prev_style: Option<Style> = None;
105 let mut content: String;
106 fn textcolor(style: &Style, first: bool) -> String {
107 format!(
108 "{}\\textcolor[RGB]{{{},{},{}}}{{",
109 if first { "" } else { "}" },
110 style.foreground.r,
111 style.foreground.b,
112 style.foreground.g
113 )
114 }
115 for &(style, text) in v.iter() {
116 if let Some(ps) = prev_style {
117 match text {
118 " " => {
119 s.push(' ');
120 continue;
121 }
122 "\n" => continue,
123 _ => (),
124 }
125 if style != ps {
126 write!(s, "{}", textcolor(&style, false)).unwrap();
127 }
128 } else {
129 write!(s, "{}", textcolor(&style, true)).unwrap();
130 }
131 content = text.to_string();
132 for &(old, new) in LATEX_REPLACE.iter() {
133 content = content.replace(old, new);
134 }
135 write!(s, "{}", &content).unwrap();
136 prev_style = Some(style);
137 }
138 s.push('}');
139 s
140}
141
142#[cfg(feature = "parsing")]
145pub fn debug_print_ops(line: &str, ops: &[(usize, ScopeStackOp)]) {
146 for &(i, ref op) in ops.iter() {
147 println!("{}", line.trim_end());
148 print!("{: <1$}", "", i);
149 match *op {
150 ScopeStackOp::Push(s) => {
151 println!("^ +{}", s);
152 }
153 ScopeStackOp::Pop(count) => {
154 println!("^ pop {}", count);
155 }
156 ScopeStackOp::Clear(amount) => {
157 println!("^ clear {:?}", amount);
158 }
159 ScopeStackOp::Restore => println!("^ restore"),
160 ScopeStackOp::Noop => println!("noop"),
161 }
162 }
163}
164
165pub struct LinesWithEndings<'a> {
188 input: &'a str,
189}
190
191impl<'a> LinesWithEndings<'a> {
192 pub fn from(input: &'a str) -> LinesWithEndings<'a> {
193 LinesWithEndings { input }
194 }
195}
196
197impl<'a> Iterator for LinesWithEndings<'a> {
198 type Item = &'a str;
199
200 #[inline]
201 fn next(&mut self) -> Option<&'a str> {
202 if self.input.is_empty() {
203 return None;
204 }
205 let split = self
206 .input
207 .find('\n')
208 .map(|i| i + 1)
209 .unwrap_or_else(|| self.input.len());
210 let (line, rest) = self.input.split_at(split);
211 self.input = rest;
212 Some(line)
213 }
214}
215
216#[allow(clippy::type_complexity)]
229pub fn split_at<'a, A: Clone>(
230 v: &[(A, &'a str)],
231 split_i: usize,
232) -> (Vec<(A, &'a str)>, Vec<(A, &'a str)>) {
233 let mut rest = v;
235 let mut rest_split_i = split_i;
236
237 let mut before = Vec::new();
239 for tok in rest {
240 if tok.1.len() > rest_split_i {
242 break;
243 }
244 before.push(tok.clone());
245 rest_split_i -= tok.1.len();
246 }
247 rest = &rest[before.len()..];
248
249 let mut after = Vec::new();
250 if !rest.is_empty() && rest_split_i > 0 {
252 let mut rest_split_index = rest_split_i;
253 while !rest[0].1.is_char_boundary(rest_split_index) && rest_split_index > 0 {
257 rest_split_index -= 1;
258 }
259 let (sa, sb) = rest[0].1.split_at(rest_split_index);
260 before.push((rest[0].0.clone(), sa));
261 after.push((rest[0].0.clone(), sb));
262 rest = &rest[1..];
263 }
264
265 after.extend_from_slice(rest);
266
267 (before, after)
268}
269
270pub fn modify_range<'a>(
287 v: &[(Style, &'a str)],
288 r: Range<usize>,
289 modifier: StyleModifier,
290) -> Vec<(Style, &'a str)> {
291 let (mut result, in_and_after) = split_at(v, r.start);
292 let (inside, mut after) = split_at(&in_and_after, r.end - r.start);
293
294 result.extend(inside.iter().map(|(style, s)| (style.apply(modifier), *s)));
295 result.append(&mut after);
296 result
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302 use crate::highlighting::FontStyle;
303
304 #[test]
305 fn test_lines_with_endings() {
306 fn lines(s: &str) -> Vec<&str> {
307 LinesWithEndings::from(s).collect()
308 }
309
310 assert!(lines("").is_empty());
311 assert_eq!(lines("f"), vec!["f"]);
312 assert_eq!(lines("foo"), vec!["foo"]);
313 assert_eq!(lines("foo\n"), vec!["foo\n"]);
314 assert_eq!(lines("foo\nbar"), vec!["foo\n", "bar"]);
315 assert_eq!(lines("foo\nbar\n"), vec!["foo\n", "bar\n"]);
316 assert_eq!(lines("foo\r\nbar"), vec!["foo\r\n", "bar"]);
317 assert_eq!(lines("foo\r\nbar\r\n"), vec!["foo\r\n", "bar\r\n"]);
318 assert_eq!(lines("\nfoo"), vec!["\n", "foo"]);
319 assert_eq!(lines("\n\n\n"), vec!["\n", "\n", "\n"]);
320 }
321
322 #[test]
323 fn test_split_at() {
324 let l: &[(u8, &str)] = &[];
325 let (before, after) = split_at(l, 0); assert_eq!((&before[..], &after[..]), (&[][..], &[][..]));
327
328 let l = &[(0u8, "abc"), (1u8, "def"), (2u8, "ghi")];
329
330 let (before, after) = split_at(l, 0); assert_eq!(
332 (&before[..], &after[..]),
333 (&[][..], &[(0u8, "abc"), (1u8, "def"), (2u8, "ghi")][..])
334 );
335
336 let (before, after) = split_at(l, 4); assert_eq!(
338 (&before[..], &after[..]),
339 (
340 &[(0u8, "abc"), (1u8, "d")][..],
341 &[(1u8, "ef"), (2u8, "ghi")][..]
342 )
343 );
344
345 let (before, after) = split_at(l, 3); assert_eq!(
347 (&before[..], &after[..]),
348 (&[(0u8, "abc")][..], &[(1u8, "def"), (2u8, "ghi")][..])
349 );
350
351 let (before, after) = split_at(l, 9); assert_eq!(
353 (&before[..], &after[..]),
354 (&[(0u8, "abc"), (1u8, "def"), (2u8, "ghi")][..], &[][..])
355 );
356
357 let (before, after) = split_at(l, 10); assert_eq!(
359 (&before[..], &after[..]),
360 (&[(0u8, "abc"), (1u8, "def"), (2u8, "ghi")][..], &[][..])
361 );
362
363 let l = &[(0u8, "こんにちは"), (1u8, "世界"), (2u8, "!")];
364
365 let (before, after) = split_at(l, 3);
366
367 assert_eq!(
368 (&before[..], &after[..]),
369 (
370 &[(0u8, "こ")][..],
371 &[(0u8, "んにちは"), (1u8, "世界"), (2u8, "!")][..]
372 )
373 );
374
375 let (before, after) = split_at(l, 4);
379
380 assert_eq!(
381 (&before[..], &after[..]),
382 (
383 &[(0u8, "こ")][..],
384 &[(0u8, "んにちは"), (1u8, "世界"), (2u8, "!")][..]
385 )
386 );
387 }
388
389 #[test]
390 fn test_as_24_bit_terminal_escaped() {
391 let style = Style {
392 foreground: Color::WHITE,
393 background: Color::BLACK,
394 font_style: FontStyle::default(),
395 };
396
397 let s = as_24_bit_terminal_escaped(&[(style, "hello")], true);
399 assert_eq!(s, "\x1b[48;2;0;0;0m\x1b[38;2;255;255;255mhello");
400
401 let s = as_24_bit_terminal_escaped(&[(style, "hello")], false);
403 assert_eq!(s, "\x1b[38;2;255;255;255mhello");
404
405 let mut foreground = Color::WHITE;
407 foreground.a = 128;
408 let style = Style {
409 foreground,
410 background: Color::BLACK,
411 font_style: FontStyle::default(),
412 };
413 let s = as_24_bit_terminal_escaped(&[(style, "hello")], true);
414 assert_eq!(s, "\x1b[48;2;0;0;0m\x1b[38;2;128;128;128mhello");
415 }
416}