From 67783ddfd4fc7f06bd6addaa6d65d49759934ace Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 30 Mar 2023 17:31:54 +0200 Subject: [PATCH] Performant and correct set_spans_truncated the previous implementation used set_string_truncated. This is not only awkward with this kind of "streaming" string (and therefore lead to an inefficient and incorrect initial implementation) but that function also truncates strings of width 1 when there is only a single char available. The implementation here is performant, correct and also handles the single width case correctly. --- helix-tui/src/buffer.rs | 60 ++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/helix-tui/src/buffer.rs b/helix-tui/src/buffer.rs index 2c212b125..8e0b0adf9 100644 --- a/helix-tui/src/buffer.rs +++ b/helix-tui/src/buffer.rs @@ -434,28 +434,44 @@ impl Buffer { } pub fn set_spans_truncated(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) { - let mut remaining_width = width; - let mut alt_x = x; - let (text, styles) = - spans - .0 - .iter() - .fold((String::new(), vec![]), |(mut s, mut h), span| { - s.push_str(span.content.as_ref()); - let mut styles = span - .styled_graphemes(span.style) - .map(|grapheme| grapheme.style) - .collect(); - h.append(&mut styles); - - let w = span.width() as u16; - alt_x = alt_x + w; - remaining_width = remaining_width.saturating_sub(w); - - (s, h) - }); - self.set_string_truncated(x, y, &text, width.into(), |idx| styles[idx], true, true); - (x, y) + // 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 as u16, 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) } pub fn set_spans(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) {