annotate_snippets/renderer/
styled_buffer.rs1use crate::renderer::stylesheet::Stylesheet;
6use crate::renderer::ElementStyle;
7use crate::Level;
8
9use std::fmt;
10use std::fmt::Write;
11
12#[derive(Debug)]
13pub(crate) struct StyledBuffer {
14 lines: Vec<Vec<StyledChar>>,
15}
16
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub(crate) struct StyledChar {
19 ch: char,
20 style: ElementStyle,
21}
22
23impl StyledChar {
24 pub(crate) const SPACE: Self = StyledChar::new(' ', ElementStyle::NoStyle);
25
26 pub(crate) const fn new(ch: char, style: ElementStyle) -> StyledChar {
27 StyledChar { ch, style }
28 }
29}
30
31impl StyledBuffer {
32 pub(crate) fn new() -> StyledBuffer {
33 StyledBuffer { lines: vec![] }
34 }
35
36 fn ensure_lines(&mut self, line: usize) {
37 if line >= self.lines.len() {
38 self.lines.resize(line + 1, Vec::new());
39 }
40 }
41
42 pub(crate) fn render(
43 &self,
44 level: &Level<'_>,
45 stylesheet: &Stylesheet,
46 str: &mut String,
47 ) -> Result<(), fmt::Error> {
48 for (i, line) in self.lines.iter().enumerate() {
49 let mut current_style = stylesheet.none;
50 for StyledChar { ch, style } in line {
51 let ch_style = style.color_spec(level, stylesheet);
52 if ch_style != current_style {
53 if !line.is_empty() {
54 write!(str, "{current_style:#}")?;
55 }
56 current_style = ch_style;
57 write!(str, "{current_style}")?;
58 }
59 write!(str, "{ch}")?;
60 }
61 write!(str, "{current_style:#}")?;
62 if i != self.lines.len() - 1 {
63 writeln!(str)?;
64 }
65 }
66 Ok(())
67 }
68
69 pub(crate) fn putc(&mut self, line: usize, col: usize, chr: char, style: ElementStyle) {
73 self.ensure_lines(line);
74 if col >= self.lines[line].len() {
75 self.lines[line].resize(col + 1, StyledChar::SPACE);
76 }
77 self.lines[line][col] = StyledChar::new(chr, style);
78 }
79
80 pub(crate) fn puts(&mut self, line: usize, col: usize, string: &str, style: ElementStyle) {
84 let mut n = col;
85 for c in string.chars() {
86 self.putc(line, n, c, style);
87 n += 1;
88 }
89 }
90
91 pub(crate) fn append(&mut self, line: usize, string: &str, style: ElementStyle) {
94 if line >= self.lines.len() {
95 self.puts(line, 0, string, style);
96 } else {
97 let col = self.lines[line].len();
98 self.puts(line, col, string, style);
99 }
100 }
101
102 pub(crate) fn replace(&mut self, line: usize, start: usize, end: usize, string: &str) {
103 if start == end {
104 return;
105 }
106 if start > self.lines[line].len() || end > self.lines[line].len() {
109 return;
110 }
111 let _ = self.lines[line].drain(start..(end - string.chars().count()));
112 for (i, c) in string.chars().enumerate() {
113 self.lines[line][start + i] = StyledChar::new(c, ElementStyle::LineNumber);
114 }
115 }
116
117 pub(crate) fn prepend(&mut self, line: usize, string: &str, style: ElementStyle) {
120 self.ensure_lines(line);
121 let string_len = string.chars().count();
122
123 if !self.lines[line].is_empty() {
124 for _ in 0..string_len {
126 self.lines[line].insert(0, StyledChar::SPACE);
127 }
128 }
129
130 self.puts(line, 0, string, style);
131 }
132
133 pub(crate) fn num_lines(&self) -> usize {
134 self.lines.len()
135 }
136
137 pub(crate) fn set_style_range(
141 &mut self,
142 line: usize,
143 col_start: usize,
144 col_end: usize,
145 style: ElementStyle,
146 overwrite: bool,
147 ) {
148 for col in col_start..col_end {
149 self.set_style(line, col, style, overwrite);
150 }
151 }
152
153 pub(crate) fn set_style(
157 &mut self,
158 line: usize,
159 col: usize,
160 style: ElementStyle,
161 overwrite: bool,
162 ) {
163 if let Some(ref mut line) = self.lines.get_mut(line) {
164 if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
165 if overwrite || matches!(s, ElementStyle::NoStyle | ElementStyle::Quotation) {
166 *s = style;
167 }
168 }
169 }
170 }
171}