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