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 | 155 |
1 files changed, 17 insertions, 138 deletions
diff --git a/helix-tui/src/buffer.rs b/helix-tui/src/buffer.rs index 4f57e8e5..9b93c405 100644 --- a/helix-tui/src/buffer.rs +++ b/helix-tui/src/buffer.rs @@ -1,4 +1,3 @@ -//! 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; @@ -6,7 +5,7 @@ use unicode_segmentation::UnicodeSegmentation; use helix_view::graphics::{Color, Modifier, Rect, Style, UnderlineStyle}; -/// One cell of the terminal. Contains one stylized grapheme. +/// A buffer cell #[derive(Debug, Clone, PartialEq, Eq)] pub struct Cell { pub symbol: String, @@ -18,33 +17,28 @@ pub struct Cell { } 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; @@ -64,7 +58,6 @@ impl Cell { self } - /// Returns the current style of the cell pub fn style(&self) -> Style { Style::default() .fg(self.fg) @@ -74,7 +67,6 @@ impl Cell { .add_modifier(self.modifier) } - /// Resets the cell to a default blank state pub fn reset(&mut self) { self.symbol.clear(); self.symbol.push(' '); @@ -138,21 +130,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 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>, @@ -220,7 +213,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`). /// @@ -249,7 +242,7 @@ impl Buffer { ((y - self.area.y) as usize) * (self.area.width as usize) + ((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) { @@ -315,69 +308,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. @@ -503,52 +433,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 +454,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 +470,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() { @@ -639,10 +521,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 +537,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 @@ -812,16 +694,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"])); } |