mirror of
https://github.com/denoland/deno.git
synced 2025-02-21 12:53:05 -05:00
fix(prompt): better output with control chars (#18108)
This commit is contained in:
parent
2e16551d24
commit
159f444d16
6 changed files with 65 additions and 5 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -525,9 +525,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console_static_text"
|
name = "console_static_text"
|
||||||
version = "0.3.4"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f166cdfb9db0607e2079b382ba64bc4164344006c733b95c1ecfa782a180a34a"
|
checksum = "953d2c3cf53213a4eccdbe8f2e0b49b5d0f77e87a2a9060117bbf9346f92b64e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"vte",
|
"vte",
|
||||||
|
@ -1285,6 +1285,7 @@ name = "deno_runtime"
|
||||||
version = "0.99.0"
|
version = "0.99.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
|
"console_static_text",
|
||||||
"deno_ast",
|
"deno_ast",
|
||||||
"deno_broadcast_channel",
|
"deno_broadcast_channel",
|
||||||
"deno_cache",
|
"deno_cache",
|
||||||
|
|
|
@ -84,6 +84,7 @@ base64 = "=0.13.1"
|
||||||
bencher = "0.1"
|
bencher = "0.1"
|
||||||
bytes = "=1.2.1"
|
bytes = "=1.2.1"
|
||||||
cache_control = "=0.2.0"
|
cache_control = "=0.2.0"
|
||||||
|
console_static_text = "=0.7.1"
|
||||||
data-url = "=0.2.0"
|
data-url = "=0.2.0"
|
||||||
dlopen = "0.1.8"
|
dlopen = "0.1.8"
|
||||||
encoding_rs = "=0.8.31"
|
encoding_rs = "=0.8.31"
|
||||||
|
|
|
@ -62,7 +62,7 @@ chrono = { version = "=0.4.22", default-features = false, features = ["clock"] }
|
||||||
clap = "=3.1.12"
|
clap = "=3.1.12"
|
||||||
clap_complete = "=3.1.2"
|
clap_complete = "=3.1.2"
|
||||||
clap_complete_fig = "=3.1.5"
|
clap_complete_fig = "=3.1.5"
|
||||||
console_static_text = "=0.3.4"
|
console_static_text.workspace = true
|
||||||
data-url.workspace = true
|
data-url.workspace = true
|
||||||
dissimilar = "=1.0.4"
|
dissimilar = "=1.0.4"
|
||||||
dprint-plugin-json = "=0.17.0"
|
dprint-plugin-json = "=0.17.0"
|
||||||
|
|
|
@ -4027,6 +4027,51 @@ fn stdio_streams_are_locked_in_permission_prompt() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn permission_prompt_strips_ansi_codes_and_control_chars() {
|
||||||
|
let _guard = util::http_server();
|
||||||
|
util::with_pty(&["repl"], |mut console| {
|
||||||
|
console.write_line(
|
||||||
|
r#"Deno.permissions.request({ name: "env", variable: "\rDo you like ice cream? y/n" });"#
|
||||||
|
);
|
||||||
|
console.write_line("close();");
|
||||||
|
let output = console.read_all_output();
|
||||||
|
|
||||||
|
assert!(output.contains(
|
||||||
|
"┌ ⚠️ Deno requests env access to \"Do you like ice cream? y/n\"."
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
util::with_pty(&["repl"], |mut console| {
|
||||||
|
console.write_line(
|
||||||
|
r#"
|
||||||
|
const boldANSI = "\u001b[1m" // bold
|
||||||
|
const unboldANSI = "\u001b[22m" // unbold
|
||||||
|
|
||||||
|
const prompt = `┌ ⚠️ ${boldANSI}Deno requests run access to "echo"${unboldANSI}
|
||||||
|
├ Requested by \`Deno.Command().output()`
|
||||||
|
|
||||||
|
const moveANSIUp = "\u001b[1A" // moves to the start of the line
|
||||||
|
const clearANSI = "\u001b[2K" // clears the line
|
||||||
|
const moveANSIStart = "\u001b[1000D" // moves to the start of the line
|
||||||
|
|
||||||
|
Deno[Object.getOwnPropertySymbols(Deno)[0]].core.ops.op_spawn_child({
|
||||||
|
cmd: "cat",
|
||||||
|
args: ["/etc/passwd"],
|
||||||
|
clearEnv: false,
|
||||||
|
env: [],
|
||||||
|
stdin: "null",
|
||||||
|
stdout: "inherit",
|
||||||
|
stderr: "piped"
|
||||||
|
}, moveANSIUp + clearANSI + moveANSIStart + prompt)"#,
|
||||||
|
);
|
||||||
|
console.write_line("close();");
|
||||||
|
let output = console.read_all_output();
|
||||||
|
|
||||||
|
assert!(output.contains(r#"┌ ⚠️ Deno requests run access to "cat""#));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
itest!(node_builtin_modules_ts {
|
itest!(node_builtin_modules_ts {
|
||||||
args: "run --quiet --allow-read run/node_builtin_modules/mod.ts hello there",
|
args: "run --quiet --allow-read run/node_builtin_modules/mod.ts hello there",
|
||||||
output: "run/node_builtin_modules/mod.ts.out",
|
output: "run/node_builtin_modules/mod.ts.out",
|
||||||
|
|
|
@ -86,6 +86,7 @@ deno_websocket.workspace = true
|
||||||
deno_webstorage.workspace = true
|
deno_webstorage.workspace = true
|
||||||
|
|
||||||
atty.workspace = true
|
atty.workspace = true
|
||||||
|
console_static_text.workspace = true
|
||||||
dlopen.workspace = true
|
dlopen.workspace = true
|
||||||
encoding_rs.workspace = true
|
encoding_rs.workspace = true
|
||||||
filetime = "0.2.16"
|
filetime = "0.2.16"
|
||||||
|
|
|
@ -5,6 +5,14 @@ use deno_core::error::AnyError;
|
||||||
use deno_core::parking_lot::Mutex;
|
use deno_core::parking_lot::Mutex;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
/// Helper function to strip ansi codes and ASCII control characters.
|
||||||
|
fn strip_ansi_codes_and_ascii_control(s: &str) -> std::borrow::Cow<str> {
|
||||||
|
console_static_text::strip_ansi_codes(s)
|
||||||
|
.chars()
|
||||||
|
.filter(|c| !c.is_ascii_control())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub const PERMISSION_EMOJI: &str = "⚠️";
|
pub const PERMISSION_EMOJI: &str = "⚠️";
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
@ -203,6 +211,10 @@ impl PermissionPrompter for TtyPrompter {
|
||||||
let _stdout_guard = std::io::stdout().lock();
|
let _stdout_guard = std::io::stdout().lock();
|
||||||
let _stderr_guard = std::io::stderr().lock();
|
let _stderr_guard = std::io::stderr().lock();
|
||||||
|
|
||||||
|
let message = strip_ansi_codes_and_ascii_control(message);
|
||||||
|
let name = strip_ansi_codes_and_ascii_control(name);
|
||||||
|
let api_name = api_name.map(strip_ansi_codes_and_ascii_control);
|
||||||
|
|
||||||
// print to stderr so that if stdout is piped this is still displayed.
|
// print to stderr so that if stdout is piped this is still displayed.
|
||||||
let opts: String = if is_unary {
|
let opts: String = if is_unary {
|
||||||
format!("[y/n/A] (y = yes, allow; n = no, deny; A = allow all {name} permissions)")
|
format!("[y/n/A] (y = yes, allow; n = no, deny; A = allow all {name} permissions)")
|
||||||
|
@ -211,9 +223,9 @@ impl PermissionPrompter for TtyPrompter {
|
||||||
};
|
};
|
||||||
eprint!("┌ {PERMISSION_EMOJI} ");
|
eprint!("┌ {PERMISSION_EMOJI} ");
|
||||||
eprint!("{}", colors::bold("Deno requests "));
|
eprint!("{}", colors::bold("Deno requests "));
|
||||||
eprint!("{}", colors::bold(message));
|
eprint!("{}", colors::bold(message.clone()));
|
||||||
eprintln!("{}", colors::bold("."));
|
eprintln!("{}", colors::bold("."));
|
||||||
if let Some(api_name) = api_name {
|
if let Some(api_name) = api_name.clone() {
|
||||||
eprintln!("├ Requested by `{api_name}` API");
|
eprintln!("├ Requested by `{api_name}` API");
|
||||||
}
|
}
|
||||||
let msg = format!("Run again with --allow-{name} to bypass this prompt.");
|
let msg = format!("Run again with --allow-{name} to bypass this prompt.");
|
||||||
|
|
Loading…
Add table
Reference in a new issue