mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
feat(core): highlight unprintable chars in permission prompts (#22468)
If we strip out unprintable chars, we don't see the full filename being requested by permission prompts. Instead, we highlight and escape them to make them visible.
This commit is contained in:
parent
9a43a2b495
commit
7e6b942312
2 changed files with 29 additions and 21 deletions
|
@ -11,12 +11,24 @@ use std::io::StderrLock;
|
||||||
use std::io::StdinLock;
|
use std::io::StdinLock;
|
||||||
use std::io::Write as IoWrite;
|
use std::io::Write as IoWrite;
|
||||||
|
|
||||||
/// Helper function to strip ansi codes and ASCII control characters.
|
/// Helper function to make control characters visible so users can see the underlying filename.
|
||||||
fn strip_ansi_codes_and_ascii_control(s: &str) -> std::borrow::Cow<str> {
|
fn escape_control_characters(s: &str) -> std::borrow::Cow<str> {
|
||||||
console_static_text::ansi::strip_ansi_codes(s)
|
if !s.contains(|c: char| c.is_ascii_control() || c.is_control()) {
|
||||||
.chars()
|
return std::borrow::Cow::Borrowed(s);
|
||||||
.filter(|c| !c.is_ascii_control())
|
}
|
||||||
.collect()
|
let mut output = String::with_capacity(s.len() * 2);
|
||||||
|
for c in s.chars() {
|
||||||
|
match c {
|
||||||
|
c if c.is_ascii_control() => output.push_str(
|
||||||
|
&colors::white_bold_on_red(c.escape_debug().to_string()).to_string(),
|
||||||
|
),
|
||||||
|
c if c.is_control() => output.push_str(
|
||||||
|
&colors::white_bold_on_red(c.escape_debug().to_string()).to_string(),
|
||||||
|
),
|
||||||
|
c => output.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const PERMISSION_EMOJI: &str = "⚠️";
|
pub const PERMISSION_EMOJI: &str = "⚠️";
|
||||||
|
@ -249,9 +261,9 @@ impl PermissionPrompter for TtyPrompter {
|
||||||
return PromptResponse::Deny; // don't grant permission if this fails
|
return PromptResponse::Deny; // don't grant permission if this fails
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = strip_ansi_codes_and_ascii_control(message);
|
let message = escape_control_characters(message);
|
||||||
let name = strip_ansi_codes_and_ascii_control(name);
|
let name = escape_control_characters(name);
|
||||||
let api_name = api_name.map(strip_ansi_codes_and_ascii_control);
|
let api_name = api_name.map(escape_control_characters);
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
|
@ -4725,19 +4725,19 @@ fn stdio_streams_are_locked_in_permission_prompt() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn permission_prompt_strips_ansi_codes_and_control_chars() {
|
fn permission_prompt_escapes_ansi_codes_and_control_chars() {
|
||||||
util::with_pty(&["repl"], |mut console| {
|
util::with_pty(&["repl"], |mut console| {
|
||||||
console.write_line(
|
console.write_line(
|
||||||
r#"Deno.permissions.request({ name: "env", variable: "\rDo you like ice cream? y/n" });"#
|
r#"Deno.permissions.request({ name: "env", variable: "\rDo you like ice cream? y/n" });"#
|
||||||
);
|
);
|
||||||
// will be uppercase on windows
|
// will be uppercase on windows
|
||||||
let env_name = if cfg!(windows) {
|
let env_name = if cfg!(windows) {
|
||||||
"DO YOU LIKE ICE CREAM? Y/N"
|
"\\rDO YOU LIKE ICE CREAM? Y/N"
|
||||||
} else {
|
} else {
|
||||||
"Do you like ice cream? y/n"
|
"\\rDo you like ice cream? y/n"
|
||||||
};
|
};
|
||||||
console.expect(format!(
|
console.expect(format!(
|
||||||
"┌ ⚠️ Deno requests env access to \"{}\".",
|
"\u{250c} \u{26a0}\u{fe0f} Deno requests env access to \"{}\".",
|
||||||
env_name
|
env_name
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
@ -4747,14 +4747,10 @@ fn permission_prompt_strips_ansi_codes_and_control_chars() {
|
||||||
console.expect("undefined");
|
console.expect("undefined");
|
||||||
console.write_line_raw(r#"const unboldANSI = "\u001b[22m";"#);
|
console.write_line_raw(r#"const unboldANSI = "\u001b[22m";"#);
|
||||||
console.expect("undefined");
|
console.expect("undefined");
|
||||||
console.write_line_raw(r#"const prompt = `┌ ⚠️ ${boldANSI}Deno requests run access to "echo"${unboldANSI}\n ├ Requested by \`Deno.Command().output()`"#);
|
console.write_line_raw(
|
||||||
console.expect("undefined");
|
r#"new Deno.Command(`${boldANSI}cat${unboldANSI}`).spawn();"#,
|
||||||
console.write_line_raw(r#"const moveANSIUp = "\u001b[1A";"#);
|
);
|
||||||
console.expect("undefined");
|
console.expect("\u{250c} \u{26a0}\u{fe0f} Deno requests run access to \"\\u{1b}[1mcat\\u{1b}[22m\".");
|
||||||
console.write_line_raw(r#"const clearANSI = "\u001b[2K";"#);
|
|
||||||
console.expect("undefined");
|
|
||||||
console.write_line_raw(r#"const moveANSIStart = "\u001b[1000D";"#);
|
|
||||||
console.expect("undefined");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue