use std::io;
use std::path::Path;
use std::sync::Mutex;
use std::time::Duration;
use std::time::Instant;
use crossterm::terminal::Clear;
use crossterm::terminal::ClearType;
use jj_lib::repo_path::RepoPath;
use crate::text_util;
use crate::ui::OutputGuard;
use crate::ui::ProgressOutput;
use crate::ui::Ui;
pub const UPDATE_HZ: u32 = 30;
pub const INITIAL_DELAY: Duration = Duration::from_millis(250);
pub struct ProgressWriter<'a> {
prefix: &'a str,
guard: Option<OutputGuard>,
output: ProgressOutput<io::Stderr>,
next_display_time: Instant,
}
impl<'a> ProgressWriter<'a> {
pub fn new(ui: &Ui, prefix: &'a str) -> Option<Self> {
let output = ui.progress_output()?;
let next_display_time = Instant::now() + INITIAL_DELAY;
Some(Self {
prefix,
guard: None,
output,
next_display_time,
})
}
pub fn display(&mut self, text: &str) -> io::Result<()> {
let now = Instant::now();
if now < self.next_display_time {
return Ok(());
}
self.next_display_time = now + Duration::from_secs(1) / UPDATE_HZ;
if self.guard.is_none() {
self.guard = Some(
self.output
.output_guard(format!("\r{}", Clear(ClearType::CurrentLine))),
);
}
let line_width = self.output.term_width().map(usize::from).unwrap_or(80);
let max_path_width = self.prefix.len() + 1; let (display_text, _) =
text_util::elide_start(text, "...", line_width.saturating_sub(max_path_width));
write!(
self.output,
"\r{}{} {display_text}",
Clear(ClearType::CurrentLine),
self.prefix
)?;
self.output.flush()
}
}
pub fn snapshot_progress(ui: &Ui) -> Option<impl Fn(&RepoPath) + use<>> {
let writer = Mutex::new(ProgressWriter::new(ui, "Snapshotting")?);
Some(move |path: &RepoPath| {
if let Ok(mut progress) = writer.try_lock() {
progress
.display(path.to_fs_path_unchecked(Path::new("")).to_str().unwrap())
.ok();
}
})
}