valis_core/modules/script/
repl.rs

1use std::borrow::Cow::{self, Borrowed, Owned};
2
3use rlua::Lua;
4use rustyline::{CompletionType, Config, EditMode, Editor, Helper};
5use rustyline::completion::{Candidate, Completer};
6use rustyline::Context;
7use rustyline::highlight::Highlighter;
8use rustyline::hint::Hinter;
9use rustyline::validate::Validator;
10
11use crate::modules::script::engine::prepare_context;
12
13struct CustomCompleter {
14    commands: Vec<&'static str>,
15}
16
17impl Completer for CustomCompleter {
18    type Candidate = SimpleCandidate;
19
20    fn complete(
21        &self,
22        line: &str,
23        pos: usize,
24        _ctx: &Context<'_>,
25    ) -> Result<(usize, Vec<SimpleCandidate>), rustyline::error::ReadlineError> {
26        let mut candidates = Vec::new();
27        for command in &self.commands {
28            if command.starts_with(line) {
29                candidates.push(SimpleCandidate {
30                    display: command.to_string(),
31                    replacement: command[pos..].to_string(),
32                });
33            }
34        }
35
36        Ok((0, candidates))
37    }
38}
39
40#[derive(Debug, Clone)]
41struct SimpleCandidate {
42    display: String,
43    replacement: String,
44}
45
46impl Candidate for SimpleCandidate {
47    fn display(&self) -> &str {
48        &self.display
49    }
50
51    fn replacement(&self) -> &str {
52        &self.replacement
53    }
54}
55
56struct CustomHelper {
57    completer: CustomCompleter,
58}
59
60impl Helper for CustomHelper {}
61
62impl Completer for CustomHelper {
63    type Candidate = SimpleCandidate;
64
65    fn complete(
66        &self,
67        line: &str,
68        pos: usize,
69        ctx: &Context<'_>,
70    ) -> Result<(usize, Vec<SimpleCandidate>), rustyline::error::ReadlineError> {
71        self.completer.complete(line, pos, ctx)
72    }
73}
74
75impl Hinter for CustomHelper {
76    type Hint = String;
77
78    fn hint(&self, _line: &str, _pos: usize, _ctx: &Context<'_>) -> Option<String> {
79        None
80    }
81}
82
83impl Highlighter for CustomHelper {
84    fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
85        Borrowed(line)
86    }
87
88    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
89        Borrowed(hint)
90    }
91}
92
93impl Validator for CustomHelper {}
94
95/// Start a REPL for the Lua engine.
96pub fn repl() {
97    let config = Config::builder()
98        .edit_mode(EditMode::Emacs)
99        .completion_type(CompletionType::List)
100        .build();
101
102    let commands = vec!["git_clone", "run"];
103    let completer = CustomCompleter { commands };
104
105    let helper = CustomHelper { completer };
106    let mut editor = Editor::with_config(config);
107    editor.set_helper(Some(helper));
108
109    let lua = Lua::new();
110    lua.context(|lua_ctx| {
111        prepare_context(&lua_ctx);
112        let prelude = include_str!("prelude.lua");
113        lua_ctx.load(prelude).exec().unwrap();
114    });
115
116    loop {
117        let readline = editor.readline(">> ");
118        match readline {
119            Ok(line) => {
120                editor.add_history_entry(line.as_str());
121                if line.trim() == "quit" {
122                    break;
123                }
124                // Execute the Lua code
125                lua.context(|lua_ctx| match lua_ctx.load(line.as_str()).exec() {
126                    Ok(_) => {}
127                    Err(e) => {
128                        eprintln!("Error: {}", e);
129                    }
130                });
131            }
132            Err(_) => break,
133        }
134    }
135}