Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-tui/src/buffer.rs')
| -rw-r--r-- | helix-tui/src/buffer.rs | 209 |
1 files changed, 35 insertions, 174 deletions
diff --git a/helix-tui/src/buffer.rs b/helix-tui/src/buffer.rs index 4f57e8e5..5dac08f8 100644 --- a/helix-tui/src/buffer.rs +++ b/helix-tui/src/buffer.rs @@ -1,50 +1,42 @@ -//! Contents of a terminal screen. A [Buffer] is made up of [Cell]s. use crate::text::{Span, Spans}; use helix_core::unicode::width::UnicodeWidthStr; use std::cmp::min; use unicode_segmentation::UnicodeSegmentation; -use helix_view::graphics::{Color, Modifier, Rect, Style, UnderlineStyle}; +use helix_graphics::{Color, Modifier, Rect, Style}; -/// One cell of the terminal. Contains one stylized grapheme. -#[derive(Debug, Clone, PartialEq, Eq)] +/// A buffer cell +#[derive(Debug, Clone, PartialEq)] pub struct Cell { pub symbol: String, pub fg: Color, pub bg: Color, - pub underline_color: Color, - pub underline_style: UnderlineStyle, pub modifier: Modifier, } impl Cell { - /// Set the cell's grapheme pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell { self.symbol.clear(); self.symbol.push_str(symbol); self } - /// Set the cell's grapheme to a [char] pub fn set_char(&mut self, ch: char) -> &mut Cell { self.symbol.clear(); self.symbol.push(ch); self } - /// Set the foreground [Color] pub fn set_fg(&mut self, color: Color) -> &mut Cell { self.fg = color; self } - /// Set the background [Color] pub fn set_bg(&mut self, color: Color) -> &mut Cell { self.bg = color; self } - /// Set the [Style] of the cell pub fn set_style(&mut self, style: Style) -> &mut Cell { if let Some(c) = style.fg { self.fg = c; @@ -52,36 +44,23 @@ impl Cell { if let Some(c) = style.bg { self.bg = c; } - if let Some(c) = style.underline_color { - self.underline_color = c; - } - if let Some(style) = style.underline_style { - self.underline_style = style; - } - self.modifier.insert(style.add_modifier); self.modifier.remove(style.sub_modifier); self } - /// Returns the current style of the cell pub fn style(&self) -> Style { Style::default() .fg(self.fg) .bg(self.bg) - .underline_color(self.underline_color) - .underline_style(self.underline_style) .add_modifier(self.modifier) } - /// Resets the cell to a default blank state pub fn reset(&mut self) { self.symbol.clear(); self.symbol.push(' '); self.fg = Color::Reset; self.bg = Color::Reset; - self.underline_color = Color::Reset; - self.underline_style = UnderlineStyle::Reset; self.modifier = Modifier::empty(); } } @@ -92,8 +71,6 @@ impl Default for Cell { symbol: " ".into(), fg: Color::Reset, bg: Color::Reset, - underline_color: Color::Reset, - underline_style: UnderlineStyle::Reset, modifier: Modifier::empty(), } } @@ -110,7 +87,7 @@ impl Default for Cell { /// /// ``` /// use helix_tui::buffer::{Buffer, Cell}; -/// use helix_view::graphics::{Rect, Color, UnderlineStyle, Style, Modifier}; +/// use helix_graphics::{Rect, Color, Style, Modifier}; /// /// let mut buf = Buffer::empty(Rect{x: 0, y: 0, width: 10, height: 5}); /// buf[(0, 2)].set_symbol("x"); @@ -120,14 +97,12 @@ impl Default for Cell { /// symbol: String::from("r"), /// fg: Color::Red, /// bg: Color::White, -/// underline_color: Color::Reset, -/// underline_style: UnderlineStyle::Reset, -/// modifier: Modifier::empty(), +/// modifier: Modifier::empty() /// }); /// buf[(5, 0)].set_char('x'); /// assert_eq!(buf[(5, 0)].symbol, "x"); /// ``` -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Buffer { /// The area represented by this buffer pub area: Rect, @@ -138,21 +113,22 @@ pub struct Buffer { impl Buffer { /// Returns a Buffer with all cells set to the default one - #[must_use] pub fn empty(area: Rect) -> Buffer { - Buffer::filled(area, &Cell::default()) + let cell: Cell = Default::default(); + Buffer::filled(area, &cell) } /// Returns a Buffer with all cells initialized with the attributes of the given Cell - #[must_use] pub fn filled(area: Rect, cell: &Cell) -> Buffer { - let size = area.area(); - let content = vec![cell.clone(); size]; + let size = area.area() as usize; + let mut content = Vec::with_capacity(size); + for _ in 0..size { + content.push(cell.clone()); + } Buffer { area, content } } /// Returns a Buffer containing the given lines - #[must_use] pub fn with_lines<S>(lines: Vec<S>) -> Buffer where S: AsRef<str>, @@ -203,7 +179,7 @@ impl Buffer { /// /// ``` /// # use helix_tui::buffer::Buffer; - /// # use helix_view::graphics::Rect; + /// # use helix_graphics::Rect; /// let rect = Rect::new(200, 100, 10, 10); /// let buffer = Buffer::empty(rect); /// // Global coordinates inside the Buffer's area @@ -220,7 +196,7 @@ impl Buffer { && y < self.area.bottom() } - /// Returns the index in the `Vec<Cell>` for the given global (x, y) coordinates. + /// Returns the index in the Vec<Cell> for the given global (x, y) coordinates. /// /// Global coordinates are offset by the Buffer's area offset (`x`/`y`). /// @@ -228,7 +204,7 @@ impl Buffer { /// /// ``` /// # use helix_tui::buffer::Buffer; - /// # use helix_view::graphics::Rect; + /// # use helix_graphics::Rect; /// let rect = Rect::new(200, 100, 10, 10); /// let buffer = Buffer::empty(rect); /// // Global coordinates to the top corner of this Buffer's area @@ -246,10 +222,10 @@ impl Buffer { y, self.area ); - ((y - self.area.y) as usize) * (self.area.width as usize) + ((x - self.area.x) as usize) + ((y - self.area.y) * self.area.width + (x - self.area.x)) as usize } - /// Returns the index in the `Vec<Cell>` for the given global (x, y) coordinates, + /// Returns the index in the Vec<Cell> for the given global (x, y) coordinates, /// or `None` if the coordinates are outside the buffer's area. fn index_of_opt(&self, x: u16, y: u16) -> Option<usize> { if self.in_bounds(x, y) { @@ -267,7 +243,7 @@ impl Buffer { /// /// ``` /// # use helix_tui::buffer::Buffer; - /// # use helix_view::graphics::Rect; + /// # use helix_graphics::Rect; /// let rect = Rect::new(200, 100, 10, 10); /// let buffer = Buffer::empty(rect); /// assert_eq!(buffer.pos_of(0), (200, 100)); @@ -285,8 +261,8 @@ impl Buffer { self.content.len() ); ( - (self.area.x as usize + (i % self.area.width as usize)) as u16, - (self.area.y as usize + (i / self.area.width as usize)) as u16, + self.area.x + i as u16 % self.area.width, + self.area.y + i as u16 / self.area.width, ) } @@ -315,69 +291,6 @@ impl Buffer { } /// Print at most the first `width` characters of a string if enough space is available - /// until the end of the line. - /// If `ellipsis` is true appends a `…` at the end of truncated lines. - /// If `truncate_start` is `true`, adds a `…` at the beginning of truncated lines. - #[allow(clippy::too_many_arguments)] - pub fn set_string_anchored( - &mut self, - x: u16, - y: u16, - truncate_start: bool, - truncate_end: bool, - string: &str, - width: usize, - style: impl Fn(usize) -> Style, // Map a grapheme's string offset to a style - ) -> (u16, u16) { - // prevent panic if out of range - if !self.in_bounds(x, y) || width == 0 { - return (x, y); - } - - let mut index = self.index_of(x, y); - let mut rendered_width = 0; - let mut graphemes = string.grapheme_indices(true); - - if truncate_start { - for _ in 0..graphemes.next().map(|(_, g)| g.width()).unwrap_or_default() { - self.content[index].set_symbol("…"); - index += 1; - rendered_width += 1; - } - } - - for (byte_offset, s) in graphemes { - let grapheme_width = s.width(); - if truncate_end && rendered_width + grapheme_width >= width { - break; - } - if grapheme_width == 0 { - continue; - } - - self.content[index].set_symbol(s); - self.content[index].set_style(style(byte_offset)); - - // Reset following cells if multi-width (they would be hidden by the grapheme): - for i in index + 1..index + grapheme_width { - self.content[i].reset(); - } - - index += grapheme_width; - rendered_width += grapheme_width; - } - - if truncate_end { - for _ in 0..width.saturating_sub(rendered_width) { - self.content[index].set_symbol("…"); - index += 1; - } - } - - (x, y) - } - - /// Print at most the first `width` characters of a string if enough space is available /// until the end of the line. If `ellipsis` is true appends a `…` at the end of /// truncated lines. If `truncate_start` is `true`, truncate the beginning of the string /// instead of the end. @@ -430,14 +343,14 @@ impl Buffer { let mut start_index = self.index_of(x, y); let mut index = self.index_of(max_offset as u16, y); - let content_width = string.width(); - let truncated = content_width > width; + let total_width = string.width(); + let truncated = total_width > width; if ellipsis && truncated { self.content[start_index].set_symbol("…"); start_index += 1; } if !truncated { - index -= width - content_width; + index -= width - total_width; } for (byte_offset, s) in graphemes.rev() { let width = s.width(); @@ -454,7 +367,6 @@ impl Buffer { self.content[i].reset(); } index -= width; - x_offset += width; } } (x_offset as u16, y) @@ -503,52 +415,7 @@ impl Buffer { (x_offset as u16, y) } - /// Print at most the first `width` characters of a [Spans] if enough space is available - /// until the end of the line. Appends a `…` at the end of truncated lines. - pub fn set_spans_truncated(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) { - // prevent panic if out of range - if !self.in_bounds(x, y) || width == 0 { - return (x, y); - } - - let mut x_offset = x as usize; - let max_offset = min(self.area.right(), width.saturating_add(x)); - let mut start_index = self.index_of(x, y); - let mut index = self.index_of(max_offset, y); - - let content_width = spans.width(); - let truncated = content_width > width as usize; - if truncated { - self.content[start_index].set_symbol("…"); - start_index += 1; - } else { - index -= width as usize - content_width; - } - for span in spans.0.iter().rev() { - for s in span.content.graphemes(true).rev() { - let width = s.width(); - if width == 0 { - continue; - } - let start = index - width; - if start < start_index { - break; - } - self.content[start].set_symbol(s); - self.content[start].set_style(span.style); - for i in start + 1..index { - self.content[i].reset(); - } - index -= width; - x_offset += width; - } - } - (x_offset as u16, y) - } - - /// Print at most the first `width` characters of a [Spans] if enough space is available - /// until the end of the line - pub fn set_spans(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) { + pub fn set_spans<'a>(&mut self, x: u16, y: u16, spans: &Spans<'a>, width: u16) -> (u16, u16) { let mut remaining_width = width; let mut x = x; for span in &spans.0 { @@ -569,9 +436,7 @@ impl Buffer { (x, y) } - /// Print at most the first `width` characters of a [Span] if enough space is available - /// until the end of the line - pub fn set_span(&mut self, x: u16, y: u16, span: &Span, width: u16) -> (u16, u16) { + pub fn set_span<'a>(&mut self, x: u16, y: u16, span: &Span<'a>, width: u16) -> (u16, u16) { self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style) } @@ -587,7 +452,6 @@ impl Buffer { } } - /// Set all cells in the [area](Rect) to the given [Style] pub fn set_style(&mut self, area: Rect, style: Style) { for y in area.top()..area.bottom() { for x in area.left()..area.right() { @@ -599,7 +463,7 @@ impl Buffer { /// Resize the buffer so that the mapped area matches the given area and that the buffer /// length is equal to area.width * area.height pub fn resize(&mut self, area: Rect) { - let length = area.area(); + let length = area.area() as usize; if self.content.len() > length { self.content.truncate(length); } else { @@ -639,10 +503,10 @@ impl Buffer { pub fn merge(&mut self, other: &Buffer) { let area = self.area.union(other.area); let cell: Cell = Default::default(); - self.content.resize(area.area(), cell.clone()); + self.content.resize(area.area() as usize, cell.clone()); // Move original content to the appropriate space - let size = self.area.area(); + let size = self.area.area() as usize; for i in (0..size).rev() { let (x, y) = self.pos_of(i); // New index in content @@ -655,7 +519,7 @@ impl Buffer { // Push content of the other buffer into this one (may erase previous // data) - let size = other.area.area(); + let size = other.area.area() as usize; for i in 0..size { let (x, y) = other.pos_of(i); // New index in content @@ -706,8 +570,8 @@ impl Buffer { let mut to_skip: usize = 0; for (i, (current, previous)) in next_buffer.iter().zip(previous_buffer.iter()).enumerate() { if (current != previous || invalidated > 0) && to_skip == 0 { - let x = (i % width as usize) as u16; - let y = (i / width as usize) as u16; + let x = i as u16 % width; + let y = i as u16 / width; updates.push((x, y, &next_buffer[i])); } @@ -812,16 +676,13 @@ mod tests { let area = Rect::new(0, 0, 1, 1); let mut buffer = Buffer::empty(area); - // U+200B is the zero-width space codepoint - assert_eq!("\u{200B}".width(), 0); - // Leading grapheme with zero width - let s = "\u{200B}a"; + let s = "\u{1}a"; buffer.set_stringn(0, 0, s, 1, Style::default()); assert_eq!(buffer, Buffer::with_lines(vec!["a"])); - // Trailing grapheme with zero width - let s = "a\u{200B}"; + // Trailing grapheme with zero with + let s = "a\u{1}"; buffer.set_stringn(0, 0, s, 1, Style::default()); assert_eq!(buffer, Buffer::with_lines(vec!["a"])); } |