diff --git a/BUILD.gn b/BUILD.gn index 259c5bdc44c7a4..948884a802231c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -51,6 +51,7 @@ main_extern = [ "$rust_build:libc", "$rust_build:log", "$rust_build:ring", + "$rust_build:rustyline", "$rust_build:tempfile", "$rust_build:rand", "$rust_build:tokio", diff --git a/build_extra/rust/BUILD.gn b/build_extra/rust/BUILD.gn index 93c370c41803a8..19e5bcb9bdd5f7 100644 --- a/build_extra/rust/BUILD.gn +++ b/build_extra/rust/BUILD.gn @@ -11,6 +11,51 @@ import("rust.gni") crates = "//third_party/rust_crates" registry_github = "$crates/registry/src/github.com-1ecc6299db9ec823/" +rust_crate("nix") { + source_root = "$registry_github/nix-0.11.0/src/lib.rs" + extern = [ + ":cfg_if", + ":libc", + ":void", + ":bitflags", + ] +} + +rust_crate("rustyline") { + source_root = "$registry_github/rustyline-2.1.0/src/lib.rs" + extern = [ + ":dirs", + ":libc", + ":log", + ":memchr", + ":nix", + ":unicode_segmentation", + ":unicode_width", + ":utf8parse", + ] +} + +rust_crate("bitflags") { + source_root = "$registry_github/bitflags-1.0.4/src/lib.rs" +} + +rust_crate("unicode_segmentation") { + source_root = "$registry_github/unicode-segmentation-1.2.1/src/lib.rs" +} +rust_crate("memchr") { + source_root = "$registry_github/memchr-2.1.0/src/lib.rs" + extern = [ + ":cfg_if", + ":libc", + ] +} +rust_crate("utf8parse") { + source_root = "$registry_github/utf8parse-0.1.1/src/lib.rs" +} +rust_crate("unicode_width") { + source_root = "$registry_github/unicode-width-0.1.5/src/lib.rs" +} + rust_crate("libc") { source_root = "$registry_github/libc-0.2.43/src/lib.rs" features = [ "use_std" ] @@ -659,7 +704,7 @@ rust_crate("hyper_rustls") { } rust_crate("dirs") { - source_root = "$registry_github/dirs-1.0.3/src/lib.rs" + source_root = "$registry_github/dirs-1.0.4/src/lib.rs" extern = [ ":libc", ":winapi", diff --git a/js/main.ts b/js/main.ts index 0d33cf063ecc39..f249baf3eecb95 100644 --- a/js/main.ts +++ b/js/main.ts @@ -8,6 +8,7 @@ import { libdeno } from "./libdeno"; import { args } from "./deno"; import { sendSync, handleAsyncMsgFromRust } from "./dispatch"; import { promiseErrorExaminer, promiseRejectHandler } from "./promise_util"; +import { repl_loop } from "./repl"; function sendStart(): msg.StartRes { const builder = new flatbuffers.Builder(); @@ -33,7 +34,12 @@ function onGlobalError( } else { console.log(`Thrown: ${String(error)}`); } - os.exit(1); + // FIXME this is a hack, and anyway doesn't work for `throw "error"` + // which (for some reason) has source == undefined + if (source !== "deno repl") { + console.log(`Source: ${source}`); + os.exit(1); + } } /* tslint:disable-next-line:no-default-export */ @@ -68,21 +74,24 @@ export default function denoMain() { } log("args", args); Object.freeze(args); - const inputFn = args[0]; if (!inputFn) { - console.log("No input script specified."); - os.exit(1); + // repl!! + // console.log("No input script specified."); + // os.exit(1); } // handle `--deps` - if (startResMsg.depsFlag()) { + if (inputFn && startResMsg.depsFlag()) { for (const dep of compiler.getModuleDependencies(inputFn, `${cwd}/`)) { console.log(dep); } os.exit(0); } - compiler.recompile = startResMsg.recompileFlag(); - compiler.run(inputFn, `${cwd}/`); + if (inputFn) { + compiler.run(inputFn, `${cwd}/`); + } else { + repl_loop(); + } } diff --git a/js/repl.ts b/js/repl.ts new file mode 100644 index 00000000000000..487e4893bb524a --- /dev/null +++ b/js/repl.ts @@ -0,0 +1,55 @@ +// Copyright 2018 the Deno authors. All rights reserved. MIT license. +import * as msg from "gen/msg_generated"; +import { flatbuffers } from "flatbuffers"; +import { assert } from "./util"; +import * as dispatch from "./dispatch"; +import { window } from "./globals"; +import * as deno from "./deno"; + +// FIXME assignis like this is bad +window.deno = deno; +/** Read the next line for the repl. + * + * import { readFile } from "deno"; + * const decoder = new TextDecoder("utf-8"); + * const data = await readFile("hello.txt"); + * console.log(decoder.decode(data)); + */ +async function readline(prompt: string): Promise { + return res(await dispatch.sendAsync(...req(prompt))); +} + +function req( + prompt: string +): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] { + const builder = new flatbuffers.Builder(); + const prompt_ = builder.createString(prompt); + msg.Repl.startRepl(builder); + msg.Repl.addPrompt(builder, prompt_); + const inner = msg.Repl.endRepl(builder); + return [builder, msg.Any.Repl, inner]; +} + +function res(baseRes: null | msg.Base): string { + assert(baseRes != null); + assert(msg.Any.ReplRes === baseRes!.innerType()); + const inner = new msg.ReplRes(); + assert(baseRes!.inner(inner) != null); + const line = inner.line(); + assert(line !== null); + return line ? line : "FIXME"; // FIXME null handling +} + +export async function repl_loop() { + while (true) { + const line = await readline(">> "); + try { + const result = eval.call(window, line); + if (result) { + console.log(result); + } + } catch (err) { + console.log(err); + } + } +} diff --git a/src/isolate.rs b/src/isolate.rs index 5a09b8855c8e88..e91abbaa721e11 100644 --- a/src/isolate.rs +++ b/src/isolate.rs @@ -11,6 +11,7 @@ use libdeno; use futures::Future; use libc::c_void; +use rustyline::Editor; use std; use std::ffi::CStr; use std::ffi::CString; @@ -56,6 +57,7 @@ pub struct IsolateState { pub flags: flags::DenoFlags, tx: Mutex>>, pub metrics: Mutex, + pub repl: Mutex>>, } impl IsolateState { @@ -120,6 +122,7 @@ impl Isolate { flags, tx: Mutex::new(Some(tx)), metrics: Mutex::new(Metrics::default()), + repl: Mutex::new(None), }), } } @@ -303,11 +306,10 @@ extern "C" fn pre_dispatch( // manually. isolate.ntasks_increment(); - let task = op - .and_then(move |buf| { - state.send_to_js(req_id, buf); - Ok(()) - }).map_err(|_| ()); + let task = op.and_then(move |buf| { + state.send_to_js(req_id, buf); + Ok(()) + }).map_err(|_| ()); tokio::spawn(task); } } diff --git a/src/main.rs b/src/main.rs index b84ee78d960676..d79ac65a644562 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,12 @@ extern crate flatbuffers; #[macro_use] extern crate futures; +// extern crate rustyline; extern crate hyper; extern crate libc; extern crate msg_rs as msg; extern crate rand; +extern crate rustyline; extern crate tempfile; extern crate tokio; extern crate tokio_executor; @@ -30,6 +32,7 @@ mod http_util; mod isolate; mod libdeno; pub mod ops; +mod repl; mod resources; mod tokio_util; mod version; @@ -78,5 +81,9 @@ fn main() { std::process::exit(1); }); isolate.event_loop(); + // if no args then enter repl + // if isolate.state.argv.len() == 1 { + // repl::repl_loop(&mut isolate) + // } }); } diff --git a/src/msg.fbs b/src/msg.fbs index 5b60213ade2dce..ab5b47531d392c 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -23,6 +23,8 @@ union Any { Rename, Readlink, ReadlinkRes, + Repl, + ReplRes, Symlink, Stat, StatRes, @@ -248,6 +250,14 @@ table ReadlinkRes { path: string; } +table Repl { + prompt: string; +} + +table ReplRes { + line: string; +} + table Symlink { oldname: string; newname: string; diff --git a/src/ops.rs b/src/ops.rs index 261ed67f5d4445..928fc5123aa3ed 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -9,6 +9,7 @@ use isolate::Isolate; use isolate::IsolateState; use isolate::Op; use msg; +use repl; use resources; use resources::Resource; use tokio_util; @@ -91,6 +92,7 @@ pub fn dispatch( msg::Any::ReadDir => op_read_dir, msg::Any::Rename => op_rename, msg::Any::Readlink => op_read_link, + msg::Any::Repl => op_repl, msg::Any::Symlink => op_symlink, msg::Any::SetEnv => op_set_env, msg::Any::Stat => op_stat, @@ -1063,6 +1065,42 @@ fn op_read_link( }) } +fn op_repl( + state: Arc, // FIXME (this will be needed!) + base: &msg::Base, + data: &'static mut [u8], +) -> Box { + assert_eq!(data.len(), 0); + let inner = base.inner_as_repl().unwrap(); + let cmd_id = base.cmd_id(); + let prompt = inner.prompt().unwrap().to_owned(); + // let f = || repl::readline(state, prompt); + + blocking!(base.sync(), || -> OpResult { + // debug!("op_repl {}", prompt); + let line = repl::readline(&state, &prompt)?; // FIXME + // let line = f()?; + let builder = &mut FlatBufferBuilder::new(); + let line_off = builder.create_string(&line); + let inner = msg::ReplRes::create( + builder, + &msg::ReplResArgs { + line: Some(line_off), + ..Default::default() + }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::ReplRes, + ..Default::default() + }, + )) + }) +} + fn op_truncate( state: Arc, base: &msg::Base, diff --git a/src/repl.rs b/src/repl.rs new file mode 100644 index 00000000000000..8056f894617fdc --- /dev/null +++ b/src/repl.rs @@ -0,0 +1,39 @@ +// Copyright 2018 the Deno authors. All rights reserved. MIT license. +extern crate rustyline; +use rustyline::Editor; + +use std::error::Error; +use std::sync::Arc; +//use futures::Future; +use msg::ErrorKind; + +use errors::new as deno_error; +use errors::DenoResult; +use isolate; + +pub fn readline( + _state: &Arc, + prompt: &String, +) -> DenoResult { + // FIXME + // let mut maybe_editor = state.repl.lock().unwrap(); + // if maybe_editor.is_none() { + // println!("{}", "Creating new Editor<()>"); + // *maybe_editor = Some(start_repl()); // will this assign within IsolateState? + // } + let maybe_editor = Some(start_repl()); + maybe_editor + .unwrap() + .readline(prompt) + .map_err(|err| deno_error(ErrorKind::Other, err.description().to_string())) +} + +// FIXME can we call save_history when this is dropped / upon exit? +// rl.save_history("history.txt").unwrap(); +fn start_repl() -> Editor<()> { + let mut editor = Editor::<()>::new(); + if editor.load_history("history.txt").is_err() { + eprintln!("No previous history."); + } + editor +} diff --git a/third_party b/third_party index a133fa714b960d..0f61667878d446 160000 --- a/third_party +++ b/third_party @@ -1 +1 @@ -Subproject commit a133fa714b960d8f88c55188ccc1a41882961e6e +Subproject commit 0f61667878d44606346590b2f531b82444d7c3d8