| 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 | |