rustc_errors/
styled_buffer.rs

1// Code for creating styled buffers
2
3use crate::snippet::{Style, StyledString};
4
5#[derive(Debug)]
6pub(crate) struct StyledBuffer {
7    lines: Vec<Vec<StyledChar>>,
8}
9
10#[derive(Debug, Clone)]
11struct StyledChar {
12    chr: char,
13    style: Style,
14}
15
16impl 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
24impl StyledBuffer {
25    pub(crate) fn new() -> StyledBuffer {
26        StyledBuffer { lines: vec![] }
27    }
28
29    /// Returns content of `StyledBuffer` split by lines and line styles
30    pub(crate) 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(crate) 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(crate) 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    pub(crate) fn replace(&mut self, line: usize, start: usize, end: usize, string: &str) {
93        if start == end {
94            return;
95        }
96        if start > self.lines[line].len() || end > self.lines[line].len() {
97            return;
98        }
99        let _ = self.lines[line].drain(start..(end - string.chars().count()));
100        for (i, c) in string.chars().enumerate() {
101            self.lines[line][start + i] = StyledChar::new(c, Style::LineNumber);
102        }
103    }
104
105    /// For given `line` inserts `string` with `style` before old content of that line,
106    /// adding lines if needed
107    pub(crate) fn prepend(&mut self, line: usize, string: &str, style: Style) {
108        self.ensure_lines(line);
109        let string_len = string.chars().count();
110
111        if !self.lines[line].is_empty() {
112            // Push the old content over to make room for new content
113            for _ in 0..string_len {
114                self.lines[line].insert(0, StyledChar::SPACE);
115            }
116        }
117
118        self.puts(line, 0, string, style);
119    }
120
121    /// For given `line` inserts `string` with `style` after old content of that line,
122    /// adding lines if needed
123    pub(crate) fn append(&mut self, line: usize, string: &str, style: Style) {
124        if line >= self.lines.len() {
125            self.puts(line, 0, string, style);
126        } else {
127            let col = self.lines[line].len();
128            self.puts(line, col, string, style);
129        }
130    }
131
132    pub(crate) fn num_lines(&self) -> usize {
133        self.lines.len()
134    }
135
136    /// Set `style` for `line`, `col_start..col_end` range if:
137    /// 1. That line and column range exist in `StyledBuffer`
138    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
139    pub(crate) fn set_style_range(
140        &mut self,
141        line: usize,
142        col_start: usize,
143        col_end: usize,
144        style: Style,
145        overwrite: bool,
146    ) {
147        for col in col_start..col_end {
148            self.set_style(line, col, style, overwrite);
149        }
150    }
151
152    /// Set `style` for `line`, `col` if:
153    /// 1. That line and column exist in `StyledBuffer`
154    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
155    fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
156        if let Some(ref mut line) = self.lines.get_mut(line) {
157            if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
158                if overwrite || matches!(s, Style::NoStyle | Style::Quotation) {
159                    *s = style;
160                }
161            }
162        }
163    }
164}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy