// Code for creating styled buffers use crate::snippet::{Style, StyledString}; #[derive(Debug)] pub struct StyledBuffer { text: Vec>, styles: Vec>, } impl StyledBuffer { pub fn new() -> StyledBuffer { StyledBuffer { text: vec![], styles: vec![] } } fn replace_tabs(&mut self) { for (line_pos, line) in self.text.iter_mut().enumerate() { let mut tab_pos = vec![]; for (pos, c) in line.iter().enumerate() { if *c == '\t' { tab_pos.push(pos); } } // start with the tabs at the end of the line to replace them with 4 space chars for pos in tab_pos.iter().rev() { assert_eq!(line.remove(*pos), '\t'); // fix the position of the style to match up after replacing the tabs let s = self.styles[line_pos].remove(*pos); for _ in 0..4 { line.insert(*pos, ' '); self.styles[line_pos].insert(*pos, s); } } } } pub fn render(&mut self) -> Vec> { let mut output: Vec> = vec![]; let mut styled_vec: Vec = vec![]; // before we render, replace tabs with spaces self.replace_tabs(); for (row, row_style) in self.text.iter().zip(&self.styles) { let mut current_style = Style::NoStyle; let mut current_text = String::new(); for (&c, &s) in row.iter().zip(row_style) { if s != current_style { if !current_text.is_empty() { styled_vec.push(StyledString { text: current_text, style: current_style }); } current_style = s; current_text = String::new(); } current_text.push(c); } 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) { while line >= self.text.len() { self.text.push(vec![]); self.styles.push(vec![]); } } pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) { self.ensure_lines(line); if col < self.text[line].len() { self.text[line][col] = chr; self.styles[line][col] = style; } else { let mut i = self.text[line].len(); while i < col { self.text[line].push(' '); self.styles[line].push(Style::NoStyle); i += 1; } self.text[line].push(chr); self.styles[line].push(style); } } 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; } } pub fn prepend(&mut self, line: usize, string: &str, style: Style) { self.ensure_lines(line); let string_len = string.chars().count(); // Push the old content over to make room for new content for _ in 0..string_len { self.styles[line].insert(0, Style::NoStyle); self.text[line].insert(0, ' '); } self.puts(line, 0, string, style); } pub fn append(&mut self, line: usize, string: &str, style: Style) { if line >= self.text.len() { self.puts(line, 0, string, style); } else { let col = self.text[line].len(); self.puts(line, col, string, style); } } pub fn num_lines(&self) -> usize { self.text.len() } 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); } } pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) { if let Some(ref mut line) = self.styles.get_mut(line) { if let Some(s) = line.get_mut(col) { if *s == Style::NoStyle || *s == Style::Quotation || overwrite { *s = style; } } } } }