1 // Code for creating styled buffers
3 use crate::snippet::{Style, StyledString};
6 pub struct StyledBuffer {
7 lines: Vec<Vec<StyledChar>>,
10 #[derive(Debug, Clone)]
17 const SPACE: Self = StyledChar::new(' ', Style::NoStyle);
19 const fn new(chr: char, style: Style) -> Self {
20 StyledChar { chr, style }
25 pub fn new() -> StyledBuffer {
26 StyledBuffer { lines: vec![] }
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')));
34 let mut output: Vec<Vec<StyledString>> = vec![];
35 let mut styled_vec: Vec<StyledString> = vec![];
37 for styled_line in &self.lines {
38 let mut current_style = Style::NoStyle;
39 let mut current_text = String::new();
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 });
46 current_style = sc.style;
47 current_text = String::new();
49 current_text.push(sc.chr);
51 if !current_text.is_empty() {
52 styled_vec.push(StyledString { text: current_text, style: current_style });
55 // We're done with the row, push and keep going
56 output.push(styled_vec);
64 fn ensure_lines(&mut self, line: usize) {
65 if line >= self.lines.len() {
66 self.lines.resize(line + 1, Vec::new());
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);
78 self.lines[line][col] = StyledChar::new(chr, style);
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) {
86 for c in string.chars() {
87 self.putc(line, n, c, style);
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();
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);
105 self.puts(line, 0, string, style);
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);
114 let col = self.lines[line].len();
115 self.puts(line, col, string, style);
119 pub fn num_lines(&self) -> usize {
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(
134 for col in col_start..col_end {
135 self.set_style(line, col, style, overwrite);
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 {