From 742a5ead066bec14047f6ab7ffa3ac8307eea715 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Sun, 24 Mar 2024 22:06:31 -0700 Subject: [PATCH] fix(text): fix panic when rendering out of bounds (#997) Previously it was possible to cause a panic when rendering to an area outside of the buffer bounds. Instead this now correctly renders nothing to the buffer. --- src/text/line.rs | 13 +++++++++++++ src/text/span.rs | 17 +++++++++++++++++ src/text/text.rs | 13 +++++++++++++ 3 files changed, 43 insertions(+) diff --git a/src/text/line.rs b/src/text/line.rs index 663b73db..f8e7ecec 100644 --- a/src/text/line.rs +++ b/src/text/line.rs @@ -587,6 +587,11 @@ mod tests { use super::*; + #[fixture] + fn small_buf() -> Buffer { + Buffer::empty(Rect::new(0, 0, 10, 1)) + } + #[test] fn raw_str() { let line = Line::raw("test content"); @@ -832,6 +837,7 @@ mod tests { const GREEN: Style = Style::new().fg(Color::Green); const ITALIC: Style = Style::new().add_modifier(Modifier::ITALIC); + #[fixture] fn hello_world() -> Line<'static> { Line::from(vec![ Span::styled("Hello ", BLUE), @@ -851,6 +857,13 @@ mod tests { assert_buffer_eq!(buf, expected); } + #[rstest] + fn render_out_of_bounds(hello_world: Line<'static>, mut small_buf: Buffer) { + let out_of_bounds = Rect::new(20, 20, 10, 1); + hello_world.render(out_of_bounds, &mut small_buf); + assert_buffer_eq!(small_buf, Buffer::empty(small_buf.area)); + } + #[test] fn render_only_styles_line_area() { let mut buf = Buffer::empty(Rect::new(0, 0, 20, 1)); diff --git a/src/text/span.rs b/src/text/span.rs index 6db2179a..20fbf012 100644 --- a/src/text/span.rs +++ b/src/text/span.rs @@ -363,6 +363,7 @@ impl Widget for Span<'_> { impl WidgetRef for Span<'_> { fn render_ref(&self, area: Rect, buf: &mut Buffer) { + let area = area.intersection(buf.area); let Rect { x: mut current_x, y, @@ -402,8 +403,15 @@ impl std::fmt::Display for Span<'_> { #[cfg(test)] mod tests { + use rstest::fixture; + use super::*; + #[fixture] + fn small_buf() -> Buffer { + Buffer::empty(Rect::new(0, 0, 10, 1)) + } + #[test] fn default() { let span = Span::default(); @@ -533,6 +541,8 @@ mod tests { } mod widget { + use rstest::rstest; + use super::*; use crate::assert_buffer_eq; @@ -550,6 +560,13 @@ mod tests { assert_buffer_eq!(buf, expected); } + #[rstest] + fn render_out_of_bounds(mut small_buf: Buffer) { + let out_of_bounds = Rect::new(20, 20, 10, 1); + Span::raw("Hello, World!").render(out_of_bounds, &mut small_buf); + assert_eq!(small_buf, Buffer::empty(small_buf.area)); + } + /// When the content of the span is longer than the area passed to render, the content /// should be truncated #[test] diff --git a/src/text/text.rs b/src/text/text.rs index 53aa9937..b20d75f9 100644 --- a/src/text/text.rs +++ b/src/text/text.rs @@ -559,6 +559,7 @@ impl Widget for Text<'_> { impl WidgetRef for Text<'_> { fn render_ref(&self, area: Rect, buf: &mut Buffer) { + let area = area.intersection(buf.area); buf.set_style(area, self.style); for (line, row) in self.iter().zip(area.rows()) { let line_width = line.width() as u16; @@ -601,6 +602,11 @@ mod tests { use super::*; + #[fixture] + fn small_buf() -> Buffer { + Buffer::empty(Rect::new(0, 0, 10, 1)) + } + #[test] fn raw() { let text = Text::raw("The first line\nThe second line"); @@ -865,6 +871,13 @@ mod tests { assert_buffer_eq!(buf, expected_buf); } + #[rstest] + fn render_out_of_bounds(mut small_buf: Buffer) { + let out_of_bounds_area = Rect::new(20, 20, 10, 1); + Text::from("Hello, world!").render(out_of_bounds_area, &mut small_buf); + assert_eq!(small_buf, Buffer::empty(small_buf.area)); + } + #[test] fn render_right_aligned() { let text = Text::from("foo").alignment(Alignment::Right);