1 | //! Adapted from [styled_buffer] |
2 | //! |
3 | //! [styled_buffer]: https://github.com/rust-lang/rust/blob/894f7a4ba6554d3797404bbf550d9919df060b97/compiler/rustc_errors/src/styled_buffer.rs |
4 | |
5 | use crate::renderer::stylesheet::Stylesheet; |
6 | use anstyle::Style; |
7 | use std::fmt; |
8 | use std::fmt::Write; |
9 | |
10 | #[derive (Debug)] |
11 | pub(crate) struct StyledBuffer { |
12 | lines: Vec<Vec<StyledChar>>, |
13 | } |
14 | |
15 | #[derive (Clone, Copy, Debug, PartialEq)] |
16 | pub(crate) struct StyledChar { |
17 | ch: char, |
18 | style: Style, |
19 | } |
20 | |
21 | impl StyledChar { |
22 | pub(crate) const SPACE: Self = StyledChar::new(ch:' ' , Style::new()); |
23 | |
24 | pub(crate) const fn new(ch: char, style: Style) -> StyledChar { |
25 | StyledChar { ch, style } |
26 | } |
27 | } |
28 | |
29 | impl StyledBuffer { |
30 | pub(crate) fn new() -> StyledBuffer { |
31 | StyledBuffer { lines: vec![] } |
32 | } |
33 | |
34 | fn ensure_lines(&mut self, line: usize) { |
35 | if line >= self.lines.len() { |
36 | self.lines.resize(line + 1, Vec::new()); |
37 | } |
38 | } |
39 | |
40 | pub(crate) fn render(&self, stylesheet: &Stylesheet) -> Result<String, fmt::Error> { |
41 | let mut str = String::new(); |
42 | for (i, line) in self.lines.iter().enumerate() { |
43 | let mut current_style = stylesheet.none; |
44 | for ch in line { |
45 | if ch.style != current_style { |
46 | if !line.is_empty() { |
47 | write!(str, " {}" , current_style.render_reset())?; |
48 | } |
49 | current_style = ch.style; |
50 | write!(str, " {}" , current_style.render())?; |
51 | } |
52 | write!(str, " {}" , ch.ch)?; |
53 | } |
54 | write!(str, " {}" , current_style.render_reset())?; |
55 | if i != self.lines.len() - 1 { |
56 | writeln!(str)?; |
57 | } |
58 | } |
59 | Ok(str) |
60 | } |
61 | |
62 | /// Sets `chr` with `style` for given `line`, `col`. |
63 | /// If `line` does not exist in our buffer, adds empty lines up to the given |
64 | /// and fills the last line with unstyled whitespace. |
65 | pub(crate) fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) { |
66 | self.ensure_lines(line); |
67 | if col >= self.lines[line].len() { |
68 | self.lines[line].resize(col + 1, StyledChar::SPACE); |
69 | } |
70 | self.lines[line][col] = StyledChar::new(chr, style); |
71 | } |
72 | |
73 | /// Sets `string` with `style` for given `line`, starting from `col`. |
74 | /// If `line` does not exist in our buffer, adds empty lines up to the given |
75 | /// and fills the last line with unstyled whitespace. |
76 | pub(crate) fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) { |
77 | let mut n = col; |
78 | for c in string.chars() { |
79 | self.putc(line, n, c, style); |
80 | n += 1; |
81 | } |
82 | } |
83 | /// For given `line` inserts `string` with `style` after old content of that line, |
84 | /// adding lines if needed |
85 | pub(crate) fn append(&mut self, line: usize, string: &str, style: Style) { |
86 | if line >= self.lines.len() { |
87 | self.puts(line, 0, string, style); |
88 | } else { |
89 | let col = self.lines[line].len(); |
90 | self.puts(line, col, string, style); |
91 | } |
92 | } |
93 | |
94 | pub(crate) fn num_lines(&self) -> usize { |
95 | self.lines.len() |
96 | } |
97 | } |
98 | |