Skip to content

Commit

Permalink
synchronized output
Browse files Browse the repository at this point in the history
  • Loading branch information
Funami580 committed Jan 6, 2024
1 parent 9b533cc commit ceff36f
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ console = { version = "0.15", default-features = false, features = ["ansi-parsin
futures-core = { version = "0.3", default-features = false, optional = true }
number_prefix = "0.4"
portable-atomic = "1.0.0"
once_cell = "1.19.0"
rayon = { version = "1.1", optional = true }
tokio = { version = "1", optional = true, features = ["io-util"] }
unicode-segmentation = { version = "1", optional = true }
Expand Down
8 changes: 7 additions & 1 deletion src/draw_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use console::Term;
use instant::Instant;

use crate::multi::{MultiProgressAlignment, MultiState};
use crate::TermLike;
use crate::{term_like, TermLike};

/// Target for draw operations
///
Expand Down Expand Up @@ -470,6 +470,9 @@ impl DrawState {
return Ok(());
}

// Begin synchronized update
let sync_guard = term_like::SyncGuard::begin_sync(term)?;

if !self.lines.is_empty() && self.move_cursor {
term.move_cursor_up(*last_line_count)?;
} else {
Expand Down Expand Up @@ -542,6 +545,9 @@ impl DrawState {
}
term.write_str(&" ".repeat(last_line_filler))?;

// End synchronized update
sync_guard.finish_sync()?;

term.flush()?;
*last_line_count = real_len - orphan_visual_line_count + shift;
Ok(())
Expand Down
31 changes: 31 additions & 0 deletions src/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ impl TermLike for InMemoryTerm {
state.history.push(Move::Flush);
state.parser.flush()
}

fn supports_ansi_codes(&self) -> bool {
true
}
}

struct InMemoryTermState {
Expand Down Expand Up @@ -234,6 +238,8 @@ enum Move {

#[cfg(test)]
mod test {
use crate::term_like;

use super::*;

fn cursor_pos(in_mem: &InMemoryTerm) -> (u16, u16) {
Expand Down Expand Up @@ -396,4 +402,29 @@ NewLine
in_mem.move_cursor_right(0).unwrap();
assert_eq!(cursor_pos(&in_mem), (1, 1));
}

#[test]
fn sync_update() {
let in_mem = InMemoryTerm::new(10, 80);
assert_eq!(cursor_pos(&in_mem), (0, 0));

let sync_guard = term_like::SyncGuard::begin_sync(&in_mem).unwrap();
in_mem.write_line("LINE ONE").unwrap();
assert_eq!(cursor_pos(&in_mem), (1, 0));
assert_eq!(
in_mem.moves_since_last_check(),
r#"Str("\u{1b}[?2026h")
Str("LINE ONE")
NewLine
"#
);

sync_guard.finish_sync().unwrap();
assert_eq!(cursor_pos(&in_mem), (1, 0));
assert_eq!(
in_mem.moves_since_last_check(),
r#"Str("\u{1b}[?2026l")
"#
);
}
}
45 changes: 45 additions & 0 deletions src/term_like.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::cell::Cell;
use std::fmt::Debug;
use std::io;

use console::Term;
use once_cell::sync::OnceCell;

/// A trait for minimal terminal-like behavior.
///
Expand Down Expand Up @@ -34,6 +36,9 @@ pub trait TermLike: Debug + Send + Sync {
fn clear_line(&self) -> io::Result<()>;

fn flush(&self) -> io::Result<()>;

// Whether ANSI escape sequences are supported
fn supports_ansi_codes(&self) -> bool;
}

impl TermLike for Term {
Expand Down Expand Up @@ -76,4 +81,44 @@ impl TermLike for Term {
fn flush(&self) -> io::Result<()> {
self.flush()
}

fn supports_ansi_codes(&self) -> bool {
static SUPPORTS_ANSI: OnceCell<bool> = OnceCell::new();
*SUPPORTS_ANSI.get_or_init(|| self.features().colors_supported())
}
}

pub(crate) struct SyncGuard<'a, T: TermLike + ?Sized> {
term_like: Cell<Option<&'a T>>,
}

impl<'a, T: TermLike + ?Sized> SyncGuard<'a, T> {
pub(crate) fn begin_sync(term_like: &'a T) -> io::Result<Self> {
if term_like.supports_ansi_codes() {
term_like.write_str("\x1b[?2026h")?;
}

Ok(Self {
term_like: Cell::new(Some(term_like)),
})
}

pub(crate) fn finish_sync(self) -> io::Result<()> {
self.finish_sync_inner()
}

fn finish_sync_inner(&self) -> io::Result<()> {
if let Some(term_like) = self.term_like.take() {
if term_like.supports_ansi_codes() {
term_like.write_str("\x1b[?2026l")?;
}
}
Ok(())
}
}

impl<T: TermLike + ?Sized> Drop for SyncGuard<'_, T> {
fn drop(&mut self) {
let _ = self.finish_sync_inner();
}
}

0 comments on commit ceff36f

Please sign in to comment.