forked from mirrors/jj
ui: create separate io wrapper for progress output
I'm going to redirect progress message to stderr, but the current Ui API makes it less clear whether the underlying streams of .write*()/.flush()/ .output_guard() are related or not.
This commit is contained in:
parent
dc7f7fb23d
commit
bea1ef73e6
3 changed files with 55 additions and 37 deletions
|
@ -510,11 +510,10 @@ fn do_git_clone(
|
|||
fn with_remote_callbacks<T>(ui: &mut Ui, f: impl FnOnce(git::RemoteCallbacks<'_>) -> T) -> T {
|
||||
let mut ui = Mutex::new(ui);
|
||||
let mut callback = None;
|
||||
if ui.get_mut().unwrap().use_progress_indicator() {
|
||||
if let Some(mut output) = ui.get_mut().unwrap().progress_output() {
|
||||
let mut progress = Progress::new(Instant::now());
|
||||
let ui = &ui;
|
||||
callback = Some(move |x: &git::Progress| {
|
||||
_ = progress.update(Instant::now(), x, *ui.lock().unwrap());
|
||||
_ = progress.update(Instant::now(), x, &mut output);
|
||||
});
|
||||
}
|
||||
let mut callbacks = git::RemoteCallbacks::default();
|
||||
|
|
|
@ -8,7 +8,7 @@ use jujutsu_lib::git;
|
|||
use jujutsu_lib::repo_path::RepoPath;
|
||||
|
||||
use crate::cleanup_guard::CleanupGuard;
|
||||
use crate::ui::{OutputGuard, Ui};
|
||||
use crate::ui::{OutputGuard, ProgressOutput, Ui};
|
||||
|
||||
pub struct Progress {
|
||||
next_print: Instant,
|
||||
|
@ -31,13 +31,13 @@ impl Progress {
|
|||
&mut self,
|
||||
now: Instant,
|
||||
progress: &git::Progress,
|
||||
ui: &mut Ui,
|
||||
output: &mut ProgressOutput,
|
||||
) -> io::Result<()> {
|
||||
use std::fmt::Write as _;
|
||||
|
||||
if progress.overall == 1.0 {
|
||||
write!(ui, "\r{}", Clear(ClearType::CurrentLine))?;
|
||||
ui.flush()?;
|
||||
write!(output, "\r{}", Clear(ClearType::CurrentLine))?;
|
||||
output.flush()?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -48,11 +48,11 @@ impl Progress {
|
|||
return Ok(());
|
||||
}
|
||||
if self.guard.is_none() {
|
||||
let guard = ui.output_guard(crossterm::cursor::Show.to_string());
|
||||
let guard = output.output_guard(crossterm::cursor::Show.to_string());
|
||||
let guard = CleanupGuard::new(move || {
|
||||
drop(guard);
|
||||
});
|
||||
_ = write!(ui, "{}", crossterm::cursor::Hide);
|
||||
_ = write!(output, "{}", crossterm::cursor::Hide);
|
||||
self.guard = Some(guard);
|
||||
}
|
||||
self.next_print = now.min(self.next_print + Duration::from_secs(1) / UPDATE_HZ);
|
||||
|
@ -70,7 +70,7 @@ impl Progress {
|
|||
write!(self.buffer, "at {scaled: >5.1} {prefix}B/s ").unwrap();
|
||||
}
|
||||
|
||||
let bar_width = ui
|
||||
let bar_width = output
|
||||
.term_width()
|
||||
.map(usize::from)
|
||||
.unwrap_or(0)
|
||||
|
@ -79,8 +79,8 @@ impl Progress {
|
|||
draw_progress(progress.overall, &mut self.buffer, bar_width);
|
||||
self.buffer.push(']');
|
||||
|
||||
write!(ui, "{}", self.buffer)?;
|
||||
ui.flush()?;
|
||||
write!(output, "{}", self.buffer)?;
|
||||
output.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -170,22 +170,20 @@ impl RateEstimateState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn snapshot_progress(ui: &mut Ui) -> Option<impl Fn(&RepoPath) + '_> {
|
||||
struct State<'a> {
|
||||
pub fn snapshot_progress(ui: &Ui) -> Option<impl Fn(&RepoPath) + '_> {
|
||||
struct State {
|
||||
guard: Option<OutputGuard>,
|
||||
ui: &'a mut Ui,
|
||||
output: ProgressOutput,
|
||||
next_display_time: Instant,
|
||||
}
|
||||
|
||||
if !ui.use_progress_indicator() {
|
||||
return None;
|
||||
}
|
||||
let output = ui.progress_output()?;
|
||||
|
||||
// Don't clutter the output during fast operations.
|
||||
let next_display_time = Instant::now() + INITIAL_DELAY;
|
||||
let state = Mutex::new(State {
|
||||
guard: None,
|
||||
ui,
|
||||
output,
|
||||
next_display_time,
|
||||
});
|
||||
|
||||
|
@ -202,22 +200,22 @@ pub fn snapshot_progress(ui: &mut Ui) -> Option<impl Fn(&RepoPath) + '_> {
|
|||
if state.guard.is_none() {
|
||||
state.guard = Some(
|
||||
state
|
||||
.ui
|
||||
.output
|
||||
.output_guard(format!("\r{}", Clear(ClearType::CurrentLine))),
|
||||
);
|
||||
}
|
||||
|
||||
let line_width = state.ui.term_width().map(usize::from).unwrap_or(80);
|
||||
let line_width = state.output.term_width().map(usize::from).unwrap_or(80);
|
||||
let path_width = line_width.saturating_sub(13); // Account for "Snapshotting "
|
||||
|
||||
_ = write!(
|
||||
state.ui,
|
||||
state.output,
|
||||
"\r{}Snapshotting {:.*}",
|
||||
Clear(ClearType::CurrentLine),
|
||||
path_width,
|
||||
path.to_fs_path(Path::new("")).display()
|
||||
);
|
||||
_ = state.ui.flush();
|
||||
_ = state.output.flush();
|
||||
})
|
||||
}
|
||||
|
||||
|
|
49
src/ui.rs
49
src/ui.rs
|
@ -194,6 +194,12 @@ impl Ui {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn progress_output(&self) -> Option<ProgressOutput> {
|
||||
self.use_progress_indicator().then(|| ProgressOutput {
|
||||
output: io::stdout(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write(&mut self, text: &str) -> io::Result<()> {
|
||||
let data = text.as_bytes();
|
||||
match &mut self.output {
|
||||
|
@ -280,20 +286,6 @@ impl Ui {
|
|||
pub fn term_width(&self) -> Option<u16> {
|
||||
term_width()
|
||||
}
|
||||
|
||||
/// Construct a guard object which writes `data` when dropped. Useful for
|
||||
/// restoring terminal state.
|
||||
pub fn output_guard(&self, text: String) -> OutputGuard {
|
||||
OutputGuard {
|
||||
text,
|
||||
output: match self.output {
|
||||
UiOutput::Terminal { .. } => io::stdout(),
|
||||
// TODO we don't actually need to write in this case, so it
|
||||
// might be better to no-op
|
||||
UiOutput::Paged { .. } => io::stdout(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum UiOutput {
|
||||
|
@ -322,6 +314,35 @@ impl UiOutput {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProgressOutput {
|
||||
output: Stdout,
|
||||
}
|
||||
|
||||
impl ProgressOutput {
|
||||
pub fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
self.output.write_fmt(fmt)
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) -> io::Result<()> {
|
||||
self.output.flush()
|
||||
}
|
||||
|
||||
pub fn term_width(&self) -> Option<u16> {
|
||||
// Terminal can be resized while progress is displayed, so don't cache it.
|
||||
term_width()
|
||||
}
|
||||
|
||||
/// Construct a guard object which writes `text` when dropped. Useful for
|
||||
/// restoring terminal state.
|
||||
pub fn output_guard(&self, text: String) -> OutputGuard {
|
||||
OutputGuard {
|
||||
text,
|
||||
output: io::stdout(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputGuard {
|
||||
text: String,
|
||||
output: Stdout,
|
||||
|
|
Loading…
Reference in a new issue