diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27bbb20d..93574072 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,8 @@ jobs: uses: actions-rs/cargo@v1 with: command: test + env: + RUST_BACKTRACE: full - name: "Clippy" uses: actions-rs/cargo@v1 with: @@ -75,3 +77,5 @@ jobs: with: command: test args: --no-default-features --features=crossterm --tests --examples + env: + RUST_BACKTRACE: full diff --git a/CHANGELOG.md b/CHANGELOG.md index b4c4a577..23abc80c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## To be released +### Fixes + +* Fix incorrect output when the first diff to draw is on the second cell of the terminal (#347). + ## v0.10.0 - 2020-07-17 ### Breaking changes diff --git a/src/backend/crossterm.rs b/src/backend/crossterm.rs index f9626cb3..c0a13bee 100644 --- a/src/backend/crossterm.rs +++ b/src/backend/crossterm.rs @@ -58,16 +58,13 @@ where let mut fg = Color::Reset; let mut bg = Color::Reset; let mut modifier = Modifier::empty(); - let mut last_y = 0; - let mut last_x = 0; - - map_error(queue!(string, MoveTo(0, 0)))?; + let mut last_pos: Option<(u16, u16)> = None; for (x, y, cell) in content { - if y != last_y || x != last_x + 1 { + // Move the cursor if the previous location was not (x - 1, y) + if !matches!(last_pos, Some(p) if x == p.0 + 1 && y == p.1) { map_error(queue!(string, MoveTo(x, y)))?; } - last_x = x; - last_y = y; + last_pos = Some((x, y)); if cell.modifier != modifier { let diff = ModifierDiff { from: modifier, diff --git a/src/backend/termion.rs b/src/backend/termion.rs index f3048b88..a71229a2 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -1,13 +1,13 @@ -use std::fmt; -use std::io; -use std::io::Write; - use super::Backend; use crate::{ buffer::Cell, layout::Rect, style::{Color, Modifier}, }; +use std::{ + fmt, + io::{self, Write}, +}; pub struct TermionBackend where @@ -82,15 +82,13 @@ where let mut fg = Color::Reset; let mut bg = Color::Reset; let mut modifier = Modifier::empty(); - let mut last_y = 0; - let mut last_x = 0; - write!(string, "{}", termion::cursor::Goto(1, 1)).unwrap(); + let mut last_pos: Option<(u16, u16)> = None; for (x, y, cell) in content { - if y != last_y || x != last_x + 1 { + // Move the cursor if the previous location was not (x - 1, y) + if !matches!(last_pos, Some(p) if x == p.0 + 1 && y == p.1) { write!(string, "{}", termion::cursor::Goto(x + 1, y + 1)).unwrap(); } - last_x = x; - last_y = y; + last_pos = Some((x, y)); if cell.modifier != modifier { write!( string, diff --git a/tests/backend_termion.rs b/tests/backend_termion.rs new file mode 100644 index 00000000..ab2882c0 --- /dev/null +++ b/tests/backend_termion.rs @@ -0,0 +1,63 @@ +#[cfg(feature = "termion")] +#[test] +fn backend_termion_should_only_write_diffs() -> Result<(), Box> { + use std::{fmt::Write, io::Cursor}; + + let mut bytes = Vec::new(); + let mut stdout = Cursor::new(&mut bytes); + { + use tui::{ + backend::TermionBackend, layout::Rect, widgets::Paragraph, Terminal, TerminalOptions, + Viewport, + }; + let backend = TermionBackend::new(&mut stdout); + let area = Rect::new(0, 0, 3, 1); + let mut terminal = Terminal::with_options( + backend, + TerminalOptions { + viewport: Viewport::fixed(area), + }, + )?; + terminal.draw(|f| { + f.render_widget(Paragraph::new("a"), area); + })?; + terminal.draw(|f| { + f.render_widget(Paragraph::new("ab"), area); + })?; + terminal.draw(|f| { + f.render_widget(Paragraph::new("abc"), area); + })?; + } + + let expected = { + use termion::{color, cursor, style}; + let mut s = String::new(); + // First draw + write!(s, "{}", cursor::Goto(1, 1))?; + s.push_str("a"); + write!(s, "{}", color::Fg(color::Reset))?; + write!(s, "{}", color::Bg(color::Reset))?; + write!(s, "{}", style::Reset)?; + write!(s, "{}", cursor::Hide)?; + // Second draw + write!(s, "{}", cursor::Goto(2, 1))?; + s.push_str("b"); + write!(s, "{}", color::Fg(color::Reset))?; + write!(s, "{}", color::Bg(color::Reset))?; + write!(s, "{}", style::Reset)?; + write!(s, "{}", cursor::Hide)?; + // Third draw + write!(s, "{}", cursor::Goto(3, 1))?; + s.push_str("c"); + write!(s, "{}", color::Fg(color::Reset))?; + write!(s, "{}", color::Bg(color::Reset))?; + write!(s, "{}", style::Reset)?; + write!(s, "{}", cursor::Hide)?; + // Terminal drop + write!(s, "{}", cursor::Show)?; + s + }; + assert_eq!(std::str::from_utf8(&bytes)?, expected); + + Ok(()) +}