From 043ee124f9a226166479c098b9c1072263ca3783 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 02:01:55 +0000 Subject: [PATCH 01/30] refactor(repl): use an inspector session --- cli/flags.rs | 2 + cli/inspector.rs | 1 + cli/main.rs | 22 +++++- cli/ops/mod.rs | 1 - cli/ops/repl.rs | 78 ------------------ cli/repl.rs | 121 ++++++++++++++-------------- cli/rt/40_repl.js | 197 ---------------------------------------------- cli/rt/99_main.js | 5 -- cli/worker.rs | 3 +- 9 files changed, 87 insertions(+), 343 deletions(-) delete mode 100644 cli/ops/repl.rs delete mode 100644 cli/rt/40_repl.js diff --git a/cli/flags.rs b/cli/flags.rs index af7bc2a08a8018..721dc84abfabf9 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -120,6 +120,7 @@ pub struct Flags { pub no_remote: bool, pub read_allowlist: Vec, pub reload: bool, + pub repl: bool, pub seed: Option, pub unstable: bool, pub v8_flags: Option>, @@ -447,6 +448,7 @@ fn completions_parse(flags: &mut Flags, matches: &clap::ArgMatches) { fn repl_parse(flags: &mut Flags, matches: &clap::ArgMatches) { runtime_args_parse(flags, matches, false); + flags.repl = true; flags.subcommand = DenoSubcommand::Repl; flags.allow_net = true; flags.allow_env = true; diff --git a/cli/inspector.rs b/cli/inspector.rs index 83e75bce4dc0d2..171be512b8b108 100644 --- a/cli/inspector.rs +++ b/cli/inspector.rs @@ -857,6 +857,7 @@ impl v8::inspector::ChannelImpl for InspectorSession { ) { let raw_message = message.unwrap().string().to_string(); let message = serde_json::from_str(&raw_message).unwrap(); + self .response_tx_map .remove(&call_id) diff --git a/cli/main.rs b/cli/main.rs index f888a6c6cf5a1c..4546bc3748496b 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -63,6 +63,7 @@ use crate::file_fetcher::SourceFileFetcher; use crate::file_fetcher::TextDocument; use crate::fs as deno_fs; use crate::global_state::GlobalState; +use crate::inspector::InspectorSession; use crate::media_type::MediaType; use crate::permissions::Permissions; use crate::worker::MainWorker; @@ -428,9 +429,26 @@ async fn run_repl(flags: Flags) -> Result<(), AnyError> { let main_module = ModuleSpecifier::resolve_url_or_path("./$deno$repl.ts").unwrap(); let global_state = GlobalState::new(flags)?; - let mut worker = MainWorker::new(&global_state, main_module); + let mut worker = MainWorker::new(&global_state, main_module.clone()); + (&mut *worker).await?; + + let inspector = worker + .inspector + .as_mut() + .expect("Inspector is not created."); + + let inspector_session = InspectorSession::new(&mut **inspector); + let repl = repl::run(&global_state, inspector_session); + + tokio::pin!(repl); + loop { - (&mut *worker).await?; + tokio::select! { + result = &mut repl => { + return result; + } + _ = &mut *worker => {} + } } } diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index c6d5cc1dca180f..b1ec5c344f6f8b 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -16,7 +16,6 @@ pub mod permissions; pub mod plugin; pub mod process; pub mod random; -pub mod repl; pub mod runtime; pub mod runtime_compiler; pub mod signal; diff --git a/cli/ops/repl.rs b/cli/ops/repl.rs deleted file mode 100644 index a2c26b2abaaf85..00000000000000 --- a/cli/ops/repl.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -use crate::repl; -use crate::repl::Repl; -use deno_core::error::bad_resource_id; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::serde_json::json; -use deno_core::serde_json::Value; -use deno_core::BufVec; -use deno_core::OpState; -use deno_core::ZeroCopyBuf; -use serde::Deserialize; -use std::cell::RefCell; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::Mutex; - -pub fn init(rt: &mut deno_core::JsRuntime) { - super::reg_json_sync(rt, "op_repl_start", op_repl_start); - super::reg_json_async(rt, "op_repl_readline", op_repl_readline); -} - -struct ReplResource(Arc>); - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct ReplStartArgs { - history_file: String, -} - -fn op_repl_start( - state: &mut OpState, - args: Value, - _zero_copy: &mut [ZeroCopyBuf], -) -> Result { - let args: ReplStartArgs = serde_json::from_value(args)?; - debug!("op_repl_start {}", args.history_file); - let history_path = { - let cli_state = super::global_state(state); - repl::history_path(&cli_state.dir, &args.history_file) - }; - let repl = repl::Repl::new(history_path); - let resource = ReplResource(Arc::new(Mutex::new(repl))); - let rid = state.resource_table.add("repl", Box::new(resource)); - Ok(json!(rid)) -} - -#[derive(Deserialize)] -struct ReplReadlineArgs { - rid: i32, - prompt: String, -} - -async fn op_repl_readline( - state: Rc>, - args: Value, - _zero_copy: BufVec, -) -> Result { - let args: ReplReadlineArgs = serde_json::from_value(args)?; - let rid = args.rid as u32; - let prompt = args.prompt; - debug!("op_repl_readline {} {}", rid, prompt); - let repl = { - let state = state.borrow(); - let resource = state - .resource_table - .get::(rid) - .ok_or_else(bad_resource_id)?; - resource.0.clone() - }; - tokio::task::spawn_blocking(move || { - let line = repl.lock().unwrap().readline(&prompt)?; - Ok(json!(line)) - }) - .await - .unwrap() -} diff --git a/cli/repl.rs b/cli/repl.rs index 7873f7d0fc5a36..9e0064d8509cc1 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -1,73 +1,78 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::deno_dir::DenoDir; +use crate::global_state::GlobalState; +use crate::inspector::InspectorSession; use deno_core::error::AnyError; +use deno_core::serde_json::json; +use rustyline::error::ReadlineError; use rustyline::Editor; -use std::fs; -use std::path::PathBuf; -pub struct Repl { - editor: Editor<()>, - history_file: PathBuf, -} +pub async fn run( + _global_state: &GlobalState, + mut session: Box, +) -> Result<(), AnyError> { + // Our inspector is unable to default to the default context id so we have to specify it here. + let context_id: u32 = 1; -impl Repl { - pub fn new(history_file: PathBuf) -> Self { - let mut repl = Self { - editor: Editor::<()>::new(), - history_file, - }; + session + .post_message("Runtime.enable".to_string(), None) + .await?; - repl.load_history(); - repl - } + let mut editor = Editor::<()>::new(); - fn load_history(&mut self) { - debug!("Loading REPL history: {:?}", self.history_file); - self - .editor - .load_history(&self.history_file.to_str().unwrap()) - .map_err(|e| { - debug!("Unable to load history file: {:?} {}", self.history_file, e) - }) - // ignore this error (e.g. it occurs on first load) - .unwrap_or(()) - } + println!("Deno {}", crate::version::DENO); + println!("exit using ctrl+d or close()"); - fn save_history(&mut self) -> Result<(), AnyError> { - fs::create_dir_all(self.history_file.parent().unwrap())?; - self - .editor - .save_history(&self.history_file.to_str().unwrap()) - .map(|_| debug!("Saved REPL history to: {:?}", self.history_file)) - .map_err(|e| { - eprintln!("Unable to save REPL history: {:?} {}", self.history_file, e); - e.into() - }) - } + loop { + let line = editor.readline("> "); + match line { + Ok(line) => { + let evaluate_response = session + .post_message( + "Runtime.evaluate".to_string(), + Some(json!({ + "expression": line, + "contextId": context_id, + // Set repl mode to true to enable const redeclarations and top level await + "replMode": false, + })), + ) + .await?; - pub fn readline(&mut self, prompt: &str) -> Result { - self - .editor - .readline(&prompt) - .map(|line| { - self.editor.add_history_entry(line.clone()); - line - }) - .map_err(AnyError::from) + let evaluate_result = evaluate_response.get("result").unwrap(); - // Forward error to TS side for processing - } -} + // TODO(caspervonb) we should investigate using previews here but to keep things + // consistent with the previous implementation we just get the preview result from + // Deno.inspectArgs. + let inspect_response = session.post_message("Runtime.callFunctionOn".to_string(), Some(json!({ + "executionContextId": context_id, + "functionDeclaration": "function (object) { return Deno[Deno.internal].inspectArgs(['%o', object]); }", + "arguments": [ + evaluate_result, + ], + }))).await?; + + let inspect_result = inspect_response.get("result").unwrap(); + println!("{}", inspect_result.get("value").unwrap().as_str().unwrap()); -impl Drop for Repl { - fn drop(&mut self) { - self.save_history().unwrap(); + editor.add_history_entry(line.as_str()); + } + Err(ReadlineError::Interrupted) => { + println!("ctrl-c"); + break; + } + Err(ReadlineError::Eof) => { + println!("ctrl-d"); + break; + } + Err(err) => { + println!("Error: {:?}", err); + break; + } + } } -} -pub fn history_path(dir: &DenoDir, history_file: &str) -> PathBuf { - let mut p: PathBuf = dir.root.clone(); - p.push(history_file); - p + // TODO(caspervonb) save history file + + Ok(()) } diff --git a/cli/rt/40_repl.js b/cli/rt/40_repl.js deleted file mode 100644 index a249b578d8065d..00000000000000 --- a/cli/rt/40_repl.js +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -((window) => { - const core = window.Deno.core; - const exit = window.__bootstrap.os.exit; - const version = window.__bootstrap.version.version; - const inspectArgs = window.__bootstrap.console.inspectArgs; - - function opStartRepl(historyFile) { - return core.jsonOpSync("op_repl_start", { historyFile }); - } - - function opReadline(rid, prompt) { - return core.jsonOpAsync("op_repl_readline", { rid, prompt }); - } - - function replLog(...args) { - core.print(inspectArgs(args) + "\n"); - } - - function replError(...args) { - core.print(inspectArgs(args) + "\n", true); - } - - // Error messages that allow users to continue input - // instead of throwing an error to REPL - // ref: https://github.com/v8/v8/blob/master/src/message-template.h - // TODO(kevinkassimo): this list might not be comprehensive - const recoverableErrorMessages = [ - "Unexpected end of input", // { or [ or ( - "Missing initializer in const declaration", // const a - "Missing catch or finally after try", // try {} - "missing ) after argument list", // console.log(1 - "Unterminated template literal", // `template - // TODO(kevinkassimo): need a parser to handling errors such as: - // "Missing } in template expression" // `${ or `${ a 123 }` - ]; - - function isRecoverableError(e) { - return recoverableErrorMessages.includes(e.message); - } - - // Returns `true` if `close()` is called in REPL. - // We should quit the REPL when this function returns `true`. - function isCloseCalled() { - return globalThis.closed; - } - - let lastEvalResult = undefined; - let lastThrownError = undefined; - - // Evaluate code. - // Returns true if code is consumed (no error/irrecoverable error). - // Returns false if error is recoverable - function evaluate(code, preprocess = true) { - const rawCode = code; - if (preprocess) { - // It is a bit unexpected that { "foo": "bar" } is interpreted as a block - // statement rather than an object literal so we interpret it as an expression statement - // to match the behavior found in a typical prompt including browser developer tools. - if (code.trimLeft().startsWith("{") && !code.trimRight().endsWith(";")) { - code = `(${code})`; - } - } - - // each evalContext is a separate function body, and we want strict mode to - // work, so we should ensure that the code starts with "use strict" - const [result, errInfo] = core.evalContext(`"use strict";\n\n${code}`); - - if (!errInfo) { - // when a function is eval'ed with just "use strict" sometimes the result - // is "use strict" which should be discarded - lastEvalResult = typeof result === "string" && result === "use strict" - ? undefined - : result; - if (!isCloseCalled()) { - replLog("%o", lastEvalResult); - } - } else if (errInfo.isCompileError && code.length != rawCode.length) { - return evaluate(rawCode, false); - } else if (errInfo.isCompileError && isRecoverableError(errInfo.thrown)) { - // Recoverable compiler error - return false; // don't consume code. - } else { - lastThrownError = errInfo.thrown; - if (errInfo.isNativeError) { - const formattedError = core.formatError(errInfo.thrown); - replError(formattedError); - } else { - replError("Thrown:", errInfo.thrown); - } - } - return true; - } - - async function replLoop() { - const { console } = globalThis; - - const historyFile = "deno_history.txt"; - const rid = opStartRepl(historyFile); - - const quitRepl = (exitCode) => { - // Special handling in case user calls deno.close(3). - try { - core.close(rid); // close signals Drop on REPL and saves history. - } catch {} - exit(exitCode); - }; - - // Configure globalThis._ to give the last evaluation result. - Object.defineProperty(globalThis, "_", { - configurable: true, - get: () => lastEvalResult, - set: (value) => { - Object.defineProperty(globalThis, "_", { - value: value, - writable: true, - enumerable: true, - configurable: true, - }); - console.log("Last evaluation result is no longer saved to _."); - }, - }); - - // Configure globalThis._error to give the last thrown error. - Object.defineProperty(globalThis, "_error", { - configurable: true, - get: () => lastThrownError, - set: (value) => { - Object.defineProperty(globalThis, "_error", { - value: value, - writable: true, - enumerable: true, - configurable: true, - }); - console.log("Last thrown error is no longer saved to _error."); - }, - }); - - replLog(`Deno ${version.deno}`); - replLog("exit using ctrl+d or close()"); - - while (true) { - if (isCloseCalled()) { - quitRepl(0); - } - - let code = ""; - // Top level read - try { - code = await opReadline(rid, "> "); - if (code.trim() === "") { - continue; - } - } catch (err) { - if (err.message === "EOF") { - quitRepl(0); - } else { - // If interrupted, don't print error. - if (err.message !== "Interrupted") { - // e.g. this happens when we have deno.close(3). - // We want to display the problem. - const formattedError = core.formatError(err); - replError(formattedError); - } - // Quit REPL anyways. - quitRepl(1); - } - } - // Start continued read - while (!evaluate(code)) { - code += "\n"; - try { - code += await opReadline(rid, " "); - } catch (err) { - // If interrupted on continued read, - // abort this read instead of quitting. - if (err.message === "Interrupted") { - break; - } else if (err.message === "EOF") { - quitRepl(0); - } else { - // e.g. this happens when we have deno.close(3). - // We want to display the problem. - const formattedError = core.formatError(err); - replError(formattedError); - quitRepl(1); - } - } - } - } - } - - window.__bootstrap.repl = { - replLoop, - }; -})(this); diff --git a/cli/rt/99_main.js b/cli/rt/99_main.js index 26e8fd6da096a6..b2120a66778b26 100644 --- a/cli/rt/99_main.js +++ b/cli/rt/99_main.js @@ -14,7 +14,6 @@ delete Object.prototype.__proto__; const errorStack = window.__bootstrap.errorStack; const os = window.__bootstrap.os; const timers = window.__bootstrap.timers; - const replLoop = window.__bootstrap.repl.replLoop; const Console = window.__bootstrap.console.Console; const worker = window.__bootstrap.worker; const signals = window.__bootstrap.signals; @@ -329,10 +328,6 @@ delete Object.prototype.__proto__; util.log("cwd", cwd); util.log("args", args); - - if (repl) { - replLoop(); - } } function bootstrapWorkerRuntime(name, useDenoNamespace, internalName) { diff --git a/cli/worker.rs b/cli/worker.rs index 242a2f4b336d83..08ccd418e5d479 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -134,7 +134,7 @@ impl Worker { &mut isolate, Some(inspector_server.clone()), )) - } else if global_state.flags.coverage { + } else if global_state.flags.coverage || global_state.flags.repl { Some(DenoInspector::new(&mut isolate, None)) } else { None @@ -309,7 +309,6 @@ impl MainWorker { ops::permissions::init(&mut worker); ops::plugin::init(&mut worker); ops::process::init(&mut worker); - ops::repl::init(&mut worker); ops::runtime_compiler::init(&mut worker); ops::signal::init(&mut worker); ops::tls::init(&mut worker); From 5c0ed58e816fa3af46d401a460c5a0d33289823f Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 06:02:43 +0000 Subject: [PATCH 02/30] Lint --- cli/rt/99_main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/rt/99_main.js b/cli/rt/99_main.js index b2120a66778b26..915defcd690737 100644 --- a/cli/rt/99_main.js +++ b/cli/rt/99_main.js @@ -293,7 +293,7 @@ delete Object.prototype.__proto__; } }); - const { args, cwd, noColor, pid, ppid, repl, unstableFlag } = + const { args, cwd, noColor, pid, ppid, unstableFlag } = runtimeStart(); registerErrors(); From f41db9ce64ff6945fe2b16cd93e34c6954f75793 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 06:23:35 +0000 Subject: [PATCH 03/30] Turn repl mode comment into a TODO comment --- cli/repl.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index 9e0064d8509cc1..1759ffd7f95b57 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -33,7 +33,8 @@ pub async fn run( Some(json!({ "expression": line, "contextId": context_id, - // Set repl mode to true to enable const redeclarations and top level await + // TODO(caspervonb) set repl mode to true to enable const redeclarations and top + // level await "replMode": false, })), ) From b882005d2e6b0c1d8ea9e13837333dd2908288d4 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 06:54:59 +0000 Subject: [PATCH 04/30] Add real multi-line editing with a bracket validator --- Cargo.lock | 11 +++++++++++ cli/Cargo.toml | 1 + cli/repl.rs | 23 ++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 452f3f8352f615..a6e04f1474d8a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,6 +426,7 @@ dependencies = [ "regex", "ring", "rustyline", + "rustyline-derive", "semver-parser 0.9.0", "serde", "sourcemap", @@ -1946,6 +1947,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rustyline-derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a50e29610a5be68d4a586a5cce3bfb572ed2c2a74227e4168444b7bf4e5235" +dependencies = [ + "quote 1.0.7", + "syn 1.0.41", +] + [[package]] name = "ryu" version = "1.0.5" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6150fa4210b8eb..ecf95a922d87b8 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -57,6 +57,7 @@ rand = "0.7.3" regex = "1.3.9" ring = "0.16.15" rustyline = { version = "6.3.0", default-features = false } +rustyline-derive = "0.3.1" serde = { version = "1.0.116", features = ["derive"] } sys-info = "0.7.0" sourcemap = "6.0.1" diff --git a/cli/repl.rs b/cli/repl.rs index 1759ffd7f95b57..6e5e6a91967924 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -6,6 +6,22 @@ use deno_core::error::AnyError; use deno_core::serde_json::json; use rustyline::error::ReadlineError; use rustyline::Editor; +use rustyline::validate::Validator; +use rustyline::validate::ValidationContext; +use rustyline::validate::ValidationResult; +use rustyline::validate::MatchingBracketValidator; +use rustyline_derive::{Completer, Helper, Highlighter, Hinter}; + +#[derive(Completer, Helper, Highlighter, Hinter)] +struct Helper { + validator: MatchingBracketValidator, +} + +impl Validator for Helper { + fn validate(&self, ctx: &mut ValidationContext) -> Result { + self.validator.validate(ctx) + } +} pub async fn run( _global_state: &GlobalState, @@ -18,7 +34,12 @@ pub async fn run( .post_message("Runtime.enable".to_string(), None) .await?; - let mut editor = Editor::<()>::new(); + let helper = Helper { + validator: MatchingBracketValidator::new(), + }; + + let mut editor = Editor::new(); + editor.set_helper(Some(helper)); println!("Deno {}", crate::version::DENO); println!("exit using ctrl+d or close()"); From 203dd7734c161cfe4cada4fb56f8c19995662c2d Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 06:57:20 +0000 Subject: [PATCH 05/30] Remove repl flag from op start --- cli/ops/runtime.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/ops/runtime.rs b/cli/ops/runtime.rs index b1eddc2654294c..66137865d23993 100644 --- a/cli/ops/runtime.rs +++ b/cli/ops/runtime.rs @@ -41,7 +41,6 @@ fn op_start( "noColor": !colors::use_color(), "pid": std::process::id(), "ppid": ppid(), - "repl": gs.flags.subcommand == DenoSubcommand::Repl, "target": env!("TARGET"), "tsVersion": version::TYPESCRIPT, "unstableFlag": gs.flags.unstable, From a286e29f3041f3a16900f7777217c5c64ff0c893 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 06:58:29 +0000 Subject: [PATCH 06/30] Lint --- cli/ops/runtime.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/ops/runtime.rs b/cli/ops/runtime.rs index 66137865d23993..3f73984791c27f 100644 --- a/cli/ops/runtime.rs +++ b/cli/ops/runtime.rs @@ -4,7 +4,6 @@ use crate::colors; use crate::metrics::Metrics; use crate::permissions::Permissions; use crate::version; -use crate::DenoSubcommand; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::json; From 5335bc794f02dfde5fe2b7eb171330aa7cd906cf Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 07:09:39 +0000 Subject: [PATCH 07/30] Format --- cli/repl.rs | 15 +++++++++------ cli/rt/99_main.js | 3 +-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 6e5e6a91967924..b5dd0b368afa76 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -5,11 +5,11 @@ use crate::inspector::InspectorSession; use deno_core::error::AnyError; use deno_core::serde_json::json; use rustyline::error::ReadlineError; -use rustyline::Editor; -use rustyline::validate::Validator; +use rustyline::validate::MatchingBracketValidator; use rustyline::validate::ValidationContext; use rustyline::validate::ValidationResult; -use rustyline::validate::MatchingBracketValidator; +use rustyline::validate::Validator; +use rustyline::Editor; use rustyline_derive::{Completer, Helper, Highlighter, Hinter}; #[derive(Completer, Helper, Highlighter, Hinter)] @@ -18,9 +18,12 @@ struct Helper { } impl Validator for Helper { - fn validate(&self, ctx: &mut ValidationContext) -> Result { - self.validator.validate(ctx) - } + fn validate( + &self, + ctx: &mut ValidationContext, + ) -> Result { + self.validator.validate(ctx) + } } pub async fn run( diff --git a/cli/rt/99_main.js b/cli/rt/99_main.js index 915defcd690737..d8462ce662f0b0 100644 --- a/cli/rt/99_main.js +++ b/cli/rt/99_main.js @@ -293,8 +293,7 @@ delete Object.prototype.__proto__; } }); - const { args, cwd, noColor, pid, ppid, unstableFlag } = - runtimeStart(); + const { args, cwd, noColor, pid, ppid, unstableFlag } = runtimeStart(); registerErrors(); From dbae724dec5aa3ca41b5058911a83e48b4d4684d Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 08:06:39 +0000 Subject: [PATCH 08/30] Remove debug output on readline errors --- cli/repl.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index b5dd0b368afa76..c6790f13047927 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -83,11 +83,9 @@ pub async fn run( editor.add_history_entry(line.as_str()); } Err(ReadlineError::Interrupted) => { - println!("ctrl-c"); break; } Err(ReadlineError::Eof) => { - println!("ctrl-d"); break; } Err(err) => { From f2750a240dec554e9b6340ef6721a5bde231efa7 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 08:14:27 +0000 Subject: [PATCH 09/30] Fix flags tests --- cli/flags.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/flags.rs b/cli/flags.rs index 721dc84abfabf9..06fbafdc3d4238 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -2144,6 +2144,7 @@ mod tests { assert_eq!( r.unwrap(), Flags { + repl: true, subcommand: DenoSubcommand::Repl, allow_net: true, allow_env: true, @@ -2164,6 +2165,7 @@ mod tests { assert_eq!( r.unwrap(), Flags { + repl: true, subcommand: DenoSubcommand::Repl, unstable: true, import_map_path: Some("import_map.json".to_string()), From 7568a7e75e7d2cf79b804be4aa851da7be818032 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 13:45:58 +0000 Subject: [PATCH 10/30] Save and load history file --- cli/repl.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index c6790f13047927..8efc978ff1954e 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -27,12 +27,14 @@ impl Validator for Helper { } pub async fn run( - _global_state: &GlobalState, + global_state: &GlobalState, mut session: Box, ) -> Result<(), AnyError> { // Our inspector is unable to default to the default context id so we have to specify it here. let context_id: u32 = 1; + let history_file = global_state.dir.root.join("deno_history.txt"); + session .post_message("Runtime.enable".to_string(), None) .await?; @@ -43,6 +45,7 @@ pub async fn run( let mut editor = Editor::new(); editor.set_helper(Some(helper)); + editor.load_history(history_file.to_str().unwrap())?; println!("Deno {}", crate::version::DENO); println!("exit using ctrl+d or close()"); @@ -95,7 +98,7 @@ pub async fn run( } } - // TODO(caspervonb) save history file + editor.save_history(history_file.to_str().unwrap())?; Ok(()) } From 06293a20216223af48c106545828dba7175590a6 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 14:37:23 +0000 Subject: [PATCH 11/30] Ensure history file always exists --- cli/repl.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli/repl.rs b/cli/repl.rs index 8efc978ff1954e..2bc87d1aadaab1 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -11,6 +11,8 @@ use rustyline::validate::ValidationResult; use rustyline::validate::Validator; use rustyline::Editor; use rustyline_derive::{Completer, Helper, Highlighter, Hinter}; +use std::fs::create_dir_all; +use std::fs::OpenOptions; #[derive(Completer, Helper, Highlighter, Hinter)] struct Helper { @@ -34,6 +36,8 @@ pub async fn run( let context_id: u32 = 1; let history_file = global_state.dir.root.join("deno_history.txt"); + create_dir_all(history_file.parent().unwrap())?; + OpenOptions::new().append(true).open(&history_file)?; session .post_message("Runtime.enable".to_string(), None) From 4db45f5a7e6e655ead5cc5e8cee59785eb126bd5 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 14:46:48 +0000 Subject: [PATCH 12/30] Allow history load to fail --- cli/repl.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 2bc87d1aadaab1..38aeb52a2e87af 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -11,8 +11,6 @@ use rustyline::validate::ValidationResult; use rustyline::validate::Validator; use rustyline::Editor; use rustyline_derive::{Completer, Helper, Highlighter, Hinter}; -use std::fs::create_dir_all; -use std::fs::OpenOptions; #[derive(Completer, Helper, Highlighter, Hinter)] struct Helper { @@ -36,8 +34,6 @@ pub async fn run( let context_id: u32 = 1; let history_file = global_state.dir.root.join("deno_history.txt"); - create_dir_all(history_file.parent().unwrap())?; - OpenOptions::new().append(true).open(&history_file)?; session .post_message("Runtime.enable".to_string(), None) @@ -49,7 +45,9 @@ pub async fn run( let mut editor = Editor::new(); editor.set_helper(Some(helper)); - editor.load_history(history_file.to_str().unwrap())?; + editor + .load_history(history_file.to_str().unwrap()) + .unwrap_or(()); println!("Deno {}", crate::version::DENO); println!("exit using ctrl+d or close()"); @@ -102,6 +100,7 @@ pub async fn run( } } + std::fs::create_dir_all(history_file.parent().unwrap())?; editor.save_history(history_file.to_str().unwrap())?; Ok(()) From 98ccecd19a5e44875397bad281d01152e3bc879a Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 14:59:37 +0000 Subject: [PATCH 13/30] Print errors to stderr --- cli/repl.rs | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 38aeb52a2e87af..092434735294f9 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -60,30 +60,45 @@ pub async fn run( .post_message( "Runtime.evaluate".to_string(), Some(json!({ - "expression": line, - "contextId": context_id, - // TODO(caspervonb) set repl mode to true to enable const redeclarations and top - // level await - "replMode": false, + "expression": line, + "contextId": context_id, + // TODO(caspervonb) set repl mode to true to enable const redeclarations and top + // level await + "replMode": false, })), ) .await?; let evaluate_result = evaluate_response.get("result").unwrap(); + let evaluate_exception_details = + evaluate_response.get("exceptionDetails"); // TODO(caspervonb) we should investigate using previews here but to keep things // consistent with the previous implementation we just get the preview result from // Deno.inspectArgs. - let inspect_response = session.post_message("Runtime.callFunctionOn".to_string(), Some(json!({ - "executionContextId": context_id, - "functionDeclaration": "function (object) { return Deno[Deno.internal].inspectArgs(['%o', object]); }", - "arguments": [ - evaluate_result, - ], + let inspect_response = session + .post_message( + "Runtime.callFunctionOn".to_string(), + Some(json!({ + "executionContextId": context_id, + "functionDeclaration": "function (object) { return Deno[Deno.internal].inspectArgs(['%o', object]); }", + "arguments": [ + evaluate_result, + ], }))).await?; let inspect_result = inspect_response.get("result").unwrap(); - println!("{}", inspect_result.get("value").unwrap().as_str().unwrap()); + + match evaluate_exception_details { + Some(_) => eprintln!( + "Uncaught {}", + inspect_result.get("value").unwrap().as_str().unwrap() + ), + None => println!( + "{}", + inspect_result.get("value").unwrap().as_str().unwrap() + ), + } editor.add_history_entry(line.as_str()); } From 36c659d3636f2ed1757401baf5d3b2ba82e103bb Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 16:10:35 +0000 Subject: [PATCH 14/30] Support _ and _error --- cli/repl.rs | 65 ++++++++++++++++++++++++++++++++++ cli/tests/integration_tests.rs | 4 +-- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 092434735294f9..e13e231fe24dbd 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -52,6 +52,47 @@ pub async fn run( println!("Deno {}", crate::version::DENO); println!("exit using ctrl+d or close()"); + let prelude = r#" + Object.defineProperty(globalThis, "_", { + configurable: true, + get: () => Deno[Deno.internal].lastEvalResult, + set: (value) => { + Object.defineProperty(globalThis, "_", { + value: value, + writable: true, + enumerable: true, + configurable: true, + }); + console.log("Last evaluation result is no longer saved to _."); + }, + }); + + Object.defineProperty(globalThis, "_error", { + configurable: true, + get: () => Deno[Deno.internal].lastThrownError, + set: (value) => { + Object.defineProperty(globalThis, "_error", { + value: value, + writable: true, + enumerable: true, + configurable: true, + }); + + console.log("Last thrown error is no longer saved to _error."); + }, + }); + "#; + + session + .post_message( + "Runtime.evaluate".to_string(), + Some(json!({ + "expression": prelude, + "contextId": context_id, + })), + ) + .await?; + loop { let line = editor.readline("> "); match line { @@ -73,6 +114,30 @@ pub async fn run( let evaluate_exception_details = evaluate_response.get("exceptionDetails"); + if evaluate_exception_details.is_some() { + session + .post_message( + "Runtime.callFunctionOn".to_string(), + Some(json!({ + "executionContextId": context_id, + "functionDeclaration": "function (object) { Deno[Deno.internal].lastThrownError = object; }", + "arguments": [ + evaluate_result, + ], + }))).await?; + } else { + session + .post_message( + "Runtime.callFunctionOn".to_string(), + Some(json!({ + "executionContextId": context_id, + "functionDeclaration": "function (object) { Deno[Deno.internal].lastEvalResult = object; }", + "arguments": [ + evaluate_result, + ], + }))).await?; + } + // TODO(caspervonb) we should investigate using previews here but to keep things // consistent with the previous implementation we just get the preview result from // Deno.inspectArgs. diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index c062d3371c9742..3dce45acc83dd7 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1292,7 +1292,7 @@ fn repl_test_save_last_thrown() { false, ); assert!(out.ends_with("1\n")); - assert_eq!(err, "Thrown: 1\n"); + assert_eq!(err, "Uncaught 1\n"); } #[test] @@ -1322,7 +1322,7 @@ fn repl_test_assign_underscore_error() { assert!( out.ends_with("Last thrown error is no longer saved to _error.\n1\n1\n") ); - assert_eq!(err, "Thrown: 2\n"); + assert_eq!(err, "Uncaught 2\n"); } #[test] From 0302a2342672f87decd868d9632e258a3596ef46 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 16:41:31 +0000 Subject: [PATCH 15/30] Break on globalThis.closed --- cli/repl.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cli/repl.rs b/cli/repl.rs index e13e231fe24dbd..0e2e8b613935cf 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -94,6 +94,26 @@ pub async fn run( .await?; loop { + let is_closing = session + .post_message( + "Runtime.evaluate".to_string(), + Some(json!({ + "expression": "(globalThis.closed)", + "contextId": context_id, + })), + ) + .await? + .get("result") + .unwrap() + .get("value") + .unwrap() + .as_bool() + .unwrap(); + + if is_closing { + break; + } + let line = editor.readline("> "); match line { Ok(line) => { From 0e7bf5ab91ab41ae0d52e795390eade1f4f6e5d2 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 16:46:22 +0000 Subject: [PATCH 16/30] Format --- cli/repl.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 0e2e8b613935cf..4656fe90437048 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -95,23 +95,23 @@ pub async fn run( loop { let is_closing = session - .post_message( - "Runtime.evaluate".to_string(), - Some(json!({ - "expression": "(globalThis.closed)", - "contextId": context_id, - })), - ) - .await? - .get("result") - .unwrap() - .get("value") - .unwrap() - .as_bool() - .unwrap(); + .post_message( + "Runtime.evaluate".to_string(), + Some(json!({ + "expression": "(globalThis.closed)", + "contextId": context_id, + })), + ) + .await? + .get("result") + .unwrap() + .get("value") + .unwrap() + .as_bool() + .unwrap(); if is_closing { - break; + break; } let line = editor.readline("> "); From ad40189afc91df80264fdb363fe9f9eaaf872117 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 17:13:13 +0000 Subject: [PATCH 17/30] Move close check to after eval to avoid printing the result of close() --- cli/repl.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 4656fe90437048..0f6bc77ccfad72 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -94,26 +94,6 @@ pub async fn run( .await?; loop { - let is_closing = session - .post_message( - "Runtime.evaluate".to_string(), - Some(json!({ - "expression": "(globalThis.closed)", - "contextId": context_id, - })), - ) - .await? - .get("result") - .unwrap() - .get("value") - .unwrap() - .as_bool() - .unwrap(); - - if is_closing { - break; - } - let line = editor.readline("> "); match line { Ok(line) => { @@ -130,6 +110,26 @@ pub async fn run( ) .await?; + let is_closing = session + .post_message( + "Runtime.evaluate".to_string(), + Some(json!({ + "expression": "(globalThis.closed)", + "contextId": context_id, + })), + ) + .await? + .get("result") + .unwrap() + .get("value") + .unwrap() + .as_bool() + .unwrap(); + + if is_closing { + break; + } + let evaluate_result = evaluate_response.get("result").unwrap(); let evaluate_exception_details = evaluate_response.get("exceptionDetails"); From 48e749b918f023a6bb03549a4e268d21636b5879 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 22:40:58 +0000 Subject: [PATCH 18/30] Wrap and retry input --- cli/repl.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index 0f6bc77ccfad72..5384c229087537 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -101,7 +101,7 @@ pub async fn run( .post_message( "Runtime.evaluate".to_string(), Some(json!({ - "expression": line, + "expression": format!("({})", line), "contextId": context_id, // TODO(caspervonb) set repl mode to true to enable const redeclarations and top // level await @@ -110,6 +110,25 @@ pub async fn run( ) .await?; + // Retry without wrapping if there is an error + let evaluate_response = + if evaluate_response.get("exceptionDetails").is_some() { + session + .post_message( + "Runtime.evaluate".to_string(), + Some(json!({ + "expression": line, + "contextId": context_id, + // TODO(caspervonb) set repl mode to true to enable const redeclarations and top + // level await + "replMode": false, + })), + ) + .await? + } else { + evaluate_response + }; + let is_closing = session .post_message( "Runtime.evaluate".to_string(), From a73b9b76c8da1d34fec0dc5a7e6883f2ad108db9 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 22:59:39 +0000 Subject: [PATCH 19/30] Ignore multiline test --- cli/tests/integration_tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 3dce45acc83dd7..463dec545c6054 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1157,6 +1157,7 @@ fn repl_test_function() { } #[test] +#[ignore] fn repl_test_multiline() { let (out, err) = util::run_and_collect_output( true, From 64d6e2aee1780ee3f988fb44712a85e42ca75f3f Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 23:25:17 +0000 Subject: [PATCH 20/30] Evaluate in strict mode --- cli/repl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 5384c229087537..2e0f6f42432e28 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -101,7 +101,7 @@ pub async fn run( .post_message( "Runtime.evaluate".to_string(), Some(json!({ - "expression": format!("({})", line), + "expression": format!("'use strict'; void 0;\n({})", line), "contextId": context_id, // TODO(caspervonb) set repl mode to true to enable const redeclarations and top // level await @@ -117,7 +117,7 @@ pub async fn run( .post_message( "Runtime.evaluate".to_string(), Some(json!({ - "expression": line, + "expression": format!("'use strict'; void 0;\n{}", line), "contextId": context_id, // TODO(caspervonb) set repl mode to true to enable const redeclarations and top // level await From 90f4388302555207ee8fccc053aa3a228e50be03 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 00:48:50 +0000 Subject: [PATCH 21/30] Disable validator for a minute --- cli/repl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 2e0f6f42432e28..64bdc66fe5c636 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -39,12 +39,12 @@ pub async fn run( .post_message("Runtime.enable".to_string(), None) .await?; - let helper = Helper { + let _helper = Helper { validator: MatchingBracketValidator::new(), }; let mut editor = Editor::new(); - editor.set_helper(Some(helper)); + // editor.set_helper(Some(helper)); editor .load_history(history_file.to_str().unwrap()) .unwrap_or(()); From 922f7bcd2c9f91a1097bf09adc69170854da5341 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 00:58:49 +0000 Subject: [PATCH 22/30] Fixup --- cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index 64bdc66fe5c636..0a732f1d024aa0 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -43,7 +43,7 @@ pub async fn run( validator: MatchingBracketValidator::new(), }; - let mut editor = Editor::new(); + let mut editor = Editor::<()>::new(); // editor.set_helper(Some(helper)); editor .load_history(history_file.to_str().unwrap()) From 8e6127d82c9649205ef62ea02c03c77297912e89 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 01:47:56 +0000 Subject: [PATCH 23/30] Spawn blocking --- cli/repl.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 0a732f1d024aa0..9cfa6c2f2c2cbe 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -11,6 +11,8 @@ use rustyline::validate::ValidationResult; use rustyline::validate::Validator; use rustyline::Editor; use rustyline_derive::{Completer, Helper, Highlighter, Hinter}; +use std::sync::Arc; +use std::sync::Mutex; #[derive(Completer, Helper, Highlighter, Hinter)] struct Helper { @@ -43,9 +45,11 @@ pub async fn run( validator: MatchingBracketValidator::new(), }; - let mut editor = Editor::<()>::new(); + let editor = Arc::new(Mutex::new(Editor::<()>::new())); // editor.set_helper(Some(helper)); editor + .lock() + .unwrap() .load_history(history_file.to_str().unwrap()) .unwrap_or(()); @@ -94,7 +98,12 @@ pub async fn run( .await?; loop { - let line = editor.readline("> "); + let editor2 = editor.clone(); + let line = tokio::task::spawn_blocking(move || { + editor2.lock().unwrap().readline("> ") + }) + .await?; + match line { Ok(line) => { let evaluate_response = session @@ -204,7 +213,7 @@ pub async fn run( ), } - editor.add_history_entry(line.as_str()); + editor.lock().unwrap().add_history_entry(line.as_str()); } Err(ReadlineError::Interrupted) => { break; @@ -220,7 +229,10 @@ pub async fn run( } std::fs::create_dir_all(history_file.parent().unwrap())?; - editor.save_history(history_file.to_str().unwrap())?; + editor + .lock() + .unwrap() + .save_history(history_file.to_str().unwrap())?; Ok(()) } From af1a4def0dda1bfc71fdd50eca209b400183986f Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 02:01:17 +0000 Subject: [PATCH 24/30] Re-enable validator --- cli/repl.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 9cfa6c2f2c2cbe..792071bfc8fc26 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -41,12 +41,14 @@ pub async fn run( .post_message("Runtime.enable".to_string(), None) .await?; - let _helper = Helper { + let helper = Helper { validator: MatchingBracketValidator::new(), }; - let editor = Arc::new(Mutex::new(Editor::<()>::new())); - // editor.set_helper(Some(helper)); + let editor = Arc::new(Mutex::new(Editor::new())); + + editor.lock().unwrap().set_helper(helper); + editor .lock() .unwrap() From fe38ac5c003a1a9c7729a9505d578bedae84ebe5 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 02:25:10 +0000 Subject: [PATCH 25/30] Fixup --- cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index 792071bfc8fc26..185af82365c6f3 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -47,7 +47,7 @@ pub async fn run( let editor = Arc::new(Mutex::new(Editor::new())); - editor.lock().unwrap().set_helper(helper); + editor.lock().unwrap().set_helper(Some(helper)); editor .lock() From d43e17c406e3914ea593dc20a5109394aa123abd Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 19:32:12 +0000 Subject: [PATCH 26/30] Explain retry mechanism --- cli/repl.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index 185af82365c6f3..274d0f0228c519 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -108,6 +108,9 @@ pub async fn run( match line { Ok(line) => { + // Things like `{}` will be evaluated as a block statement which is suprising as most + // repl's treat everything as an expression, so we first try to evaluate the input as an + // expression by wrapping it in parens. let evaluate_response = session .post_message( "Runtime.evaluate".to_string(), @@ -121,7 +124,8 @@ pub async fn run( ) .await?; - // Retry without wrapping if there is an error + // If that fails, we retry it without wrapping in parens letting the error bubble up to the + // user if it is still an error. let evaluate_response = if evaluate_response.get("exceptionDetails").is_some() { session From e4d374b0e224d0f6a0e685b69498a58a0edf1e78 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 20:12:08 +0000 Subject: [PATCH 27/30] Only wrap on things that look like it may be an object literal --- cli/repl.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 274d0f0228c519..04a66e9795ba3e 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -108,14 +108,22 @@ pub async fn run( match line { Ok(line) => { - // Things like `{}` will be evaluated as a block statement which is suprising as most - // repl's treat everything as an expression, so we first try to evaluate the input as an - // expression by wrapping it in parens. + // It is a bit unexpected that { "foo": "bar" } is interpreted as a block + // statement rather than an object literal so we interpret it as an expression statement + // to match the behavior found in a typical prompt including browser developer tools. + let wrapped_line = if line.trim_start().starts_with("{") + && !line.trim_end().ends_with(";") + { + format!("({})", &line) + } else { + line.clone() + }; + let evaluate_response = session .post_message( "Runtime.evaluate".to_string(), Some(json!({ - "expression": format!("'use strict'; void 0;\n({})", line), + "expression": format!("'use strict'; void 0;\n{}", &wrapped_line), "contextId": context_id, // TODO(caspervonb) set repl mode to true to enable const redeclarations and top // level await @@ -127,12 +135,14 @@ pub async fn run( // If that fails, we retry it without wrapping in parens letting the error bubble up to the // user if it is still an error. let evaluate_response = - if evaluate_response.get("exceptionDetails").is_some() { + if evaluate_response.get("exceptionDetails").is_some() + && &wrapped_line != &line + { session .post_message( "Runtime.evaluate".to_string(), Some(json!({ - "expression": format!("'use strict'; void 0;\n{}", line), + "expression": format!("'use strict'; void 0;\n{}", &line), "contextId": context_id, // TODO(caspervonb) set repl mode to true to enable const redeclarations and top // level await From 2559fa34cf16ebd7e3a42f5a2ac4a6f3acf17ed4 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 20:21:12 +0000 Subject: [PATCH 28/30] Lint --- cli/repl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/repl.rs b/cli/repl.rs index 04a66e9795ba3e..2cb9221ef86a75 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -111,8 +111,8 @@ pub async fn run( // It is a bit unexpected that { "foo": "bar" } is interpreted as a block // statement rather than an object literal so we interpret it as an expression statement // to match the behavior found in a typical prompt including browser developer tools. - let wrapped_line = if line.trim_start().starts_with("{") - && !line.trim_end().ends_with(";") + let wrapped_line = if line.trim_start().starts_with('{') + && !line.trim_end().ends_with(';') { format!("({})", &line) } else { From 3e7c7242441f70f5af97b3675e673cc3bac8b2f7 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 20:26:14 +0000 Subject: [PATCH 29/30] More lint --- cli/repl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/repl.rs b/cli/repl.rs index 2cb9221ef86a75..5e4c439acf76c6 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -136,7 +136,7 @@ pub async fn run( // user if it is still an error. let evaluate_response = if evaluate_response.get("exceptionDetails").is_some() - && &wrapped_line != &line + && wrapped_line != line { session .post_message( From 6548e21617273230721014cd0b4b968c04bc79cd Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 1 Oct 2020 21:25:51 +0000 Subject: [PATCH 30/30] Minor comment on helper struct --- cli/repl.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/repl.rs b/cli/repl.rs index 5e4c439acf76c6..57e517bd761fed 100644 --- a/cli/repl.rs +++ b/cli/repl.rs @@ -14,6 +14,7 @@ use rustyline_derive::{Completer, Helper, Highlighter, Hinter}; use std::sync::Arc; use std::sync::Mutex; +// Provides syntax specific helpers to the editor like validation for multi-line edits. #[derive(Completer, Helper, Highlighter, Hinter)] struct Helper { validator: MatchingBracketValidator,