on master help sign in
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.

commit67783ddfd4fc7f06bd6addaa6d65d49759934ace authorPascal Kuthe <pascal.kuthe@semimod.de> dateThu 30 Mar 2023, 17:31 (4 years ago) parente72be52
1 file changed+38−22
helix-tui/src/buffer.rs+38−22
helix-tui/src/buffer.rs+38−22
@@ -434,28 +434,44 @@ impl Buffer {
434434 }
435435
436436 pub fn set_spans_truncated(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) {
437− let mut remaining_width = width;
438− let mut alt_x = x;
439− let (text, styles) =
440− spans
441− .0
442− .iter()
443− .fold((String::new(), vec![]), |(mut s, mut h), span| {
444− s.push_str(span.content.as_ref());
445− let mut styles = span
446− .styled_graphemes(span.style)
447− .map(|grapheme| grapheme.style)
448− .collect();
449− h.append(&mut styles);
450−
451− let w = span.width() as u16;
452− alt_x = alt_x + w;
453− remaining_width = remaining_width.saturating_sub(w);
454−
455− (s, h)
456− });
457− self.set_string_truncated(x, y, &text, width.into(), |idx| styles[idx], true, true);
458− (x, y)
437+ // prevent panic if out of range
438+ if !self.in_bounds(x, y) || width == 0 {
439+ return (x, y);
440+ }
441+
442+ let mut x_offset = x as usize;
443+ let max_offset = min(self.area.right(), width.saturating_add(x));
444+ let mut start_index = self.index_of(x, y);
445+ let mut index = self.index_of(max_offset as u16, y);
446+
447+ let content_width = spans.width();
448+ let truncated = content_width > width as usize;
449+ if truncated {
450+ self.content[start_index].set_symbol("…");
451+ start_index += 1;
452+ } else {
453+ index -= width as usize - content_width;
454+ }
455+ for span in spans.0.iter().rev() {
456+ for s in span.content.graphemes(true).rev() {
457+ let width = s.width();
458+ if width == 0 {
459+ continue;
460+ }
461+ let start = index - width;
462+ if start < start_index {
463+ break;
464+ }
465+ self.content[start].set_symbol(s);
466+ self.content[start].set_style(span.style);
467+ for i in start + 1..index {
468+ self.content[i].reset();
469+ }
470+ index -= width;
471+ x_offset += width;
472+ }
473+ }
474+ (x_offset as u16, y)
459475 }
460476
461477 pub fn set_spans(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) {