summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2019-05-30 11:07:58 -0400
committerRyan Dahl <ry@tinyclouds.org>2019-06-25 06:32:28 -0700
commit3a4d88475b40a17f2ce17b775a3f07c78be83d79 (patch)
tree2780e2c7ea86c0a7e039cfc3e478bd82131a467e
parent89216c7baaab8ade3daf9103572647addeb404f3 (diff)
Port code from Cargo and use for progress
A lot of its functionality is unused still, but the goal it to slowly migrate logging functionality to it. There is also a useful progress bar which can be ported over later - it depends on this module. https://github.com/rust-lang/cargo/blob/4c1fa54d10f58d69ac9ff55be68e1b1c25ecb816/src/cargo/util/progress.rs
-rw-r--r--Cargo.lock11
-rw-r--r--build_extra/rust/BUILD.gn13
-rw-r--r--cli/BUILD.gn1
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/ansi.rs2
-rw-r--r--cli/compiler.rs2
-rw-r--r--cli/deno_dir.rs4
-rw-r--r--cli/main.rs19
-rw-r--r--cli/permissions.rs1
-rw-r--r--cli/progress.rs39
-rw-r--r--cli/shell.rs491
m---------third_party0
12 files changed, 553 insertions, 31 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e5ba0961e..fa227869d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -219,6 +219,7 @@ dependencies = [
"dirs 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"flatbuffers 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fwdansi 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.12.30 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper-rustls 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -353,6 +354,15 @@ dependencies = [
]
[[package]]
+name = "fwdansi"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "h2"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1551,6 +1561,7 @@ dependencies = [
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139"
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
+"checksum fwdansi 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34dd4c507af68d37ffef962063dfa1944ce0dd4d5b82043dbab1dabe088610c3"
"checksum h2 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "1e42e3daed5a7e17b12a0c23b5b2fbff23a925a570938ebee4baca1a9a1a2240"
"checksum http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "eed324f0f0daf6ec10c474f150505af2c143f251722bf9dbd1261bd1f2ee2c1a"
"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d"
diff --git a/build_extra/rust/BUILD.gn b/build_extra/rust/BUILD.gn
index b1a36fedf..8151eb89f 100644
--- a/build_extra/rust/BUILD.gn
+++ b/build_extra/rust/BUILD.gn
@@ -1480,6 +1480,19 @@ rust_rlib("termcolor") {
}
}
+rust_rlib("fwdansi") {
+ edition = "2015"
+ source_root = "$cargo_home/registry/src/github.com-1ecc6299db9ec823/fwdansi-1.0.1/src/lib.rs"
+ args = [
+ "--cap-lints",
+ "allow",
+ ]
+ extern_rlib = [
+ "memchr",
+ "termcolor",
+ ]
+}
+
rust_rlib("textwrap") {
edition = "2015"
source_root = "$cargo_home/registry/src/github.com-1ecc6299db9ec823/textwrap-0.11.0/src/lib.rs"
diff --git a/cli/BUILD.gn b/cli/BUILD.gn
index 72cf8710b..20a5b2dee 100644
--- a/cli/BUILD.gn
+++ b/cli/BUILD.gn
@@ -26,6 +26,7 @@ main_extern_rlib = [
"dirs",
"flatbuffers",
"futures",
+ "fwdansi",
"http",
"hyper",
"hyper_rustls",
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 6289f9bb4..9a6d18c1a 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -55,6 +55,7 @@ utime = "0.2.1"
[target.'cfg(windows)'.dependencies]
winapi = "0.3.7"
+fwdansi = "1.0.1"
[target.'cfg(unix)'.dependencies]
nix = "0.13.x"
diff --git a/cli/ansi.rs b/cli/ansi.rs
index 32936ace0..ab2347bcd 100644
--- a/cli/ansi.rs
+++ b/cli/ansi.rs
@@ -1,4 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+// TODO(ry) Rename this file to colors.rs
+// TODO(ry) Replace ansi_term with termcolor.
use ansi_term::Color::Black;
use ansi_term::Color::Fixed;
use ansi_term::Color::Red;
diff --git a/cli/compiler.rs b/cli/compiler.rs
index dc30758b2..872ed28a2 100644
--- a/cli/compiler.rs
+++ b/cli/compiler.rs
@@ -180,7 +180,7 @@ pub fn compile_async(
err_check(worker.execute("workerMain()"));
err_check(worker.execute("compilerMain()"));
- let compiling_job = state.progress.add(format!("Compiling {}", module_name));
+ let compiling_job = state.progress.add("Compile", &module_name);
let resource = worker.state.resource.clone();
let compiler_rid = resource.rid;
diff --git a/cli/deno_dir.rs b/cli/deno_dir.rs
index 8695b0578..324aa0e32 100644
--- a/cli/deno_dir.rs
+++ b/cli/deno_dir.rs
@@ -647,9 +647,7 @@ fn fetch_remote_source_async(
) -> impl Future<Item = Option<ModuleMetaData>, Error = DenoError> {
use crate::http_util::FetchOnceResult;
- let download_job = deno_dir
- .progress
- .add(format!("Downloading {}", module_name));
+ let download_job = deno_dir.progress.add("Download", module_name);
let module_name = module_name.to_owned();
let filepath = filepath.to_owned();
diff --git a/cli/main.rs b/cli/main.rs
index 05748f5ab..8121e5f7c 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -35,6 +35,7 @@ mod progress;
mod repl;
pub mod resolve_addr;
pub mod resources;
+mod shell;
mod signal;
pub mod source_maps;
mod startup_data;
@@ -158,17 +159,15 @@ fn create_worker_and_state(
flags: DenoFlags,
argv: Vec<String>,
) -> (Worker, ThreadSafeState) {
+ use crate::shell::Shell;
+ use std::sync::Arc;
+ use std::sync::Mutex;
+ let shell = Arc::new(Mutex::new(Shell::new()));
let progress = Progress::new();
- progress.set_callback(|done, completed, total, msg| {
- if !done {
- eprint!("\r[{}/{}] {}", completed, total, msg);
- eprint!("\x1B[K"); // Clear to end of line.
- return;
- }
-
- // print empty line only if progress bar was used
- if done && total > 0 {
- eprintln!();
+ progress.set_callback(move |_done, _completed, _total, status, msg| {
+ if !status.is_empty() {
+ let mut s = shell.lock().unwrap();
+ s.status(status, msg).expect("shell problem");
}
});
let state = ThreadSafeState::new(flags, argv, ops::op_selector_std, progress);
diff --git a/cli/permissions.rs b/cli/permissions.rs
index b8830c537..c1878b6ff 100644
--- a/cli/permissions.rs
+++ b/cli/permissions.rs
@@ -499,7 +499,6 @@ fn check_path_white_list(
#[cfg(test)]
mod tests {
- #![allow(clippy::cyclomatic_complexity)]
use super::*;
// Creates vector of strings, Vec<String>
diff --git a/cli/progress.rs b/cli/progress.rs
index a9bfcc37d..c2fa7979f 100644
--- a/cli/progress.rs
+++ b/cli/progress.rs
@@ -12,7 +12,7 @@ impl Progress {
pub fn set_callback<F>(&self, f: F)
where
- F: Fn(bool, usize, usize, &str) + Send + Sync + 'static,
+ F: Fn(bool, usize, usize, &str, &str) + Send + Sync + 'static,
{
let mut s = self.0.lock().unwrap();
assert!(s.callback.is_none());
@@ -30,11 +30,17 @@ impl Progress {
s.job_names.clone()
}
- pub fn add(&self, name: String) -> Job {
+ pub fn add(&self, status: &str, name: &str) -> Job {
let mut s = self.0.lock().unwrap();
let id = s.job_names.len();
- s.maybe_call_callback(false, s.complete, s.job_names.len() + 1, &name);
- s.job_names.push(name);
+ s.maybe_call_callback(
+ false,
+ s.complete,
+ s.job_names.len() + 1,
+ status,
+ name,
+ );
+ s.job_names.push(name.to_string());
Job {
id,
inner: self.0.clone(),
@@ -43,11 +49,11 @@ impl Progress {
pub fn done(&self) {
let s = self.0.lock().unwrap();
- s.maybe_call_callback(true, s.complete, s.job_names.len(), "");
+ s.maybe_call_callback(true, s.complete, s.job_names.len(), "", "");
}
}
-type Callback = dyn Fn(bool, usize, usize, &str) + Send + Sync;
+type Callback = dyn Fn(bool, usize, usize, &str, &str) + Send + Sync;
#[derive(Default)]
struct Inner {
@@ -62,10 +68,11 @@ impl Inner {
done: bool,
complete: usize,
total: usize,
+ status: &str,
msg: &str,
) {
if let Some(ref cb) = self.callback {
- cb(done, complete, total, msg);
+ cb(done, complete, total, status, msg);
}
}
@@ -87,7 +94,7 @@ impl Drop for Job {
s.complete += 1;
let name = &s.job_names[self.id];
let (complete, total) = s.progress();
- s.maybe_call_callback(false, complete, total, name);
+ s.maybe_call_callback(false, complete, total, "", name);
}
}
@@ -100,12 +107,12 @@ mod tests {
let p = Progress::new();
assert_eq!(p.progress(), (0, 0));
{
- let _j1 = p.add("hello".to_string());
+ let _j1 = p.add("status", "hello");
assert_eq!(p.progress(), (0, 1));
}
assert_eq!(p.progress(), (1, 1));
{
- let _j2 = p.add("hello".to_string());
+ let _j2 = p.add("status", "hello");
assert_eq!(p.progress(), (1, 2));
}
assert_eq!(p.progress(), (2, 2));
@@ -114,8 +121,8 @@ mod tests {
#[test]
fn history() {
let p = Progress::new();
- let _a = p.add("a".to_string());
- let _b = p.add("b".to_string());
+ let _a = p.add("status", "a");
+ let _b = p.add("status", "b");
assert_eq!(p.history(), vec!["a", "b"]);
}
@@ -127,16 +134,16 @@ mod tests {
let p = Progress::new();
let callback_history_ = callback_history.clone();
- p.set_callback(move |_done, complete, total, msg| {
+ p.set_callback(move |_done, complete, total, _status, msg| {
// println!("callback: {}, {}, {}", complete, total, msg);
let mut h = callback_history_.lock().unwrap();
h.push((complete, total, String::from(msg)));
});
{
- let _a = p.add("a".to_string());
- let _b = p.add("b".to_string());
+ let _a = p.add("status", "a");
+ let _b = p.add("status", "b");
}
- let _c = p.add("c".to_string());
+ let _c = p.add("status", "c");
}
let h = callback_history.lock().unwrap();
diff --git a/cli/shell.rs b/cli/shell.rs
new file mode 100644
index 000000000..4dc388f39
--- /dev/null
+++ b/cli/shell.rs
@@ -0,0 +1,491 @@
+// This file was forked from Cargo on 2019.05.29:
+// https://github.com/rust-lang/cargo/blob/edd874/src/cargo/core/shell.rs
+// Cargo is MIT licenced:
+// https://github.com/rust-lang/cargo/blob/edd874/LICENSE-MIT
+
+#![allow(dead_code)]
+#![allow(irrefutable_let_patterns)]
+
+use std::fmt;
+use std::io::prelude::*;
+
+use atty;
+use termcolor::Color::{Cyan, Green, Red, Yellow};
+use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor};
+
+use crate::deno_error::DenoResult;
+
+/// The requested verbosity of output.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum Verbosity {
+ Verbose,
+ Normal,
+ Quiet,
+}
+
+/// An abstraction around a `Write`able object that remembers preferences for output verbosity and
+/// color.
+pub struct Shell {
+ /// the `Write`able object, either with or without color support (represented by different enum
+ /// variants)
+ err: ShellOut,
+ /// How verbose messages should be
+ verbosity: Verbosity,
+ /// Flag that indicates the current line needs to be cleared before
+ /// printing. Used when a progress bar is currently displayed.
+ needs_clear: bool,
+}
+
+impl fmt::Debug for Shell {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.err {
+ /*
+ ShellOut::Write(_) => f
+ .debug_struct("Shell")
+ .field("verbosity", &self.verbosity)
+ .finish(),
+ */
+ ShellOut::Stream { color_choice, .. } => f
+ .debug_struct("Shell")
+ .field("verbosity", &self.verbosity)
+ .field("color_choice", &color_choice)
+ .finish(),
+ }
+ }
+}
+
+/// A `Write`able object, either with or without color support
+enum ShellOut {
+ /// A plain write object without color support
+ // TODO(ry) Disabling this type of output because it makes Shell
+ // not thread safe and thus not includable in ThreadSafeState.
+ // But I think we will want this in the future.
+ //Write(Box<dyn Write>),
+ /// Color-enabled stdio, with information on whether color should be used
+ Stream {
+ stream: StandardStream,
+ tty: bool,
+ color_choice: ColorChoice,
+ },
+}
+
+/// Whether messages should use color output
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub enum ColorChoice {
+ /// Force color output
+ Always,
+ /// Force disable color output
+ Never,
+ /// Intelligently guess whether to use color output
+ CargoAuto,
+}
+
+impl Shell {
+ /// Creates a new shell (color choice and verbosity), defaulting to 'auto' color and verbose
+ /// output.
+ pub fn new() -> Shell {
+ Shell {
+ err: ShellOut::Stream {
+ stream: StandardStream::stderr(
+ ColorChoice::CargoAuto.to_termcolor_color_choice(),
+ ),
+ color_choice: ColorChoice::CargoAuto,
+ tty: atty::is(atty::Stream::Stderr),
+ },
+ verbosity: Verbosity::Verbose,
+ needs_clear: false,
+ }
+ }
+
+ /*
+ /// Creates a shell from a plain writable object, with no color, and max verbosity.
+ pub fn from_write(out: Box<dyn Write>) -> Shell {
+ Shell {
+ err: ShellOut::Write(out),
+ verbosity: Verbosity::Verbose,
+ needs_clear: false,
+ }
+ }
+ */
+
+ /// Prints a message, where the status will have `color` color, and can be justified. The
+ /// messages follows without color.
+ fn print(
+ &mut self,
+ status: &dyn fmt::Display,
+ message: Option<&dyn fmt::Display>,
+ color: Color,
+ justified: bool,
+ ) -> DenoResult<()> {
+ match self.verbosity {
+ Verbosity::Quiet => Ok(()),
+ _ => {
+ if self.needs_clear {
+ self.err_erase_line();
+ }
+ self.err.print(status, message, color, justified)
+ }
+ }
+ }
+
+ /// Sets whether the next print should clear the current line.
+ pub fn set_needs_clear(&mut self, needs_clear: bool) {
+ self.needs_clear = needs_clear;
+ }
+
+ /// Returns `true` if the `needs_clear` flag is unset.
+ pub fn is_cleared(&self) -> bool {
+ !self.needs_clear
+ }
+
+ /// Returns the width of the terminal in spaces, if any.
+ pub fn err_width(&self) -> Option<usize> {
+ match self.err {
+ ShellOut::Stream { tty: true, .. } => imp::stderr_width(),
+ _ => None,
+ }
+ }
+
+ /// Returns `true` if stderr is a tty.
+ pub fn is_err_tty(&self) -> bool {
+ match self.err {
+ ShellOut::Stream { tty, .. } => tty,
+ // _ => false,
+ }
+ }
+
+ /// Gets a reference to the underlying writer.
+ pub fn err(&mut self) -> &mut dyn Write {
+ if self.needs_clear {
+ self.err_erase_line();
+ }
+ self.err.as_write()
+ }
+
+ /// Erase from cursor to end of line.
+ pub fn err_erase_line(&mut self) {
+ if let ShellOut::Stream { tty: true, .. } = self.err {
+ imp::err_erase_line(self);
+ self.needs_clear = false;
+ }
+ }
+
+ /// Shortcut to right-align and color green a status message.
+ pub fn status<T, U>(&mut self, status: T, message: U) -> DenoResult<()>
+ where
+ T: fmt::Display,
+ U: fmt::Display,
+ {
+ self.print(&status, Some(&message), Green, false)
+ }
+
+ pub fn status_header<T>(&mut self, status: T) -> DenoResult<()>
+ where
+ T: fmt::Display,
+ {
+ self.print(&status, None, Cyan, true)
+ }
+
+ /// Shortcut to right-align a status message.
+ pub fn status_with_color<T, U>(
+ &mut self,
+ status: T,
+ message: U,
+ color: Color,
+ ) -> DenoResult<()>
+ where
+ T: fmt::Display,
+ U: fmt::Display,
+ {
+ self.print(&status, Some(&message), color, true)
+ }
+
+ /// Runs the callback only if we are in verbose mode.
+ pub fn verbose<F>(&mut self, mut callback: F) -> DenoResult<()>
+ where
+ F: FnMut(&mut Shell) -> DenoResult<()>,
+ {
+ match self.verbosity {
+ Verbosity::Verbose => callback(self),
+ _ => Ok(()),
+ }
+ }
+
+ /// Runs the callback if we are not in verbose mode.
+ pub fn concise<F>(&mut self, mut callback: F) -> DenoResult<()>
+ where
+ F: FnMut(&mut Shell) -> DenoResult<()>,
+ {
+ match self.verbosity {
+ Verbosity::Verbose => Ok(()),
+ _ => callback(self),
+ }
+ }
+
+ /// Prints a red 'error' message.
+ pub fn error<T: fmt::Display>(&mut self, message: T) -> DenoResult<()> {
+ self.print(&"error:", Some(&message), Red, false)
+ }
+
+ /// Prints an amber 'warning' message.
+ pub fn warn<T: fmt::Display>(&mut self, message: T) -> DenoResult<()> {
+ match self.verbosity {
+ Verbosity::Quiet => Ok(()),
+ _ => self.print(&"warning:", Some(&message), Yellow, false),
+ }
+ }
+
+ /// Updates the verbosity of the shell.
+ pub fn set_verbosity(&mut self, verbosity: Verbosity) {
+ self.verbosity = verbosity;
+ }
+
+ /// Gets the verbosity of the shell.
+ pub fn verbosity(&self) -> Verbosity {
+ self.verbosity
+ }
+
+ /// Updates the color choice (always, never, or auto) from a string..
+ pub fn set_color_choice(&mut self, color: Option<&str>) -> DenoResult<()> {
+ if let ShellOut::Stream {
+ ref mut stream,
+ ref mut color_choice,
+ ..
+ } = self.err
+ {
+ let cfg = match color {
+ Some("always") => ColorChoice::Always,
+ Some("never") => ColorChoice::Never,
+
+ Some("auto") | None => ColorChoice::CargoAuto,
+
+ Some(arg) => panic!(
+ "argument for --color must be auto, always, or \
+ never, but found `{}`",
+ arg
+ ),
+ };
+ *color_choice = cfg;
+ *stream = StandardStream::stderr(cfg.to_termcolor_color_choice());
+ }
+ Ok(())
+ }
+
+ /// Gets the current color choice.
+ ///
+ /// If we are not using a color stream, this will always return `Never`, even if the color
+ /// choice has been set to something else.
+ pub fn color_choice(&self) -> ColorChoice {
+ match self.err {
+ ShellOut::Stream { color_choice, .. } => color_choice,
+ // ShellOut::Write(_) => ColorChoice::Never,
+ }
+ }
+
+ /// Whether the shell supports color.
+ pub fn supports_color(&self) -> bool {
+ match &self.err {
+ // ShellOut::Write(_) => false,
+ ShellOut::Stream { stream, .. } => stream.supports_color(),
+ }
+ }
+
+ /// Prints a message and translates ANSI escape code into console colors.
+ pub fn print_ansi(&mut self, message: &[u8]) -> DenoResult<()> {
+ if self.needs_clear {
+ self.err_erase_line();
+ }
+ #[cfg(windows)]
+ {
+ if let ShellOut::Stream { stream, .. } = &mut self.err {
+ ::fwdansi::write_ansi(stream, message)?;
+ return Ok(());
+ }
+ }
+ self.err().write_all(message)?;
+ Ok(())
+ }
+}
+
+impl Default for Shell {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl ShellOut {
+ /// Prints out a message with a status. The status comes first, and is bold plus the given
+ /// color. The status can be justified, in which case the max width that will right align is
+ /// 12 chars.
+ fn print(
+ &mut self,
+ status: &dyn fmt::Display,
+ message: Option<&dyn fmt::Display>,
+ color: Color,
+ justified: bool,
+ ) -> DenoResult<()> {
+ match *self {
+ ShellOut::Stream { ref mut stream, .. } => {
+ stream.reset()?;
+ stream
+ .set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
+ if justified {
+ write!(stream, "{:>12}", status)?;
+ } else {
+ write!(stream, "{}", status)?;
+ }
+ stream.reset()?;
+ match message {
+ Some(message) => writeln!(stream, " {}", message)?,
+ None => write!(stream, " ")?,
+ }
+ } /*
+ ShellOut::Write(ref mut w) => {
+ if justified {
+ write!(w, "{:>12}", status)?;
+ } else {
+ write!(w, "{}", status)?;
+ }
+ match message {
+ Some(message) => writeln!(w, " {}", message)?,
+ None => write!(w, " ")?,
+ }
+ }
+ */
+ }
+ Ok(())
+ }
+
+ /// Gets this object as a `io::Write`.
+ fn as_write(&mut self) -> &mut dyn Write {
+ match *self {
+ ShellOut::Stream { ref mut stream, .. } => stream,
+ // ShellOut::Write(ref mut w) => w,
+ }
+ }
+}
+
+impl ColorChoice {
+ /// Converts our color choice to termcolor's version.
+ fn to_termcolor_color_choice(self) -> termcolor::ColorChoice {
+ match self {
+ ColorChoice::Always => termcolor::ColorChoice::Always,
+ ColorChoice::Never => termcolor::ColorChoice::Never,
+ ColorChoice::CargoAuto => {
+ if atty::is(atty::Stream::Stderr) {
+ termcolor::ColorChoice::Auto
+ } else {
+ termcolor::ColorChoice::Never
+ }
+ }
+ }
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "macos"))]
+mod imp {
+ use std::mem;
+
+ use libc;
+
+ use super::Shell;
+
+ pub fn stderr_width() -> Option<usize> {
+ unsafe {
+ let mut winsize: libc::winsize = mem::zeroed();
+ if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ, &mut winsize) < 0 {
+ return None;
+ }
+ if winsize.ws_col > 0 {
+ Some(winsize.ws_col as usize)
+ } else {
+ None
+ }
+ }
+ }
+
+ pub fn err_erase_line(shell: &mut Shell) {
+ // This is the "EL - Erase in Line" sequence. It clears from the cursor
+ // to the end of line.
+ // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
+ let _ = shell.err.as_write().write_all(b"\x1B[K");
+ }
+}
+
+#[cfg(all(unix, not(any(target_os = "linux", target_os = "macos"))))]
+mod imp {
+ pub(super) use super::default_err_erase_line as err_erase_line;
+
+ pub fn stderr_width() -> Option<usize> {
+ None
+ }
+}
+
+#[cfg(windows)]
+mod imp {
+ use std::{cmp, mem, ptr};
+ use winapi::um::fileapi::*;
+ use winapi::um::handleapi::*;
+ use winapi::um::processenv::*;
+ use winapi::um::winbase::*;
+ use winapi::um::wincon::*;
+ use winapi::um::winnt::*;
+
+ pub(super) use super::default_err_erase_line as err_erase_line;
+
+ pub fn stderr_width() -> Option<usize> {
+ unsafe {
+ let stdout = GetStdHandle(STD_ERROR_HANDLE);
+ let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
+ if GetConsoleScreenBufferInfo(stdout, &mut csbi) != 0 {
+ return Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize);
+ }
+
+ // On mintty/msys/cygwin based terminals, the above fails with
+ // INVALID_HANDLE_VALUE. Use an alternate method which works
+ // in that case as well.
+ let h = CreateFileA(
+ "CONOUT$\0".as_ptr() as *const CHAR,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ ptr::null_mut(),
+ OPEN_EXISTING,
+ 0,
+ ptr::null_mut(),
+ );
+ if h == INVALID_HANDLE_VALUE {
+ return None;
+ }
+
+ let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
+ let rc = GetConsoleScreenBufferInfo(h, &mut csbi);
+ CloseHandle(h);
+ if rc != 0 {
+ let width = (csbi.srWindow.Right - csbi.srWindow.Left) as usize;
+ // Unfortunately cygwin/mintty does not set the size of the
+ // backing console to match the actual window size. This
+ // always reports a size of 80 or 120 (not sure what
+ // determines that). Use a conservative max of 60 which should
+ // work in most circumstances. ConEmu does some magic to
+ // resize the console correctly, but there's no reasonable way
+ // to detect which kind of terminal we are running in, or if
+ // GetConsoleScreenBufferInfo returns accurate information.
+ return Some(cmp::min(60, width));
+ }
+ None
+ }
+ }
+}
+
+#[cfg(
+ any(
+ all(unix, not(any(target_os = "linux", target_os = "macos"))),
+ windows
+ )
+)]
+fn default_err_erase_line(shell: &mut Shell) {
+ if let Some(max_width) = imp::stderr_width() {
+ let blank = " ".repeat(max_width);
+ drop(write!(shell.err.as_write(), "{}\r", blank));
+ }
+}
diff --git a/third_party b/third_party
-Subproject 4c9d6c9f133cae292528d3b68e55249acd8c1b1
+Subproject 159c954ab0e11f655c7cb2a36302c0261c3e121