diff --git a/cli/tools/repl/editor.rs b/cli/tools/repl/editor.rs index 69fec9df06..73196d3f34 100644 --- a/cli/tools/repl/editor.rs +++ b/cli/tools/repl/editor.rs @@ -5,6 +5,7 @@ use crate::colors; use deno_ast::swc::parser::error::SyntaxError; use deno_ast::swc::parser::token::Token; use deno_ast::swc::parser::token::Word; +use deno_core::anyhow::Context as _; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::serde_json; @@ -27,6 +28,8 @@ use rustyline::{ConditionalEventHandler, Event, EventContext, RepeatCount}; use rustyline_derive::{Helper, Hinter}; use std::borrow::Cow; use std::path::PathBuf; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; use std::sync::Arc; use super::channel::RustylineSyncMessageSender; @@ -368,10 +371,14 @@ impl Highlighter for EditorHelper { pub struct ReplEditor { inner: Arc>>, history_file_path: PathBuf, + errored_on_history_save: Arc, } impl ReplEditor { - pub fn new(helper: EditorHelper, history_file_path: PathBuf) -> Self { + pub fn new( + helper: EditorHelper, + history_file_path: PathBuf, + ) -> Result { let editor_config = Config::builder() .completion_type(CompletionType::List) .build(); @@ -389,25 +396,35 @@ impl ReplEditor { EventHandler::Conditional(Box::new(TabEventHandler)), ); - ReplEditor { + let history_file_dir = history_file_path.parent().unwrap(); + std::fs::create_dir_all(history_file_dir).with_context(|| { + format!( + "Unable to create directory for the history file: {}", + history_file_dir.display() + ) + })?; + + Ok(ReplEditor { inner: Arc::new(Mutex::new(editor)), history_file_path, - } + errored_on_history_save: Arc::new(AtomicBool::new(false)), + }) } pub fn readline(&self) -> Result { self.inner.lock().readline("> ") } - pub fn add_history_entry(&self, entry: String) { + pub fn update_history(&self, entry: String) { self.inner.lock().add_history_entry(entry); - } + if let Err(e) = self.inner.lock().append_history(&self.history_file_path) { + if self.errored_on_history_save.load(Relaxed) { + return; + } - pub fn save_history(&self) -> Result<(), AnyError> { - std::fs::create_dir_all(self.history_file_path.parent().unwrap())?; - - self.inner.lock().save_history(&self.history_file_path)?; - Ok(()) + self.errored_on_history_save.store(true, Relaxed); + eprintln!("Unable to save history file: {}", e); + } } } diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index a41fe61187..d4d8060f53 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -154,7 +154,7 @@ pub async fn run( }; let history_file_path = ps.dir.root.join("deno_history.txt"); - let editor = ReplEditor::new(helper, history_file_path); + let editor = ReplEditor::new(helper, history_file_path)?; println!("Deno {}", crate::version::deno()); println!("exit using ctrl+d, ctrl+c, or close()"); @@ -169,35 +169,42 @@ pub async fn run( match line { Ok(line) => { should_exit_on_interrupt = false; + editor.update_history(line.clone()); - if line == ".restart" { - println!("Started a new REPL session. Global scope has been reset."); - repl_session = create_repl_session( - ps, - module_url.clone(), - maybe_eval_files.clone(), - maybe_eval.clone(), - ) - .await?; - continue; - } else if line == ".help" { - print_help(); - continue; - } else if line == ".exit" { - break; + match &line { + ".restart" => { + println!( + "Started a new REPL session. Global scope has been reset." + ); + repl_session = create_repl_session( + ps, + module_url.clone(), + maybe_eval_files.clone(), + maybe_eval.clone(), + ) + .await?; + continue; + } + ".help" => { + print_help(); + continue; + } + ".exit" => { + break; + } + line => { + let output = + repl_session.evaluate_line_and_get_output(&line).await?; + + // We check for close and break here instead of making it a loop condition to get + // consistent behavior in when the user evaluates a call to close(). + if repl_session.closing().await? { + break; + } + + println!("{}", output); + } } - - let output = repl_session.evaluate_line_and_get_output(&line).await?; - - // We check for close and break here instead of making it a loop condition to get - // consistent behavior in when the user evaluates a call to close(). - if repl_session.closing().await? { - break; - } - - println!("{}", output); - - editor.add_history_entry(line); } Err(ReadlineError::Interrupted) => { if should_exit_on_interrupt { @@ -217,7 +224,5 @@ pub async fn run( } } - editor.save_history()?; - Ok(repl_session.worker.get_exit_code()) }