about summary refs log tree commit diff
path: root/tvix/eval
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-11T21·27+0300
committertazjin <tazjin@tvl.su>2022-08-26T17·19+0000
commit7c803a7e72564807cd9029a829109d795b9beb6d (patch)
tree761838b0a52a9eb2faafc6027ad019ce7e4bcce2 /tvix/eval
parentd14db8dcaae3c6d624ce17417ac58a44a6f5983c (diff)
feat(tvix/eval): use rustyline crate for REPL r/4505
This is a substantially nicer experience, immediately granting us
history, proper exiting and so on.

Change-Id: Iba4cb1713b9ac53d0799722bdbe2cd0e94a2f527
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6171
Reviewed-by: grfn <grfn@gws.fyi>
Tested-by: BuildkiteCI
Diffstat (limited to 'tvix/eval')
-rw-r--r--tvix/eval/src/main.rs68
1 files changed, 49 insertions, 19 deletions
diff --git a/tvix/eval/src/main.rs b/tvix/eval/src/main.rs
index 41d9ed36bd5d..0c4d082783e2 100644
--- a/tvix/eval/src/main.rs
+++ b/tvix/eval/src/main.rs
@@ -1,8 +1,6 @@
-use std::{
-    env, fs,
-    io::{self, Write},
-    mem, process,
-};
+use std::{env, fs, path::PathBuf, process};
+
+use rustyline::{error::ReadlineError, Editor};
 
 fn main() {
     let mut args = env::args();
@@ -21,26 +19,58 @@ fn main() {
 fn run_file(file: &str) {
     let contents = fs::read_to_string(file).expect("failed to read the input file");
 
-    run(contents);
+    match tvix_eval::interpret(&contents) {
+        Ok(result) => println!("=> {} :: {}", result, result.type_of()),
+        Err(err) => eprintln!("{}", err),
+    }
+}
+
+fn state_dir() -> Option<PathBuf> {
+    let mut path = dirs::data_dir();
+    path.as_mut().map(|p| p.push("tvix"));
+    path
 }
 
 fn run_prompt() {
-    let mut line = String::new();
+    let mut rl = Editor::<()>::new().expect("should be able to launch rustyline");
+
+    let history_path = match state_dir() {
+        Some(mut path) => {
+            path.push("history.txt");
+            rl.load_history(&path).ok();
+
+            Some(path)
+        }
+
+        None => None,
+    };
 
     loop {
-        print!("> ");
-        io::stdout().flush().unwrap();
-        io::stdin()
-            .read_line(&mut line)
-            .expect("failed to read user input");
-        run(mem::take(&mut line));
-        line.clear();
+        let readline = rl.readline("tvix-repl> ");
+        match readline {
+            Ok(line) => {
+                if line.is_empty() {
+                    continue;
+                }
+
+                match tvix_eval::interpret(&line) {
+                    Ok(result) => {
+                        println!("=> {} :: {}", result, result.type_of());
+                        rl.add_history_entry(line);
+                    }
+                    Err(err) => println!("{}", err),
+                }
+            }
+            Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break,
+
+            Err(err) => {
+                eprintln!("error: {}", err);
+                break;
+            }
+        }
     }
-}
 
-fn run(code: String) {
-    match tvix_eval::interpret(&code) {
-        Ok(result) => println!("=> {} :: {}", result, result.type_of()),
-        Err(err) => eprintln!("{}", err),
+    if let Some(path) = history_path {
+        rl.save_history(&path).unwrap();
     }
 }