Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(line): line truncation respects alignment #987

Merged
merged 9 commits into from
Mar 31, 2024
101 changes: 94 additions & 7 deletions src/text/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,39 @@
self.spans.iter_mut()
}

/// Returns a line that's truncated corresponding to it's alignment and result width
#[must_use = "method returns the modified value"]
pub fn truncated(&'a self, result_width: u16) -> Self {
TadoTheMiner marked this conversation as resolved.
Show resolved Hide resolved
let mut truncated_line = Line::default();
let width = self.width() as u16;
let mut offset = match self.alignment {
Some(Alignment::Center) => (width.saturating_sub(result_width)) / 2,
Some(Alignment::Right) => width.saturating_sub(result_width),
_ => 0,
};
let mut x = 0;
for span in &self.spans {
let span_width = span.width() as u16;
if offset >= span_width {
offset -= span_width;
continue;
TadoTheMiner marked this conversation as resolved.
Show resolved Hide resolved
}
let mut new_span = span.clone();
let new_span_width = span_width - offset;
if x + new_span_width > result_width {
let span_end = (result_width - x + offset) as usize;
new_span.content = Cow::from(&span.content[offset as usize..span_end]);
truncated_line.spans.push(new_span);
break;
}

new_span.content = Cow::from(&span.content[offset as usize..]);
truncated_line.spans.push(new_span);
x += new_span_width;
offset = 0;
}
truncated_line
}
/// Adds a span to the line.
///
/// `span` can be any type that is convertible into a `Span`. For example, you can pass a
Expand Down Expand Up @@ -554,17 +587,23 @@
let area = area.intersection(buf.area);
buf.set_style(area, self.style);
let width = self.width() as u16;
let offset = match self.alignment {
Some(Alignment::Center) => (area.width.saturating_sub(width)) / 2,
Some(Alignment::Right) => area.width.saturating_sub(width),
Some(Alignment::Left) | None => 0,
let mut x = area.left();
let line = if width > area.width {
self.truncated(area.width)
} else {
let offset = match self.alignment {
Some(Alignment::Center) => (area.width.saturating_sub(width)) / 2,
Some(Alignment::Right) => area.width.saturating_sub(width),
Some(Alignment::Left) | None => 0,
};
x = x.saturating_add(offset);
self.to_owned()
};
let mut x = area.left().saturating_add(offset);
for span in &self.spans {
for span in &line.spans {
let span_width = span.width() as u16;
let span_area = Rect {
x,
width: span_width.min(area.right() - x),
width: span_width.min(area.right().saturating_sub(x)),
..area
};
span.render(span_area, buf);
Expand Down Expand Up @@ -604,6 +643,7 @@
use rstest::{fixture, rstest};

use super::*;
use crate::assert_buffer_eq;

#[fixture]
fn small_buf() -> Buffer {
Expand Down Expand Up @@ -847,6 +887,53 @@

assert_eq!(format!("{line_from_styled_span}"), "Hello, world!");
}
#[test]
fn render_truncates_left() {
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
Line::from("Hello world")
.left_aligned()
.render(buf.area, &mut buf);
let expected = Buffer::with_lines(vec!["Hello"]);
assert_buffer_eq!(buf, expected);

Check warning on line 897 in src/text/line.rs

View check run for this annotation

Codecov / codecov/patch

src/text/line.rs#L897

Added line #L897 was not covered by tests
}

#[test]
fn render_truncates_right() {
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
Line::from("Hello world")
.right_aligned()
.render(buf.area, &mut buf);
let expected = Buffer::with_lines(vec!["world"]);
assert_buffer_eq!(buf, expected);

Check warning on line 907 in src/text/line.rs

View check run for this annotation

Codecov / codecov/patch

src/text/line.rs#L907

Added line #L907 was not covered by tests
}

#[test]
fn render_truncates_center() {
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
Line::from("Hello world")
.centered()
.render(buf.area, &mut buf);
let expected = Buffer::with_lines(vec!["lo wo"]);
assert_buffer_eq!(buf, expected);

Check warning on line 917 in src/text/line.rs

View check run for this annotation

Codecov / codecov/patch

src/text/line.rs#L917

Added line #L917 was not covered by tests
}

#[test]
fn truncate_line_with_multiple_spans() {
let line = Line::default().spans(vec!["foo", "bar"]);
assert_eq!(
line.right_aligned().truncated(4).to_string(),
String::from("obar")
);
}

#[test]
fn truncation_ignores_useless_spans() {
let line = Line::default().spans(vec!["foo", "bar"]);
assert_eq!(
line.right_aligned().truncated(3),
Line::default().spans(vec!["bar"])
);
}

#[test]
fn left_aligned() {
Expand Down