diff options
-rw-r--r-- | Cargo.lock | 11 | ||||
-rw-r--r-- | build_extra/rust/BUILD.gn | 13 | ||||
-rw-r--r-- | cli/BUILD.gn | 1 | ||||
-rw-r--r-- | cli/Cargo.toml | 1 | ||||
-rw-r--r-- | cli/ansi.rs | 2 | ||||
-rw-r--r-- | cli/compiler.rs | 2 | ||||
-rw-r--r-- | cli/deno_dir.rs | 4 | ||||
-rw-r--r-- | cli/main.rs | 19 | ||||
-rw-r--r-- | cli/permissions.rs | 1 | ||||
-rw-r--r-- | cli/progress.rs | 39 | ||||
-rw-r--r-- | cli/shell.rs | 491 | ||||
m--------- | third_party | 0 |
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 |