]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_errors/src/styled_buffer.rs
Rollup merge of #101600 - notriddle:notriddle/li, r=GuillaumeGomez
[rust.git] / compiler / rustc_errors / src / styled_buffer.rs
1 // Code for creating styled buffers
2
3 use crate::snippet::{Style, StyledString};
4
5 #[derive(Debug)]
6 pub struct StyledBuffer {
7     lines: Vec<Vec<StyledChar>>,
8 }
9
10 #[derive(Debug, Clone)]
11 struct StyledChar {
12     chr: char,
13     style: Style,
14 }
15
16 impl StyledChar {
17     const SPACE: Self = StyledChar::new(' ', Style::NoStyle);
18
19     const fn new(chr: char, style: Style) -> Self {
20         StyledChar { chr, style }
21     }
22 }
23
24 impl StyledBuffer {
25     pub fn new() -> StyledBuffer {
26         StyledBuffer { lines: vec![] }
27     }
28
29     /// Returns content of `StyledBuffer` split by lines and line styles
30     pub fn render(&self) -> Vec<Vec<StyledString>> {
31         // Tabs are assumed to have been replaced by spaces in calling code.
32         debug_assert!(self.lines.iter().all(|r| !r.iter().any(|sc| sc.chr == '\t')));
33
34         let mut output: Vec<Vec<StyledString>> = vec![];
35         let mut styled_vec: Vec<StyledString> = vec![];
36
37         for styled_line in &self.lines {
38             let mut current_style = Style::NoStyle;
39             let mut current_text = String::new();
40
41             for sc in styled_line {
42                 if sc.style != current_style {
43                     if !current_text.is_empty() {
44                         styled_vec.push(StyledString { text: current_text, style: current_style });
45                     }
46                     current_style = sc.style;
47                     current_text = String::new();
48                 }
49                 current_text.push(sc.chr);
50             }
51             if !current_text.is_empty() {
52                 styled_vec.push(StyledString { text: current_text, style: current_style });
53             }
54
55             // We're done with the row, push and keep going
56             output.push(styled_vec);
57
58             styled_vec = vec![];
59         }
60
61         output
62     }
63
64     fn ensure_lines(&mut self, line: usize) {
65         if line >= self.lines.len() {
66             self.lines.resize(line + 1, Vec::new());
67         }
68     }
69
70     /// Sets `chr` with `style` for given `line`, `col`.
71     /// If `line` does not exist in our buffer, adds empty lines up to the given
72     /// and fills the last line with unstyled whitespace.
73     pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
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     /// Sets `string` with `style` for given `line`, starting from `col`.
82     /// If `line` does not exist in our buffer, adds empty lines up to the given
83     /// and fills the last line with unstyled whitespace.
84     pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
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     /// For given `line` inserts `string` with `style` before old content of that line,
93     /// adding lines if needed
94     pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
95         self.ensure_lines(line);
96         let string_len = string.chars().count();
97
98         if !self.lines[line].is_empty() {
99             // Push the old content over to make room for new content
100             for _ in 0..string_len {
101                 self.lines[line].insert(0, StyledChar::SPACE);
102             }
103         }
104
105         self.puts(line, 0, string, style);
106     }
107
108     /// For given `line` inserts `string` with `style` after old content of that line,
109     /// adding lines if needed
110     pub fn append(&mut self, line: usize, string: &str, style: Style) {
111         if line >= self.lines.len() {
112             self.puts(line, 0, string, style);
113         } else {
114             let col = self.lines[line].len();
115             self.puts(line, col, string, style);
116         }
117     }
118
119     pub fn num_lines(&self) -> usize {
120         self.lines.len()
121     }
122
123     /// Set `style` for `line`, `col_start..col_end` range if:
124     /// 1. That line and column range exist in `StyledBuffer`
125     /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
126     pub fn set_style_range(
127         &mut self,
128         line: usize,
129         col_start: usize,
130         col_end: usize,
131         style: Style,
132         overwrite: bool,
133     ) {
134         for col in col_start..col_end {
135             self.set_style(line, col, style, overwrite);
136         }
137     }
138
139     /// Set `style` for `line`, `col` if:
140     /// 1. That line and column exist in `StyledBuffer`
141     /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
142     pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
143         if let Some(ref mut line) = self.lines.get_mut(line) {
144             if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
145                 if overwrite || *s == Style::NoStyle || *s == Style::Quotation {
146                     *s = style;
147                 }
148             }
149         }
150     }
151 }