From ecb482f2978bfbfe396fa29968a4b72e01432c86 Mon Sep 17 00:00:00 2001 From: Florian Dehau Date: Sun, 2 Aug 2020 18:30:52 +0200 Subject: [PATCH] fix(backend): move the cursor when first diff is on second cell Both termion and crossterm backends were not moving the cursor if the first diff to draw was on the second cell. The condition triggering the cursor move has been updated to fix this. In addition, two tests have been added to avoid future regressions. --- .github/workflows/ci.yml | 4 +++ CHANGELOG.md | 4 +++ src/backend/crossterm.rs | 11 +++---- src/backend/termion.rs | 18 +++++------- tests/backend_termion.rs | 63 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 tests/backend_termion.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27bbb20..9357407 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 b4c4a57..23abc80 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 f9626cb..c0a13be 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 f3048b8..a71229a 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 0000000..ab2882c --- /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(()) +}