mirror of
https://github.com/denoland/deno.git
synced 2025-09-28 21:24:48 +00:00
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.
4c1fa54d10/src/cargo/util/progress.rs
This commit is contained in:
parent
89216c7baa
commit
3a4d88475b
12 changed files with 554 additions and 32 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -219,6 +219,7 @@ dependencies = [
|
||||||
"dirs 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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 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)",
|
"hyper-rustls 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -352,6 +353,15 @@ dependencies = [
|
||||||
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
@ -1551,6 +1561,7 @@ dependencies = [
|
||||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
"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 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 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 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 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"
|
"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d"
|
||||||
|
|
|
@ -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") {
|
rust_rlib("textwrap") {
|
||||||
edition = "2015"
|
edition = "2015"
|
||||||
source_root = "$cargo_home/registry/src/github.com-1ecc6299db9ec823/textwrap-0.11.0/src/lib.rs"
|
source_root = "$cargo_home/registry/src/github.com-1ecc6299db9ec823/textwrap-0.11.0/src/lib.rs"
|
||||||
|
|
|
@ -26,6 +26,7 @@ main_extern_rlib = [
|
||||||
"dirs",
|
"dirs",
|
||||||
"flatbuffers",
|
"flatbuffers",
|
||||||
"futures",
|
"futures",
|
||||||
|
"fwdansi",
|
||||||
"http",
|
"http",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper_rustls",
|
"hyper_rustls",
|
||||||
|
|
|
@ -55,6 +55,7 @@ utime = "0.2.1"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = "0.3.7"
|
winapi = "0.3.7"
|
||||||
|
fwdansi = "1.0.1"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
nix = "0.13.x"
|
nix = "0.13.x"
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// 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::Black;
|
||||||
use ansi_term::Color::Fixed;
|
use ansi_term::Color::Fixed;
|
||||||
use ansi_term::Color::Red;
|
use ansi_term::Color::Red;
|
||||||
|
|
|
@ -180,7 +180,7 @@ pub fn compile_async(
|
||||||
err_check(worker.execute("workerMain()"));
|
err_check(worker.execute("workerMain()"));
|
||||||
err_check(worker.execute("compilerMain()"));
|
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 resource = worker.state.resource.clone();
|
||||||
let compiler_rid = resource.rid;
|
let compiler_rid = resource.rid;
|
||||||
|
|
|
@ -647,9 +647,7 @@ fn fetch_remote_source_async(
|
||||||
) -> impl Future<Item = Option<ModuleMetaData>, Error = DenoError> {
|
) -> impl Future<Item = Option<ModuleMetaData>, Error = DenoError> {
|
||||||
use crate::http_util::FetchOnceResult;
|
use crate::http_util::FetchOnceResult;
|
||||||
|
|
||||||
let download_job = deno_dir
|
let download_job = deno_dir.progress.add("Download", module_name);
|
||||||
.progress
|
|
||||||
.add(format!("Downloading {}", module_name));
|
|
||||||
|
|
||||||
let module_name = module_name.to_owned();
|
let module_name = module_name.to_owned();
|
||||||
let filepath = filepath.to_owned();
|
let filepath = filepath.to_owned();
|
||||||
|
|
19
cli/main.rs
19
cli/main.rs
|
@ -35,6 +35,7 @@ mod progress;
|
||||||
mod repl;
|
mod repl;
|
||||||
pub mod resolve_addr;
|
pub mod resolve_addr;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
mod shell;
|
||||||
mod signal;
|
mod signal;
|
||||||
pub mod source_maps;
|
pub mod source_maps;
|
||||||
mod startup_data;
|
mod startup_data;
|
||||||
|
@ -158,17 +159,15 @@ fn create_worker_and_state(
|
||||||
flags: DenoFlags,
|
flags: DenoFlags,
|
||||||
argv: Vec<String>,
|
argv: Vec<String>,
|
||||||
) -> (Worker, ThreadSafeState) {
|
) -> (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();
|
let progress = Progress::new();
|
||||||
progress.set_callback(|done, completed, total, msg| {
|
progress.set_callback(move |_done, _completed, _total, status, msg| {
|
||||||
if !done {
|
if !status.is_empty() {
|
||||||
eprint!("\r[{}/{}] {}", completed, total, msg);
|
let mut s = shell.lock().unwrap();
|
||||||
eprint!("\x1B[K"); // Clear to end of line.
|
s.status(status, msg).expect("shell problem");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// print empty line only if progress bar was used
|
|
||||||
if done && total > 0 {
|
|
||||||
eprintln!();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let state = ThreadSafeState::new(flags, argv, ops::op_selector_std, progress);
|
let state = ThreadSafeState::new(flags, argv, ops::op_selector_std, progress);
|
||||||
|
|
|
@ -499,7 +499,6 @@ fn check_path_white_list(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#![allow(clippy::cyclomatic_complexity)]
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// Creates vector of strings, Vec<String>
|
// Creates vector of strings, Vec<String>
|
||||||
|
|
|
@ -12,7 +12,7 @@ impl Progress {
|
||||||
|
|
||||||
pub fn set_callback<F>(&self, f: F)
|
pub fn set_callback<F>(&self, f: F)
|
||||||
where
|
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();
|
let mut s = self.0.lock().unwrap();
|
||||||
assert!(s.callback.is_none());
|
assert!(s.callback.is_none());
|
||||||
|
@ -30,11 +30,17 @@ impl Progress {
|
||||||
s.job_names.clone()
|
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 mut s = self.0.lock().unwrap();
|
||||||
let id = s.job_names.len();
|
let id = s.job_names.len();
|
||||||
s.maybe_call_callback(false, s.complete, s.job_names.len() + 1, &name);
|
s.maybe_call_callback(
|
||||||
s.job_names.push(name);
|
false,
|
||||||
|
s.complete,
|
||||||
|
s.job_names.len() + 1,
|
||||||
|
status,
|
||||||
|
name,
|
||||||
|
);
|
||||||
|
s.job_names.push(name.to_string());
|
||||||
Job {
|
Job {
|
||||||
id,
|
id,
|
||||||
inner: self.0.clone(),
|
inner: self.0.clone(),
|
||||||
|
@ -43,11 +49,11 @@ impl Progress {
|
||||||
|
|
||||||
pub fn done(&self) {
|
pub fn done(&self) {
|
||||||
let s = self.0.lock().unwrap();
|
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)]
|
#[derive(Default)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
|
@ -62,10 +68,11 @@ impl Inner {
|
||||||
done: bool,
|
done: bool,
|
||||||
complete: usize,
|
complete: usize,
|
||||||
total: usize,
|
total: usize,
|
||||||
|
status: &str,
|
||||||
msg: &str,
|
msg: &str,
|
||||||
) {
|
) {
|
||||||
if let Some(ref cb) = self.callback {
|
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;
|
s.complete += 1;
|
||||||
let name = &s.job_names[self.id];
|
let name = &s.job_names[self.id];
|
||||||
let (complete, total) = s.progress();
|
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();
|
let p = Progress::new();
|
||||||
assert_eq!(p.progress(), (0, 0));
|
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(), (0, 1));
|
||||||
}
|
}
|
||||||
assert_eq!(p.progress(), (1, 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(), (1, 2));
|
||||||
}
|
}
|
||||||
assert_eq!(p.progress(), (2, 2));
|
assert_eq!(p.progress(), (2, 2));
|
||||||
|
@ -114,8 +121,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn history() {
|
fn history() {
|
||||||
let p = Progress::new();
|
let p = Progress::new();
|
||||||
let _a = p.add("a".to_string());
|
let _a = p.add("status", "a");
|
||||||
let _b = p.add("b".to_string());
|
let _b = p.add("status", "b");
|
||||||
assert_eq!(p.history(), vec!["a", "b"]);
|
assert_eq!(p.history(), vec!["a", "b"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,16 +134,16 @@ mod tests {
|
||||||
let p = Progress::new();
|
let p = Progress::new();
|
||||||
let callback_history_ = callback_history.clone();
|
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);
|
// println!("callback: {}, {}, {}", complete, total, msg);
|
||||||
let mut h = callback_history_.lock().unwrap();
|
let mut h = callback_history_.lock().unwrap();
|
||||||
h.push((complete, total, String::from(msg)));
|
h.push((complete, total, String::from(msg)));
|
||||||
});
|
});
|
||||||
{
|
{
|
||||||
let _a = p.add("a".to_string());
|
let _a = p.add("status", "a");
|
||||||
let _b = p.add("b".to_string());
|
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();
|
let h = callback_history.lock().unwrap();
|
||||||
|
|
491
cli/shell.rs
Normal file
491
cli/shell.rs
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4c9d6c9f133cae292528d3b68e55249acd8c1b16
|
Subproject commit 159c954ab0e11f655c7cb2a36302c0261c3e121b
|
Loading…
Add table
Add a link
Reference in a new issue