rust/compiler/rustc_errors/src/styled_buffer.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

152 lines
4.9 KiB
Rust
Raw Normal View History

// Code for creating styled buffers
2019-02-07 03:53:01 +09:00
use crate::snippet::{Style, StyledString};
#[derive(Debug)]
pub struct StyledBuffer {
2021-04-16 05:38:32 +03:00
lines: Vec<Vec<StyledChar>>,
}
2021-04-18 02:15:15 +03:00
#[derive(Debug, Clone)]
struct StyledChar {
chr: char,
style: Style,
}
impl StyledChar {
2021-04-18 02:15:15 +03:00
const SPACE: Self = StyledChar::new(' ', Style::NoStyle);
2021-04-18 02:15:15 +03:00
const fn new(chr: char, style: Style) -> Self {
StyledChar { chr, style }
2021-04-16 03:20:07 +03:00
}
}
impl StyledBuffer {
pub fn new() -> StyledBuffer {
2021-04-16 05:38:32 +03:00
StyledBuffer { lines: vec![] }
}
/// Returns content of `StyledBuffer` split by lines and line styles
pub fn render(&self) -> Vec<Vec<StyledString>> {
// Tabs are assumed to have been replaced by spaces in calling code.
2021-04-16 05:38:32 +03:00
debug_assert!(self.lines.iter().all(|r| !r.iter().any(|sc| sc.chr == '\t')));
let mut output: Vec<Vec<StyledString>> = vec![];
let mut styled_vec: Vec<StyledString> = vec![];
2021-04-16 05:38:32 +03:00
for styled_line in &self.lines {
let mut current_style = Style::NoStyle;
let mut current_text = String::new();
2021-04-16 05:38:32 +03:00
for sc in styled_line {
if sc.style != current_style {
if !current_text.is_empty() {
styled_vec.push(StyledString { text: current_text, style: current_style });
}
current_style = sc.style;
current_text = String::new();
}
current_text.push(sc.chr);
}
if !current_text.is_empty() {
styled_vec.push(StyledString { text: current_text, style: current_style });
}
// We're done with the row, push and keep going
output.push(styled_vec);
styled_vec = vec![];
}
output
}
fn ensure_lines(&mut self, line: usize) {
2021-04-18 02:15:15 +03:00
if line >= self.lines.len() {
self.lines.resize(line + 1, Vec::new());
}
}
2021-04-16 04:14:59 +03:00
/// Sets `chr` with `style` for given `line`, `col`.
2021-04-18 02:15:15 +03:00
/// If `line` does not exist in our buffer, adds empty lines up to the given
/// and fills the last line with unstyled whitespace.
pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
self.ensure_lines(line);
2021-04-18 02:15:15 +03:00
if col >= self.lines[line].len() {
self.lines[line].resize(col + 1, StyledChar::SPACE);
}
2021-04-18 02:15:15 +03:00
self.lines[line][col] = StyledChar::new(chr, style);
}
2021-04-16 04:14:59 +03:00
/// Sets `string` with `style` for given `line`, starting from `col`.
2021-04-18 02:15:15 +03:00
/// If `line` does not exist in our buffer, adds empty lines up to the given
/// and fills the last line with unstyled whitespace.
pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
let mut n = col;
for c in string.chars() {
self.putc(line, n, c, style);
n += 1;
}
}
2021-04-16 04:14:59 +03:00
/// For given `line` inserts `string` with `style` before old content of that line,
/// adding lines if needed
pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
self.ensure_lines(line);
let string_len = string.chars().count();
2021-04-16 05:38:32 +03:00
if !self.lines[line].is_empty() {
// Push the old content over to make room for new content
for _ in 0..string_len {
2021-04-18 02:15:15 +03:00
self.lines[line].insert(0, StyledChar::SPACE);
}
}
self.puts(line, 0, string, style);
}
2021-04-16 04:14:59 +03:00
/// For given `line` inserts `string` with `style` after old content of that line,
/// adding lines if needed
pub fn append(&mut self, line: usize, string: &str, style: Style) {
2021-04-16 05:38:32 +03:00
if line >= self.lines.len() {
self.puts(line, 0, string, style);
} else {
2021-04-16 05:38:32 +03:00
let col = self.lines[line].len();
self.puts(line, col, string, style);
}
}
pub fn num_lines(&self) -> usize {
2021-04-16 05:38:32 +03:00
self.lines.len()
}
2021-04-16 04:14:59 +03:00
/// Set `style` for `line`, `col_start..col_end` range if:
/// 1. That line and column range exist in `StyledBuffer`
/// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
pub fn set_style_range(
&mut self,
line: usize,
col_start: usize,
col_end: usize,
style: Style,
overwrite: bool,
) {
for col in col_start..col_end {
self.set_style(line, col, style, overwrite);
}
}
2021-04-16 04:14:59 +03:00
/// Set `style` for `line`, `col` if:
/// 1. That line and column exist in `StyledBuffer`
/// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
2021-04-16 05:38:32 +03:00
if let Some(ref mut line) = self.lines.get_mut(line) {
if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
if overwrite || matches!(s, Style::NoStyle | Style::Quotation) {
*s = style;
}
}
}
}
}