diff --git a/repl/Cargo.toml b/repl/Cargo.toml index 47b903ada9..f8b48f3bd0 100644 --- a/repl/Cargo.toml +++ b/repl/Cargo.toml @@ -21,6 +21,7 @@ gluon_vm = { version = "0.6.2", path = "../vm", features = ["serialization"] } # gluon_completion = { path = "../completion", version = "0.6.2" } # GLUON gluon_format = { version = "0.6.2", path = "../format" } # GLUON +app_dirs = "1.0.0" futures = "0.1.11" futures-cpupool = "0.1" tokio-core = "0.1" diff --git a/repl/src/main.rs b/repl/src/main.rs index a0887c78a8..013bf5284b 100644 --- a/repl/src/main.rs +++ b/repl/src/main.rs @@ -1,6 +1,7 @@ //! REPL for the gluon programming language #![doc(html_root_url = "https://docs.rs/gluon_repl/0.4.1")] // # GLUON +extern crate app_dirs; #[macro_use] extern crate clap; #[cfg(feature = "env_logger")] @@ -37,6 +38,11 @@ use gluon::vm::Error as VMError; mod repl; +const APP_INFO: app_dirs::AppInfo = app_dirs::AppInfo { + name: "gluon-repl", + author: "gluon-lang", +}; + fn run_files<'s, I>(vm: &Thread, files: I) -> Result<()> where I: Iterator, @@ -168,7 +174,6 @@ fn main() { } } - #[cfg(test)] mod tests { // If nothing else this suppresses the unused imports warnings when compiling in test mode diff --git a/repl/src/repl.glu b/repl/src/repl.glu index 270051d0d6..3279c03030 100644 --- a/repl/src/repl.glu +++ b/repl/src/repl.glu @@ -167,16 +167,15 @@ let loop repl : Repl -> IO () = let run_line line = if string.is_empty (string.trim line) then wrap Continue + else if string.starts_with line ":" then + do_command repl.commands line else - if string.starts_with line ":" then - do_command repl.commands line - else - let action = - do eval_thread = thread.new_thread () - let eval_action = repl_prim.eval_line line - repl_prim.finish_or_interrupt repl.cpu_pool eval_thread eval_action - io.catch action wrap >>= io.println - *> wrap Continue + let action = + do eval_thread = thread.new_thread () + let eval_action = repl_prim.eval_line line + repl_prim.finish_or_interrupt repl.cpu_pool eval_thread eval_action + io.catch action wrap >>= io.println + *> wrap Continue do line_result = rustyline.readline repl.editor "> " match line_result with @@ -186,7 +185,9 @@ let loop repl : Repl -> IO () = do continue = run_line line match continue with | Continue -> loop repl - | Quit -> wrap () + | Quit -> + do _ = rustyline.save_history repl.editor + wrap () let run x : () -> IO () = do _ = io.println "gluon (:h for help, :q to quit)" diff --git a/repl/src/repl.rs b/repl/src/repl.rs index aa76be93fe..8c1bee3c91 100644 --- a/repl/src/repl.rs +++ b/repl/src/repl.rs @@ -4,6 +4,7 @@ extern crate rustyline; extern crate gluon_completion as completion; use std::error::Error as StdError; +use std::path::PathBuf; use std::sync::Mutex; use futures::{Future, Sink, Stream}; @@ -162,7 +163,6 @@ struct CpuPool(self::futures_cpupool::CpuPool); impl_userdata!{ CpuPool } - #[derive(Serialize, Deserialize)] pub enum ReadlineError { Eof, @@ -192,9 +192,22 @@ impl<'vm> Pushable<'vm> for ReadlineError { } } +fn app_dir_root() -> Result> { + Ok(::app_dirs::app_root( + ::app_dirs::AppDataType::UserData, + &::APP_INFO, + )?) +} fn new_editor(vm: WithVM<()>) -> IO { let mut editor = rustyline::Editor::new(); + + let history_result = + app_dir_root().and_then(|path| Ok(editor.load_history(&*path.join("history"))?)); + + if let Err(err) = history_result { + warn!("Unable to load history: {}", err); + } editor.set_completer(Some(Completer(vm.vm.root_thread()))); IO::Value(Editor(Mutex::new(editor))) } @@ -354,7 +367,6 @@ fn finish_or_interrupt( let (sender, receiver) = mpsc::channel(1); - remote.spawn(|handle| { ::tokio_signal::ctrl_c(handle) .map(|x| { @@ -390,6 +402,25 @@ fn finish_or_interrupt( )) } +fn save_history(editor: &Editor) -> IO<()> { + let history_result = app_dir_root().and_then(|path| { + editor + .0 + .lock() + .unwrap() + .save_history(&*path.join("history")) + .map_err(|err| { + let err: Box = Box::new(err); + err + }) + }); + + if let Err(err) = history_result { + warn!("Unable to load history: {}", err); + } + IO::Value(()) +} + fn load_rustyline(vm: &Thread) -> vm::Result { vm.register_type::("Editor", &[])?; vm.register_type::("CpuPool", &[])?; @@ -398,7 +429,8 @@ fn load_rustyline(vm: &Thread) -> vm::Result { vm, record!( new_editor => primitive!(1 new_editor), - readline => primitive!(2 readline) + readline => primitive!(2 readline), + save_history => primitive!(1 save_history) ), ) }