annotate_snippets/renderer/
styled_buffer.rs

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
5use crate::renderer::stylesheet::Stylesheet;
6use anstyle::Style;
7use std::fmt;
8use std::fmt::Write;
9
10#[derive(Debug)]
11pub(crate) struct StyledBuffer {
12    lines: Vec<Vec<StyledChar>>,
13}
14
15#[derive(Clone, Copy, Debug, PartialEq)]
16pub(crate) struct StyledChar {
17    ch: char,
18    style: Style,
19}
20
21impl StyledChar {
22    pub(crate) const SPACE: Self = StyledChar::new(' ', Style::new());
23
24    pub(crate) const fn new(ch: char, style: Style) -> StyledChar {
25        StyledChar { ch, style }
26    }
27}
28
29impl 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}