mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-04 11:34:35 +00:00
feat: add taskbar progress reporting
This commit is contained in:
parent
4a8fd9b5df
commit
a0624eaf53
@ -854,7 +854,7 @@ impl<'gctx> DrainState<'gctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error(
|
fn handle_error(
|
||||||
&self,
|
&mut self,
|
||||||
shell: &mut Shell,
|
shell: &mut Shell,
|
||||||
err_state: &mut ErrorsDuringDrain,
|
err_state: &mut ErrorsDuringDrain,
|
||||||
new_err: impl Into<ErrorToHandle>,
|
new_err: impl Into<ErrorToHandle>,
|
||||||
@ -863,6 +863,7 @@ impl<'gctx> DrainState<'gctx> {
|
|||||||
if new_err.print_always || err_state.count == 0 {
|
if new_err.print_always || err_state.count == 0 {
|
||||||
crate::display_error(&new_err.error, shell);
|
crate::display_error(&new_err.error, shell);
|
||||||
if err_state.count == 0 && !self.active.is_empty() {
|
if err_state.count == 0 && !self.active.is_empty() {
|
||||||
|
self.progress.indicate_error();
|
||||||
let _ = shell.warn("build failed, waiting for other jobs to finish...");
|
let _ = shell.warn("build failed, waiting for other jobs to finish...");
|
||||||
}
|
}
|
||||||
err_state.count += 1;
|
err_state.count += 1;
|
||||||
|
@ -56,6 +56,7 @@ impl Shell {
|
|||||||
stderr_tty: std::io::stderr().is_terminal(),
|
stderr_tty: std::io::stderr().is_terminal(),
|
||||||
stdout_unicode: supports_unicode(&std::io::stdout()),
|
stdout_unicode: supports_unicode(&std::io::stdout()),
|
||||||
stderr_unicode: supports_unicode(&std::io::stderr()),
|
stderr_unicode: supports_unicode(&std::io::stderr()),
|
||||||
|
stderr_term_integration: supports_term_integration(&std::io::stderr()),
|
||||||
},
|
},
|
||||||
verbosity: Verbosity::Verbose,
|
verbosity: Verbosity::Verbose,
|
||||||
needs_clear: false,
|
needs_clear: false,
|
||||||
@ -122,6 +123,18 @@ impl Shell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_err_term_integration_available(&self) -> bool {
|
||||||
|
if let ShellOut::Stream {
|
||||||
|
stderr_term_integration,
|
||||||
|
..
|
||||||
|
} = self.output
|
||||||
|
{
|
||||||
|
stderr_term_integration
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a reference to the underlying stdout writer.
|
/// Gets a reference to the underlying stdout writer.
|
||||||
pub fn out(&mut self) -> &mut dyn Write {
|
pub fn out(&mut self) -> &mut dyn Write {
|
||||||
if self.needs_clear {
|
if self.needs_clear {
|
||||||
@ -426,6 +439,7 @@ enum ShellOut {
|
|||||||
hyperlinks: bool,
|
hyperlinks: bool,
|
||||||
stdout_unicode: bool,
|
stdout_unicode: bool,
|
||||||
stderr_unicode: bool,
|
stderr_unicode: bool,
|
||||||
|
stderr_term_integration: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,6 +589,16 @@ fn supports_hyperlinks() -> bool {
|
|||||||
supports_hyperlinks::supports_hyperlinks()
|
supports_hyperlinks::supports_hyperlinks()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines whether the terminal supports ANSI OSC 9;4.
|
||||||
|
#[allow(clippy::disallowed_methods)] // Read environment variables to detect terminal
|
||||||
|
fn supports_term_integration(stream: &dyn IsTerminal) -> bool {
|
||||||
|
let windows_terminal = std::env::var("WT_SESSION").is_ok();
|
||||||
|
let conemu = std::env::var("ConEmuANSI").ok() == Some("ON".into());
|
||||||
|
let wezterm = std::env::var("TERM_PROGRAM").ok() == Some("WezTerm".into());
|
||||||
|
|
||||||
|
(windows_terminal || conemu || wezterm) && stream.is_terminal()
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Hyperlink<D: fmt::Display> {
|
pub struct Hyperlink<D: fmt::Display> {
|
||||||
url: Option<D>,
|
url: Option<D>,
|
||||||
}
|
}
|
||||||
|
@ -2832,6 +2832,8 @@ pub struct TermConfig {
|
|||||||
pub struct ProgressConfig {
|
pub struct ProgressConfig {
|
||||||
pub when: ProgressWhen,
|
pub when: ProgressWhen,
|
||||||
pub width: Option<usize>,
|
pub width: Option<usize>,
|
||||||
|
/// Communicate progress status with a terminal
|
||||||
|
pub term_integration: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Debug, Default, Deserialize)]
|
||||||
@ -2864,10 +2866,12 @@ where
|
|||||||
"auto" => Ok(Some(ProgressConfig {
|
"auto" => Ok(Some(ProgressConfig {
|
||||||
when: ProgressWhen::Auto,
|
when: ProgressWhen::Auto,
|
||||||
width: None,
|
width: None,
|
||||||
|
term_integration: None,
|
||||||
})),
|
})),
|
||||||
"never" => Ok(Some(ProgressConfig {
|
"never" => Ok(Some(ProgressConfig {
|
||||||
when: ProgressWhen::Never,
|
when: ProgressWhen::Never,
|
||||||
width: None,
|
width: None,
|
||||||
|
term_integration: None,
|
||||||
})),
|
})),
|
||||||
"always" => Err(E::custom("\"always\" progress requires a `width` key")),
|
"always" => Err(E::custom("\"always\" progress requires a `width` key")),
|
||||||
_ => Err(E::unknown_variant(s, &["auto", "never"])),
|
_ => Err(E::unknown_variant(s, &["auto", "never"])),
|
||||||
@ -2889,6 +2893,7 @@ where
|
|||||||
if let ProgressConfig {
|
if let ProgressConfig {
|
||||||
when: ProgressWhen::Always,
|
when: ProgressWhen::Always,
|
||||||
width: None,
|
width: None,
|
||||||
|
..
|
||||||
} = pc
|
} = pc
|
||||||
{
|
{
|
||||||
return Err(serde::de::Error::custom(
|
return Err(serde::de::Error::custom(
|
||||||
|
@ -74,6 +74,108 @@ struct Format {
|
|||||||
style: ProgressStyle,
|
style: ProgressStyle,
|
||||||
max_width: usize,
|
max_width: usize,
|
||||||
max_print: usize,
|
max_print: usize,
|
||||||
|
term_integration: TerminalIntegration,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Controls terminal progress integration via OSC sequences.
|
||||||
|
struct TerminalIntegration {
|
||||||
|
enabled: bool,
|
||||||
|
error: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A progress status value printable as an ANSI OSC 9;4 escape code.
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Debug))]
|
||||||
|
enum StatusValue {
|
||||||
|
/// No output.
|
||||||
|
None,
|
||||||
|
/// Remove progress.
|
||||||
|
Remove,
|
||||||
|
/// Progress value (0-100).
|
||||||
|
Value(f64),
|
||||||
|
/// Indeterminate state (no bar, just animation)
|
||||||
|
Indeterminate,
|
||||||
|
/// Progress value in an error state (0-100).
|
||||||
|
Error(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ProgressOutput {
|
||||||
|
/// Print progress without a message
|
||||||
|
PrintNow,
|
||||||
|
/// Progress, message and progress report
|
||||||
|
TextAndReport(String, StatusValue),
|
||||||
|
/// Only progress report, no message and no text progress
|
||||||
|
Report(StatusValue),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerminalIntegration {
|
||||||
|
#[cfg(test)]
|
||||||
|
fn new(enabled: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
enabled,
|
||||||
|
error: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `TerminalIntegration` from Cargo's configuration.
|
||||||
|
/// Autodetect support if not explicitly enabled or disabled.
|
||||||
|
fn from_config(gctx: &GlobalContext) -> Self {
|
||||||
|
let enabled = gctx
|
||||||
|
.progress_config()
|
||||||
|
.term_integration
|
||||||
|
.unwrap_or_else(|| gctx.shell().is_err_term_integration_available());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
enabled,
|
||||||
|
error: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn progress_state(&self, value: StatusValue) -> StatusValue {
|
||||||
|
match (self.enabled, self.error) {
|
||||||
|
(true, false) => value,
|
||||||
|
(true, true) => match value {
|
||||||
|
StatusValue::Value(v) => StatusValue::Error(v),
|
||||||
|
_ => StatusValue::Error(100.0),
|
||||||
|
},
|
||||||
|
(false, _) => StatusValue::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&self) -> StatusValue {
|
||||||
|
self.progress_state(StatusValue::Remove)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self, percent: f64) -> StatusValue {
|
||||||
|
self.progress_state(StatusValue::Value(percent))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn indeterminate(&self) -> StatusValue {
|
||||||
|
self.progress_state(StatusValue::Indeterminate)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(&mut self) {
|
||||||
|
self.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for StatusValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// From https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
|
||||||
|
// ESC ] 9 ; 4 ; st ; pr ST
|
||||||
|
// When st is 0: remove progress.
|
||||||
|
// When st is 1: set progress value to pr (number, 0-100).
|
||||||
|
// When st is 2: set error state in taskbar, pr is optional.
|
||||||
|
// When st is 3: set indeterminate state, pr is ignored.
|
||||||
|
// When st is 4: set paused state, pr is optional.
|
||||||
|
let (state, progress) = match self {
|
||||||
|
Self::None => return Ok(()), // No output
|
||||||
|
Self::Remove => (0, 0.0),
|
||||||
|
Self::Value(v) => (1, *v),
|
||||||
|
Self::Indeterminate => (3, 0.0),
|
||||||
|
Self::Error(v) => (2, *v),
|
||||||
|
};
|
||||||
|
write!(f, "\x1b]9;4;{state};{progress:.0}\x1b\\")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gctx> Progress<'gctx> {
|
impl<'gctx> Progress<'gctx> {
|
||||||
@ -126,6 +228,7 @@ impl<'gctx> Progress<'gctx> {
|
|||||||
// 50 gives some space for text after the progress bar,
|
// 50 gives some space for text after the progress bar,
|
||||||
// even on narrow (e.g. 80 char) terminals.
|
// even on narrow (e.g. 80 char) terminals.
|
||||||
max_print: 50,
|
max_print: 50,
|
||||||
|
term_integration: TerminalIntegration::from_config(gctx),
|
||||||
},
|
},
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
done: false,
|
done: false,
|
||||||
@ -223,7 +326,7 @@ impl<'gctx> Progress<'gctx> {
|
|||||||
/// calling it too often.
|
/// calling it too often.
|
||||||
pub fn print_now(&mut self, msg: &str) -> CargoResult<()> {
|
pub fn print_now(&mut self, msg: &str) -> CargoResult<()> {
|
||||||
match &mut self.state {
|
match &mut self.state {
|
||||||
Some(s) => s.print("", msg),
|
Some(s) => s.print(ProgressOutput::PrintNow, msg),
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,6 +337,13 @@ impl<'gctx> Progress<'gctx> {
|
|||||||
s.clear();
|
s.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the progress reporter to the error state.
|
||||||
|
pub fn indicate_error(&mut self) {
|
||||||
|
if let Some(s) = &mut self.state {
|
||||||
|
s.format.term_integration.error()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Throttle {
|
impl Throttle {
|
||||||
@ -269,6 +379,11 @@ impl Throttle {
|
|||||||
impl<'gctx> State<'gctx> {
|
impl<'gctx> State<'gctx> {
|
||||||
fn tick(&mut self, cur: usize, max: usize, msg: &str) -> CargoResult<()> {
|
fn tick(&mut self, cur: usize, max: usize, msg: &str) -> CargoResult<()> {
|
||||||
if self.done {
|
if self.done {
|
||||||
|
write!(
|
||||||
|
self.gctx.shell().err(),
|
||||||
|
"{}",
|
||||||
|
self.format.term_integration.remove()
|
||||||
|
)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,21 +395,30 @@ impl<'gctx> State<'gctx> {
|
|||||||
// return back to the beginning of the line for the next print.
|
// return back to the beginning of the line for the next print.
|
||||||
self.try_update_max_width();
|
self.try_update_max_width();
|
||||||
if let Some(pbar) = self.format.progress(cur, max) {
|
if let Some(pbar) = self.format.progress(cur, max) {
|
||||||
self.print(&pbar, msg)?;
|
self.print(pbar, msg)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print(&mut self, prefix: &str, msg: &str) -> CargoResult<()> {
|
fn print(&mut self, progress: ProgressOutput, msg: &str) -> CargoResult<()> {
|
||||||
self.throttle.update();
|
self.throttle.update();
|
||||||
self.try_update_max_width();
|
self.try_update_max_width();
|
||||||
|
|
||||||
|
let (mut line, report) = match progress {
|
||||||
|
ProgressOutput::PrintNow => (String::new(), None),
|
||||||
|
ProgressOutput::TextAndReport(prefix, report) => (prefix, Some(report)),
|
||||||
|
ProgressOutput::Report(report) => (String::new(), Some(report)),
|
||||||
|
};
|
||||||
|
|
||||||
// make sure we have enough room for the header
|
// make sure we have enough room for the header
|
||||||
if self.format.max_width < 15 {
|
if self.format.max_width < 15 {
|
||||||
|
// even if we don't have space we can still output progress report
|
||||||
|
if let Some(tb) = report {
|
||||||
|
write!(self.gctx.shell().err(), "{tb}\r")?;
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut line = prefix.to_string();
|
|
||||||
self.format.render(&mut line, msg);
|
self.format.render(&mut line, msg);
|
||||||
while line.len() < self.format.max_width - 15 {
|
while line.len() < self.format.max_width - 15 {
|
||||||
line.push(' ');
|
line.push(' ');
|
||||||
@ -305,7 +429,11 @@ impl<'gctx> State<'gctx> {
|
|||||||
let mut shell = self.gctx.shell();
|
let mut shell = self.gctx.shell();
|
||||||
shell.set_needs_clear(false);
|
shell.set_needs_clear(false);
|
||||||
shell.status_header(&self.name)?;
|
shell.status_header(&self.name)?;
|
||||||
write!(shell.err(), "{}\r", line)?;
|
if let Some(tb) = report {
|
||||||
|
write!(shell.err(), "{line}{tb}\r")?;
|
||||||
|
} else {
|
||||||
|
write!(shell.err(), "{line}\r")?;
|
||||||
|
}
|
||||||
self.last_line = Some(line);
|
self.last_line = Some(line);
|
||||||
shell.set_needs_clear(true);
|
shell.set_needs_clear(true);
|
||||||
}
|
}
|
||||||
@ -314,6 +442,12 @@ impl<'gctx> State<'gctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
|
// Always clear the progress report
|
||||||
|
let _ = write!(
|
||||||
|
self.gctx.shell().err(),
|
||||||
|
"{}",
|
||||||
|
self.format.term_integration.remove()
|
||||||
|
);
|
||||||
// No need to clear if the progress is not currently being displayed.
|
// No need to clear if the progress is not currently being displayed.
|
||||||
if self.last_line.is_some() && !self.gctx.shell().is_cleared() {
|
if self.last_line.is_some() && !self.gctx.shell().is_cleared() {
|
||||||
self.gctx.shell().err_erase_line();
|
self.gctx.shell().err_erase_line();
|
||||||
@ -331,7 +465,7 @@ impl<'gctx> State<'gctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Format {
|
impl Format {
|
||||||
fn progress(&self, cur: usize, max: usize) -> Option<String> {
|
fn progress(&self, cur: usize, max: usize) -> Option<ProgressOutput> {
|
||||||
assert!(cur <= max);
|
assert!(cur <= max);
|
||||||
// Render the percentage at the far right and then figure how long the
|
// Render the percentage at the far right and then figure how long the
|
||||||
// progress bar is
|
// progress bar is
|
||||||
@ -339,11 +473,21 @@ impl Format {
|
|||||||
let pct = if !pct.is_finite() { 0.0 } else { pct };
|
let pct = if !pct.is_finite() { 0.0 } else { pct };
|
||||||
let stats = match self.style {
|
let stats = match self.style {
|
||||||
ProgressStyle::Percentage => format!(" {:6.02}%", pct * 100.0),
|
ProgressStyle::Percentage => format!(" {:6.02}%", pct * 100.0),
|
||||||
ProgressStyle::Ratio => format!(" {}/{}", cur, max),
|
ProgressStyle::Ratio => format!(" {cur}/{max}"),
|
||||||
ProgressStyle::Indeterminate => String::new(),
|
ProgressStyle::Indeterminate => String::new(),
|
||||||
};
|
};
|
||||||
|
let report = match self.style {
|
||||||
|
ProgressStyle::Percentage | ProgressStyle::Ratio => {
|
||||||
|
self.term_integration.value(pct * 100.0)
|
||||||
|
}
|
||||||
|
ProgressStyle::Indeterminate => self.term_integration.indeterminate(),
|
||||||
|
};
|
||||||
|
|
||||||
let extra_len = stats.len() + 2 /* [ and ] */ + 15 /* status header */;
|
let extra_len = stats.len() + 2 /* [ and ] */ + 15 /* status header */;
|
||||||
let Some(display_width) = self.width().checked_sub(extra_len) else {
|
let Some(display_width) = self.width().checked_sub(extra_len) else {
|
||||||
|
if self.term_integration.enabled {
|
||||||
|
return Some(ProgressOutput::Report(report));
|
||||||
|
}
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -371,7 +515,7 @@ impl Format {
|
|||||||
string.push(']');
|
string.push(']');
|
||||||
string.push_str(&stats);
|
string.push_str(&stats);
|
||||||
|
|
||||||
Some(string)
|
Some(ProgressOutput::TextAndReport(string, report))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, string: &mut String, msg: &str) {
|
fn render(&self, string: &mut String, msg: &str) {
|
||||||
@ -398,7 +542,11 @@ impl Format {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn progress_status(&self, cur: usize, max: usize, msg: &str) -> Option<String> {
|
fn progress_status(&self, cur: usize, max: usize, msg: &str) -> Option<String> {
|
||||||
let mut ret = self.progress(cur, max)?;
|
let mut ret = match self.progress(cur, max)? {
|
||||||
|
// Check only the variant that contains text.
|
||||||
|
ProgressOutput::TextAndReport(text, _) => text,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
self.render(&mut ret, msg);
|
self.render(&mut ret, msg);
|
||||||
Some(ret)
|
Some(ret)
|
||||||
}
|
}
|
||||||
@ -420,6 +568,7 @@ fn test_progress_status() {
|
|||||||
style: ProgressStyle::Ratio,
|
style: ProgressStyle::Ratio,
|
||||||
max_print: 40,
|
max_print: 40,
|
||||||
max_width: 60,
|
max_width: 60,
|
||||||
|
term_integration: TerminalIntegration::new(false),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format.progress_status(0, 4, ""),
|
format.progress_status(0, 4, ""),
|
||||||
@ -493,6 +642,7 @@ fn test_progress_status_percentage() {
|
|||||||
style: ProgressStyle::Percentage,
|
style: ProgressStyle::Percentage,
|
||||||
max_print: 40,
|
max_print: 40,
|
||||||
max_width: 60,
|
max_width: 60,
|
||||||
|
term_integration: TerminalIntegration::new(false),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format.progress_status(0, 77, ""),
|
format.progress_status(0, 77, ""),
|
||||||
@ -518,6 +668,7 @@ fn test_progress_status_too_short() {
|
|||||||
style: ProgressStyle::Percentage,
|
style: ProgressStyle::Percentage,
|
||||||
max_print: 25,
|
max_print: 25,
|
||||||
max_width: 25,
|
max_width: 25,
|
||||||
|
term_integration: TerminalIntegration::new(false),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format.progress_status(1, 1, ""),
|
format.progress_status(1, 1, ""),
|
||||||
@ -528,6 +679,25 @@ fn test_progress_status_too_short() {
|
|||||||
style: ProgressStyle::Percentage,
|
style: ProgressStyle::Percentage,
|
||||||
max_print: 24,
|
max_print: 24,
|
||||||
max_width: 24,
|
max_width: 24,
|
||||||
|
term_integration: TerminalIntegration::new(false),
|
||||||
};
|
};
|
||||||
assert_eq!(format.progress_status(1, 1, ""), None);
|
assert_eq!(format.progress_status(1, 1, ""), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_term_integration_disabled() {
|
||||||
|
let report = TerminalIntegration::new(false);
|
||||||
|
let mut out = String::new();
|
||||||
|
out.push_str(&report.remove().to_string());
|
||||||
|
out.push_str(&report.value(10.0).to_string());
|
||||||
|
out.push_str(&report.indeterminate().to_string());
|
||||||
|
assert!(out.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_term_integration_error_state() {
|
||||||
|
let mut report = TerminalIntegration::new(true);
|
||||||
|
assert_eq!(report.value(10.0), StatusValue::Value(10.0));
|
||||||
|
report.error();
|
||||||
|
assert_eq!(report.value(50.0), StatusValue::Error(50.0));
|
||||||
|
}
|
||||||
|
@ -198,6 +198,7 @@ hyperlinks = true # whether cargo inserts links into output
|
|||||||
unicode = true # whether cargo can render output using non-ASCII unicode characters
|
unicode = true # whether cargo can render output using non-ASCII unicode characters
|
||||||
progress.when = 'auto' # whether cargo shows progress bar
|
progress.when = 'auto' # whether cargo shows progress bar
|
||||||
progress.width = 80 # width of progress bar
|
progress.width = 80 # width of progress bar
|
||||||
|
progress.term-integration = true # whether cargo reports progress to terminal emulator
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
@ -1361,6 +1362,13 @@ Controls whether or not progress bar is shown in the terminal. Possible values:
|
|||||||
|
|
||||||
Sets the width for progress bar.
|
Sets the width for progress bar.
|
||||||
|
|
||||||
|
#### `term.progress.term-integration`
|
||||||
|
* Type: bool
|
||||||
|
* Default: auto-detect
|
||||||
|
* Environment: `CARGO_TERM_PROGRESS_TERM_INTEGRATION`
|
||||||
|
|
||||||
|
Report progress to the teminal emulator for display in places like the task bar.
|
||||||
|
|
||||||
[`cargo bench`]: ../commands/cargo-bench.md
|
[`cargo bench`]: ../commands/cargo-bench.md
|
||||||
[`cargo login`]: ../commands/cargo-login.md
|
[`cargo login`]: ../commands/cargo-login.md
|
||||||
[`cargo logout`]: ../commands/cargo-logout.md
|
[`cargo logout`]: ../commands/cargo-logout.md
|
||||||
|
Loading…
x
Reference in New Issue
Block a user