summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2024-01-02 09:36:05 +0530
committerGitHub <noreply@github.com>2024-01-02 09:36:05 +0530
commitb21462355a61d69bedf15ae51304719f6014b8df (patch)
treef58b2e00551d9d2981882520d0f64f7e8cf73722
parent96b581bdd2bedb31aa51c0a992686f27ed1a1f10 (diff)
Revert "fix(runtime): Make native modal keyboard interaction consistent with browsers" (#21739)
Reverts denoland/deno#18453 Fixes https://github.com/denoland/deno/issues/21602 https://github.com/denoland/deno/issues/21631 https://github.com/denoland/deno/issues/21641 Reasons for revert: - alert() and confirm() swallowed ^C with raw mode. - prompt() did not re-raise the interrupt signal from rustyline. - Default 'Y' on confirm() is a bad default and breaking change. cc @lionel-rowe
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml1
-rw-r--r--cli/Cargo.toml2
-rw-r--r--cli/tests/integration/run_tests.rs179
-rw-r--r--cli/tests/testdata/run/066_prompt.ts21
-rw-r--r--runtime/Cargo.toml1
-rw-r--r--runtime/js/41_prompt.js107
-rw-r--r--runtime/ops/tty.rs36
8 files changed, 100 insertions, 249 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6b491ac09..f8ef62fc4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1635,7 +1635,6 @@ dependencies = [
"once_cell",
"regex",
"ring",
- "rustyline",
"serde",
"signal-hook-registry",
"termcolor",
@@ -4990,7 +4989,6 @@ dependencies = [
"cfg-if",
"clipboard-win",
"fd-lock",
- "home",
"libc",
"log",
"memchr",
diff --git a/Cargo.toml b/Cargo.toml
index 49c8a6567..b08064b50 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -138,7 +138,6 @@ rustls = "0.21.8"
rustls-pemfile = "1.0.0"
rustls-tokio-stream = "=0.2.16"
rustls-webpki = "0.101.4"
-rustyline = "=13.0.0"
webpki-roots = "0.25.2"
scopeguard = "1.2.0"
saffron = "=0.1.0"
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index ad9974070..dc9bd1228 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -118,7 +118,7 @@ quick-junit = "^0.3.5"
rand = { workspace = true, features = ["small_rng"] }
regex.workspace = true
ring.workspace = true
-rustyline.workspace = true
+rustyline = { version = "=13.0.0", default-features = false, features = ["custom-bindings", "with-file-history"] }
rustyline-derive = "=0.7.0"
serde.workspace = true
serde_repr.workspace = true
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index 16425b14e..43bc212c6 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -2807,155 +2807,40 @@ mod permissions {
fn _066_prompt() {
TestContext::default()
.new_command()
- .args_vec(["repl"])
+ .args_vec(["run", "--quiet", "--unstable", "run/066_prompt.ts"])
.with_pty(|mut console| {
- // alert with no message displays default "Alert"
- // alert displays "[Press any key to continue]"
- // alert can be closed with Enter key
- console.write_line_raw("alert()");
- console.expect("Alert [Press any key to continue]");
- console.write_raw("\r"); // Enter
- console.expect("undefined");
-
- // alert can be closed with Escape key
- console.write_line_raw("alert()");
- console.expect("Alert [Press any key to continue]");
- console.write_raw("\x1b"); // Escape
- console.expect("undefined");
-
- // alert can display custom text
- // alert can be closed with arbitrary keyboard key (x)
- if !cfg!(windows) {
- // it seems to work on windows, just not in the tests
- console.write_line_raw("alert('foo')");
- console.expect("foo [Press any key to continue]");
- console.write_raw("x");
- console.expect("undefined");
- }
-
- // confirm with no message displays default "Confirm"
- // confirm returns true by immediately pressing Enter
- console.write_line_raw("confirm()");
- console.expect("Confirm [Y/n]");
- console.write_raw("\r"); // Enter
- console.expect("true");
-
- // tese seem to work on windows, just not in the tests
- if !cfg!(windows) {
- // confirm returns false by pressing Escape
- console.write_line_raw("confirm()");
- console.expect("Confirm [Y/n]");
- console.write_raw("\x1b"); // Escape
- console.expect("false");
-
- // confirm can display custom text
- // confirm returns true by pressing y
- console.write_line_raw("confirm('continue?')");
- console.expect("continue? [Y/n]");
- console.write_raw("y");
- console.expect("true");
-
- // confirm returns false by pressing n
- console.write_line_raw("confirm('continue?')");
- console.expect("continue? [Y/n]");
- console.write_raw("n");
- console.expect("false");
-
- // confirm can display custom text
- // confirm returns true by pressing Y
- console.write_line_raw("confirm('continue?')");
- console.expect("continue? [Y/n]");
- console.write_raw("Y");
- console.expect("true");
-
- // confirm returns false by pressing N
- console.write_line_raw("confirm('continue?')");
- console.expect("continue? [Y/n]");
- console.write_raw("N");
- console.expect("false");
- }
-
- // prompt with no message displays default "Prompt"
- // prompt returns user-inserted text
- console.write_line_raw("prompt()");
+ console.expect("What is your name? [Jane Doe] ");
+ console.write_line_raw("John Doe");
+ console.expect("Your name is John Doe.");
+ console.expect("What is your name? [Jane Doe] ");
+ console.write_line_raw("");
+ console.expect("Your name is Jane Doe.");
console.expect("Prompt ");
- console.write_line_raw("abc");
- console.expect("\"abc\"");
-
- // prompt can display custom text
- // prompt with no default value returns empty string when immediately pressing Enter
- console.write_line_raw("prompt('foo')");
- console.expect("foo ");
- console.write_raw("\r"); // Enter
- console.expect("\"\"");
-
- // prompt with non-string default value converts it to string
- console.write_line_raw("prompt('foo', 1)");
- console.expect("foo 1");
- console.write_raw("\r"); // Enter
- console.expect("\"1\"");
-
- // prompt with non-string default value that can't be converted throws an error
- console.write_line_raw("prompt('foo', Symbol())");
- console.expect(
- "Uncaught TypeError: Cannot convert a Symbol value to a string",
- );
-
- // prompt with empty-string default value returns empty string when immediately pressing Enter
- console.write_line_raw("prompt('foo', '')");
- console.expect("foo ");
- console.write_raw("\r"); // Enter
- console.expect("\"\"");
-
- // prompt with contentful default value returns default value when immediately pressing Enter
- console.write_line_raw("prompt('foo', 'bar')");
- console.expect("foo bar");
- console.write_raw("\r"); // Enter
- console.expect("\"bar\"");
-
- // prompt with contentful default value allows editing of default value
- console.write_line_raw("prompt('foo', 'bar')");
- console.expect("foo bar");
- console.write_raw("\x1b[D"); // Left arrow
- console.write_raw("\x1b[D"); // Left arrow
- console.write_raw("\x7f"); // Backspace
- console.write_raw("c");
- console.expect("foo car");
- console.write_raw("\r"); // Enter
- console.expect("\"car\"");
-
- // prompt returns null by pressing Escape
- console.write_line_raw("prompt()");
- console.expect("Prompt ");
- console.write_raw("\x1b"); // Escape
- console.expect("null");
-
- #[cfg(not(any(target_os = "macos", target_os = "windows")))]
- {
- // confirm returns false by pressing Ctrl+C
- console.write_line_raw("confirm()");
- console.expect("Confirm [Y/n] ");
- console.write_raw("\x03"); // Ctrl+C
- console.expect("false");
-
- // confirm returns false by pressing Ctrl+D
- console.write_line_raw("confirm()");
- console.expect("Confirm [Y/n] ");
- console.write_raw("\x04"); // Ctrl+D
- console.expect("false");
-
- // prompt returns null by pressing Ctrl+C
- console.write_line_raw("prompt()");
- console.expect("Prompt ");
- console.write_raw("\x03"); // Ctrl+C
- console.expect("null");
-
- // prompt returns null by pressing Ctrl+D
- console.write_line_raw("prompt()");
- console.expect("Prompt ");
- console.write_raw("\x04"); // Ctrl+D
- console.expect("null");
- }
+ console.write_line_raw("foo");
+ console.expect("Your input is foo.");
+ console.expect("Question 0 [y/N] ");
+ console.write_line_raw("Y");
+ console.expect("Your answer is true");
+ console.expect("Question 1 [y/N] ");
+ console.write_line_raw("N");
+ console.expect("Your answer is false");
+ console.expect("Question 2 [y/N] ");
+ console.write_line_raw("yes");
+ console.expect("Your answer is false");
+ console.expect("Confirm [y/N] ");
+ console.write_line("");
+ console.expect("Your answer is false");
+ console.expect("What is Windows EOL? ");
+ console.write_line("windows");
+ console.expect("Your answer is \"windows\"");
+ console.expect("Hi [Enter] ");
+ console.write_line("");
+ console.expect("Alert [Enter] ");
+ console.write_line("");
+ console.expect("The end of test");
+ console.expect("What is EOF? ");
+ console.write_line("");
+ console.expect("Your answer is null");
});
}
diff --git a/cli/tests/testdata/run/066_prompt.ts b/cli/tests/testdata/run/066_prompt.ts
new file mode 100644
index 000000000..e3daa7ac0
--- /dev/null
+++ b/cli/tests/testdata/run/066_prompt.ts
@@ -0,0 +1,21 @@
+const name0 = prompt("What is your name?", "Jane Doe"); // Answer John Doe
+console.log(`Your name is ${name0}.`);
+const name1 = prompt("What is your name?", "Jane Doe"); // Answer with default
+console.log(`Your name is ${name1}.`);
+const input = prompt(); // Answer foo
+console.log(`Your input is ${input}.`);
+const answer0 = confirm("Question 0"); // Answer y
+console.log(`Your answer is ${answer0}`);
+const answer1 = confirm("Question 1"); // Answer n
+console.log(`Your answer is ${answer1}`);
+const answer2 = confirm("Question 2"); // Answer with yes (returns false)
+console.log(`Your answer is ${answer2}`);
+const answer3 = confirm(); // Answer with default
+console.log(`Your answer is ${answer3}`);
+const windows = prompt("What is Windows EOL?");
+console.log(`Your answer is ${JSON.stringify(windows)}`);
+alert("Hi");
+alert();
+console.log("The end of test");
+const eof = prompt("What is EOF?");
+console.log(`Your answer is ${JSON.stringify(eof)}`);
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 456d2f76f..043899ecf 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -112,7 +112,6 @@ notify.workspace = true
once_cell.workspace = true
regex.workspace = true
ring.workspace = true
-rustyline = { workspace = true, features = ["custom-bindings"] }
serde.workspace = true
signal-hook-registry = "1.4.0"
termcolor = "1.1.3"
diff --git a/runtime/js/41_prompt.js b/runtime/js/41_prompt.js
index ca2b82c21..787b9f9f7 100644
--- a/runtime/js/41_prompt.js
+++ b/runtime/js/41_prompt.js
@@ -1,95 +1,78 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { core, primordials } from "ext:core/mod.js";
-const ops = core.ops;
import { isatty } from "ext:runtime/40_tty.js";
import { stdin } from "ext:deno_io/12_io.js";
-import { getNoColor } from "ext:deno_console/01_console.js";
-const { Uint8Array, StringFromCodePoint } = primordials;
-
-const ESC = "\x1b";
-const CTRL_C = "\x03";
-const CTRL_D = "\x04";
-
-const bold = ansi(1, 22);
-const italic = ansi(3, 23);
-const yellow = ansi(33, 0);
-function ansi(start, end) {
- return (str) => getNoColor() ? str : `\x1b[${start}m${str}\x1b[${end}m`;
-}
+const { ArrayPrototypePush, StringPrototypeCharCodeAt, Uint8Array } =
+ primordials;
+const LF = StringPrototypeCharCodeAt("\n", 0);
+const CR = StringPrototypeCharCodeAt("\r", 0);
function alert(message = "Alert") {
if (!isatty(stdin.rid)) {
return;
}
- core.print(
- `${yellow(bold(`${message}`))} [${italic("Press any key to continue")}] `,
- );
-
- try {
- stdin.setRaw(true);
- stdin.readSync(new Uint8Array(1024));
- } finally {
- stdin.setRaw(false);
- }
+ core.print(`${message} [Enter] `, false);
- core.print("\n");
+ readLineFromStdinSync();
}
-function prompt(message = "Prompt", defaultValue = "") {
+function confirm(message = "Confirm") {
if (!isatty(stdin.rid)) {
- return null;
+ return false;
}
- return ops.op_read_line_prompt(
- `${message} `,
- `${defaultValue}`,
- );
+ core.print(`${message} [y/N] `, false);
+
+ const answer = readLineFromStdinSync();
+
+ return answer === "Y" || answer === "y";
}
-const inputMap = new primordials.Map([
- ["Y", true],
- ["y", true],
- ["\r", true],
- ["\n", true],
- ["\r\n", true],
- ["N", false],
- ["n", false],
- [ESC, false],
- [CTRL_C, false],
- [CTRL_D, false],
-]);
+function prompt(message = "Prompt", defaultValue) {
+ defaultValue ??= null;
-function confirm(message = "Confirm") {
if (!isatty(stdin.rid)) {
- return false;
+ return null;
}
- core.print(`${yellow(bold(`${message}`))} [${italic("Y/n")}] `);
+ if (defaultValue) {
+ message += ` [${defaultValue}]`;
+ }
- let val = false;
- try {
- stdin.setRaw(true);
+ message += " ";
- while (true) {
- const b = new Uint8Array(1024);
- stdin.readSync(b);
- let byteString = "";
+ // output in one shot to make the tests more reliable
+ core.print(message, false);
- let i = 0;
- while (b[i]) byteString += StringFromCodePoint(b[i++]);
+ return readLineFromStdinSync() || defaultValue;
+}
- if (inputMap.has(byteString)) {
- val = inputMap.get(byteString);
+function readLineFromStdinSync() {
+ const c = new Uint8Array(1);
+ const buf = [];
+
+ while (true) {
+ const n = stdin.readSync(c);
+ if (n === null || n === 0) {
+ break;
+ }
+ if (c[0] === CR) {
+ const n = stdin.readSync(c);
+ if (c[0] === LF) {
+ break;
+ }
+ ArrayPrototypePush(buf, CR);
+ if (n === null || n === 0) {
break;
}
}
- } finally {
- stdin.setRaw(false);
+ if (c[0] === LF) {
+ break;
+ }
+ ArrayPrototypePush(buf, c[0]);
}
-
- core.print(`${val ? "y" : "n"}\n`);
- return val;
+ return core.decode(new Uint8Array(buf));
}
export { alert, confirm, prompt };
diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs
index 04192b90e..3227ee562 100644
--- a/runtime/ops/tty.rs
+++ b/runtime/ops/tty.rs
@@ -5,13 +5,6 @@ use std::io::Error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::OpState;
-use rustyline::config::Configurer;
-use rustyline::error::ReadlineError;
-use rustyline::Cmd;
-use rustyline::Editor;
-use rustyline::KeyCode;
-use rustyline::KeyEvent;
-use rustyline::Modifiers;
#[cfg(unix)]
use deno_core::ResourceId;
@@ -50,12 +43,7 @@ use winapi::um::wincon;
deno_core::extension!(
deno_tty,
- ops = [
- op_stdin_set_raw,
- op_isatty,
- op_console_size,
- op_read_line_prompt,
- ],
+ ops = [op_stdin_set_raw, op_isatty, op_console_size],
state = |state| {
#[cfg(unix)]
state.put(TtyModeStore::default());
@@ -332,25 +320,3 @@ mod tests {
);
}
}
-
-#[op2]
-#[string]
-pub fn op_read_line_prompt(
- #[string] prompt_text: String,
- #[string] default_value: String,
-) -> Result<Option<String>, AnyError> {
- let mut editor = Editor::<(), rustyline::history::DefaultHistory>::new()
- .expect("Failed to create editor.");
-
- editor.set_keyseq_timeout(1);
- editor
- .bind_sequence(KeyEvent(KeyCode::Esc, Modifiers::empty()), Cmd::Interrupt);
-
- let read_result =
- editor.readline_with_initial(&prompt_text, (&default_value, ""));
- match read_result {
- Ok(line) => Ok(Some(line)),
- Err(ReadlineError::Interrupted | ReadlineError::Eof) => Ok(None),
- Err(err) => Err(err.into()),
- }
-}