turtle/repl/
mod.rs

1use ansi_term::{Color, Style};
2use rustyline::completion::{Completer, FilenameCompleter, Pair};
3use rustyline::error::ReadlineError;
4use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
5use rustyline::hint::{Hinter, HistoryHinter};
6use rustyline::validate::Validator;
7use rustyline::validate::{self, MatchingBracketValidator};
8use rustyline::Editor;
9use rustyline::{CompletionType, Config, Context};
10use rustyline_derive::Helper;
11use std::borrow::Cow::{self, Borrowed, Owned};
12
13use crate::Locker;
14
15use crate::{parse, CallSnapshot, Environment};
16
17#[derive(Helper)]
18struct ReplHelper {
19    highlighter: MatchingBracketHighlighter,
20    validator: MatchingBracketValidator,
21    hinter: HistoryHinter,
22    colored_prompt: String,
23    completer: FilenameCompleter,
24}
25
26impl Completer for ReplHelper {
27    type Candidate = Pair;
28
29    fn complete(
30        &self,
31        line: &str,
32        pos: usize,
33        ctx: &Context<'_>,
34    ) -> Result<(usize, Vec<Pair>), ReadlineError> {
35        self.completer.complete(line, pos, ctx)
36    }
37}
38
39impl Hinter for ReplHelper {
40    fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
41        self.hinter.hint(line, pos, ctx)
42    }
43}
44
45impl Highlighter for ReplHelper {
46    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
47        &'s self,
48        prompt: &'p str,
49        default: bool,
50    ) -> Cow<'b, str> {
51        if default {
52            Borrowed(&self.colored_prompt)
53        } else {
54            Borrowed(prompt)
55        }
56    }
57
58    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
59        Owned(Style::new().dimmed().paint(hint).to_string())
60    }
61
62    fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
63        self.highlighter.highlight(line, pos)
64    }
65
66    fn highlight_char(&self, line: &str, pos: usize) -> bool {
67        self.highlighter.highlight_char(line, pos)
68    }
69}
70
71impl Validator for ReplHelper {
72    fn validate(
73        &self,
74        ctx: &mut validate::ValidationContext,
75    ) -> rustyline::Result<validate::ValidationResult> {
76        self.validator.validate(ctx)
77    }
78
79    fn validate_while_typing(&self) -> bool {
80        self.validator.validate_while_typing()
81    }
82}
83
84pub fn spawn(env: Locker<Environment>) {
85    let config = Config::builder()
86        .history_ignore_space(true)
87        .completion_type(CompletionType::List)
88        .build();
89    let h = ReplHelper {
90        completer: FilenameCompleter::new(),
91        highlighter: MatchingBracketHighlighter::new(),
92        hinter: HistoryHinter {},
93        colored_prompt: "".to_owned(),
94        validator: MatchingBracketValidator::new(),
95    };
96    let mut rl = Editor::with_config(config);
97    rl.set_helper(Some(h));
98
99    if rl.load_history(".turtle_history.txt").is_err() {
100        println!("It looks like this is your first time running Turtle from this directory; no history was loaded.")
101    }
102
103    loop {
104        let p = "🐢 > ".to_string();
105        rl.helper_mut().expect("No helper").colored_prompt =
106            Color::Green.bold().paint(&p).to_string();
107        let line = rl.readline(&p);
108        match line {
109            Ok(line) => {
110                rl.add_history_entry(line.as_str());
111                match parse(line.as_str(), "<stdin>") {
112                    Ok(values) => {
113                        for value in values {
114                            let snapshot = CallSnapshot::root(&value.clone());
115                            match value
116                                .eval_async(snapshot, env.clone())
117                                .unwrap()
118                                .recv()
119                                .unwrap()
120                            {
121                                Ok(result) => println!(
122                                    "   {} {}",
123                                    Color::Blue.bold().paint("="),
124                                    Style::default().bold().paint(format!("{}", result))
125                                ),
126                                Err(error) => eprintln!("{}", error),
127                            }
128                        }
129                    }
130                    Err(err) => eprintln!("{:#}", err),
131                }
132            }
133            Err(ReadlineError::Interrupted) => break,
134            Err(ReadlineError::Eof) => break,
135            Err(err) => {
136                eprintln!("Error: {:?}", err);
137                break;
138            }
139        }
140        rl.save_history(".turtle_history.txt").unwrap();
141    }
142}