mrubyedge_cli/subcommands/
repl.rs1use clap::Args;
2use crossterm::{
3 cursor,
4 event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
5 execute,
6 terminal::{self, ClearType},
7};
8use std::io::{self, Write};
9
10use mruby_compiler2_sys as mrbc;
11use mrubyedge::yamrb::helpers::mrb_call_inspect;
12
13#[derive(Args)]
14pub struct ReplArgs {
15 #[arg(short = 'v', long)]
17 pub verbose: bool,
18}
19
20pub fn execute(args: ReplArgs) -> Result<(), Box<dyn std::error::Error>> {
21 eprintln!("mruby/edge REPL ({})", mrubyedge::version!());
22 eprintln!("Type 'exit[↩]' or press Ctrl+D to quit");
23 eprintln!(
24 "Press Enter to execute, Option+Enter(Shift+Enter also supported in iTerm2) for line continuation"
25 );
26 eprintln!();
27
28 let empty_code = "";
30 let mrb_bin = unsafe {
31 let mut ctx = mrbc::MRubyCompiler2Context::new();
32 ctx.compile(empty_code)?
33 };
34 let mut rite = mrubyedge::rite::load(&mrb_bin)?;
35 let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
36
37 terminal::enable_raw_mode()?;
39 let mut stdout = io::stdout();
40
41 let mut line_number = 1;
42 let mut buffer = String::new();
43 let mut current_line = String::new();
44
45 print!("repl:{:03}> ", line_number);
47 stdout.flush()?;
48
49 let result = (|| -> Result<(), Box<dyn std::error::Error>> {
50 loop {
51 if let Event::Key(key_event) = event::read()? {
53 match key_event {
54 KeyEvent {
56 code: KeyCode::Char('d'),
57 modifiers: KeyModifiers::CONTROL,
58 ..
59 } => {
60 terminal::disable_raw_mode()?;
61 println!();
62 break;
63 }
64 KeyEvent {
66 code: KeyCode::Char('c'),
67 modifiers: KeyModifiers::CONTROL,
68 ..
69 } => {
70 terminal::disable_raw_mode()?;
71 println!();
72 current_line.clear();
73 buffer.clear();
74 line_number += 1;
75 print!("repl:{:03}> ", line_number);
76 stdout.flush()?;
77 terminal::enable_raw_mode()?;
78 }
79 KeyEvent {
81 code: KeyCode::Enter,
82 modifiers: KeyModifiers::ALT | KeyModifiers::SHIFT,
83 ..
84 } => {
85 terminal::disable_raw_mode()?;
86 println!();
87 buffer.push_str(¤t_line);
88 buffer.push('\n');
89 current_line.clear();
90 print!("repl:{:03}* ", line_number);
91 stdout.flush()?;
92 terminal::enable_raw_mode()?;
93 }
94 KeyEvent {
96 code: KeyCode::Enter,
97 modifiers: KeyModifiers::NONE,
98 ..
99 } => {
100 terminal::disable_raw_mode()?;
101 println!();
102
103 if !current_line.is_empty() {
105 buffer.push_str(¤t_line);
106 buffer.push('\n');
107 }
108
109 if buffer.trim().is_empty() {
110 current_line.clear();
111 print!("repl:{:03}> ", line_number);
112 stdout.flush()?;
113 terminal::enable_raw_mode()?;
114 continue;
115 }
116
117 let trimmed = buffer.trim();
119 if trimmed == "exit" || trimmed == "quit" {
120 break;
121 }
122
123 unsafe {
125 let mut ctx = mrbc::MRubyCompiler2Context::new();
126 match ctx.compile(&buffer) {
127 Ok(mrb_bin) => match mrubyedge::rite::load(&mrb_bin) {
128 Ok(mut new_rite) => match vm.eval_rite(&mut new_rite) {
129 Ok(result) => match mrb_call_inspect(&mut vm, result) {
130 Ok(inspect_result) => {
131 match TryInto::<String>::try_into(
132 inspect_result.as_ref(),
133 ) {
134 Ok(s) => println!(" => {}", s),
135 Err(_) => println!(" => <unprintable>"),
136 }
137 }
138 Err(_) => println!(" => <inspect failed>"),
139 },
140 Err(e) => {
141 eprintln!("{:?}", e);
142 vm.exception.take();
143 }
144 },
145 Err(e) => {
146 eprintln!("Failed to load bytecode: {:?}", e);
147 }
148 },
149 Err(e) => {
150 eprintln!("Compilation error: {}", e);
151 }
152 }
153 }
154
155 buffer.clear();
156 current_line.clear();
157 line_number += 1;
158 print!("repl:{:03}> ", line_number);
159 stdout.flush()?;
160
161 terminal::enable_raw_mode()?;
162 }
163 KeyEvent {
165 code: KeyCode::Backspace,
166 ..
167 } => {
168 if !current_line.is_empty() {
169 current_line.pop();
170 execute!(
171 stdout,
172 cursor::MoveLeft(1),
173 terminal::Clear(ClearType::UntilNewLine)
174 )?;
175 stdout.flush()?;
176 }
177 }
178 KeyEvent {
180 code: KeyCode::Char(c),
181 modifiers: KeyModifiers::NONE | KeyModifiers::SHIFT,
182 ..
183 } => {
184 current_line.push(c);
185 print!("{}", c);
186 stdout.flush()?;
187 }
188 _ => {
189 }
191 }
192 }
193 }
194
195 Ok(())
196 })();
197
198 terminal::disable_raw_mode()?;
200
201 if args.verbose {
202 eprintln!("REPL session ended");
203 }
204
205 result
206}