just playing with tangled
1use std::path::Path;
2use std::sync::Mutex;
3use std::time::Duration;
4use std::time::Instant;
5
6use crossterm::terminal::Clear;
7use crossterm::terminal::ClearType;
8use jj_lib::repo_path::RepoPath;
9
10use crate::text_util;
11use crate::ui::OutputGuard;
12use crate::ui::ProgressOutput;
13use crate::ui::Ui;
14
15pub const UPDATE_HZ: u32 = 30;
16pub const INITIAL_DELAY: Duration = Duration::from_millis(250);
17
18pub fn snapshot_progress(ui: &Ui) -> Option<impl Fn(&RepoPath) + use<>> {
19 struct State {
20 guard: Option<OutputGuard>,
21 output: ProgressOutput<std::io::Stderr>,
22 next_display_time: Instant,
23 }
24
25 let output = ui.progress_output()?;
26
27 // Don't clutter the output during fast operations.
28 let next_display_time = Instant::now() + INITIAL_DELAY;
29 let state = Mutex::new(State {
30 guard: None,
31 output,
32 next_display_time,
33 });
34
35 Some(move |path: &RepoPath| {
36 let mut state = state.lock().unwrap();
37 let now = Instant::now();
38 if now < state.next_display_time {
39 // Future work: Display current path after exactly, say, 250ms has elapsed, to
40 // better handle large single files
41 return;
42 }
43 state.next_display_time = now + Duration::from_secs(1) / UPDATE_HZ;
44
45 if state.guard.is_none() {
46 state.guard = Some(
47 state
48 .output
49 .output_guard(format!("\r{}", Clear(ClearType::CurrentLine))),
50 );
51 }
52
53 let line_width = state.output.term_width().map(usize::from).unwrap_or(80);
54 let max_path_width = line_width.saturating_sub(13); // Account for "Snapshotting "
55 let fs_path = path.to_fs_path_unchecked(Path::new(""));
56 let (display_path, _) =
57 text_util::elide_start(fs_path.to_str().unwrap(), "...", max_path_width);
58
59 _ = write!(
60 state.output,
61 "\r{}Snapshotting {display_path}",
62 Clear(ClearType::CurrentLine),
63 );
64 _ = state.output.flush();
65 })
66}