Skip to content

Commit 1edf7c8

Browse files
committed
Cleanup some stuff related to the repl
1 parent 67f005e commit 1edf7c8

File tree

3 files changed

+158
-114
lines changed

3 files changed

+158
-114
lines changed

src/shell.rs

Lines changed: 3 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod readline;
12
#[cfg(not(target_os = "wasi"))]
23
mod rustyline_helper;
34

@@ -10,11 +11,8 @@ use rustpython_vm::{
1011
scope::Scope,
1112
VirtualMachine,
1213
};
13-
#[cfg(not(target_os = "wasi"))]
14-
use rustyline_helper::ShellHelper;
1514

16-
use std::io;
17-
use std::path::Path;
15+
use readline::{Readline, ReadlineResult};
1816

1917
enum ShellExecResult {
2018
Ok,
@@ -45,110 +43,6 @@ fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> ShellExecResul
4543
}
4644
}
4745

48-
enum ReadlineResult {
49-
Line(String),
50-
EOF,
51-
Interrupt,
52-
IO(std::io::Error),
53-
EncodingError,
54-
Other(Box<dyn std::error::Error>),
55-
}
56-
57-
#[allow(unused)]
58-
struct BasicReadline;
59-
60-
#[allow(unused)]
61-
impl BasicReadline {
62-
fn new(_vm: &VirtualMachine, _scope: Scope) -> Self {
63-
BasicReadline
64-
}
65-
66-
fn load_history(&mut self, _path: &Path) -> io::Result<()> {
67-
Ok(())
68-
}
69-
70-
fn save_history(&mut self, _path: &Path) -> io::Result<()> {
71-
Ok(())
72-
}
73-
74-
fn add_history_entry(&mut self, _entry: &str) {}
75-
76-
fn readline(&mut self, prompt: &str) -> ReadlineResult {
77-
use std::io::prelude::*;
78-
print!("{}", prompt);
79-
if let Err(e) = io::stdout().flush() {
80-
return ReadlineResult::IO(e);
81-
}
82-
83-
match io::stdin().lock().lines().next() {
84-
Some(Ok(line)) => ReadlineResult::Line(line),
85-
None => ReadlineResult::EOF,
86-
Some(Err(e)) => match e.kind() {
87-
io::ErrorKind::Interrupted => ReadlineResult::Interrupt,
88-
io::ErrorKind::InvalidData => ReadlineResult::EncodingError,
89-
_ => ReadlineResult::IO(e),
90-
},
91-
}
92-
}
93-
}
94-
95-
#[cfg(target_os = "wasi")]
96-
type Readline = BasicReadline;
97-
98-
#[cfg(not(target_os = "wasi"))]
99-
struct RustylineReadline<'vm> {
100-
repl: rustyline::Editor<ShellHelper<'vm>>,
101-
}
102-
#[cfg(not(target_os = "wasi"))]
103-
impl<'vm> RustylineReadline<'vm> {
104-
fn new(vm: &'vm VirtualMachine, scope: Scope) -> Self {
105-
use rustyline::{CompletionType, Config, Editor};
106-
let mut repl = Editor::with_config(
107-
Config::builder()
108-
.completion_type(CompletionType::List)
109-
.tab_stop(4)
110-
.build(),
111-
);
112-
repl.set_helper(Some(ShellHelper::new(vm, scope)));
113-
RustylineReadline { repl }
114-
}
115-
116-
fn load_history(&mut self, path: &Path) -> rustyline::Result<()> {
117-
self.repl.load_history(path)
118-
}
119-
120-
fn save_history(&mut self, path: &Path) -> rustyline::Result<()> {
121-
if !path.exists() {
122-
if let Some(parent) = path.parent() {
123-
std::fs::create_dir_all(parent)?;
124-
}
125-
}
126-
self.repl.save_history(path)
127-
}
128-
129-
fn add_history_entry(&mut self, entry: &str) {
130-
self.repl.add_history_entry(entry);
131-
}
132-
133-
fn readline(&mut self, prompt: &str) -> ReadlineResult {
134-
use rustyline::error::ReadlineError;
135-
match self.repl.readline(prompt) {
136-
Ok(line) => ReadlineResult::Line(line),
137-
Err(ReadlineError::Interrupted) => ReadlineResult::Interrupt,
138-
Err(ReadlineError::Eof) => ReadlineResult::EOF,
139-
Err(ReadlineError::Io(e)) => ReadlineResult::IO(e),
140-
#[cfg(unix)]
141-
Err(ReadlineError::Utf8Error) => ReadlineResult::EncodingError,
142-
#[cfg(windows)]
143-
Err(ReadlineError::Decode(_)) => ReadlineResult::EncodingError,
144-
Err(e) => ReadlineResult::Other(e.into()),
145-
}
146-
}
147-
}
148-
149-
#[cfg(not(target_os = "wasi"))]
150-
type Readline<'a> = RustylineReadline<'a>;
151-
15246
pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
15347
println!(
15448
"Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}",
@@ -187,7 +81,7 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
18781
ReadlineResult::Line(line) => {
18882
debug!("You entered {:?}", line);
18983

190-
repl.add_history_entry(line.trim_end());
84+
repl.add_history_entry(line.trim_end()).unwrap();
19185

19286
let stop_continuing = line.is_empty();
19387

src/shell/readline.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use std::io;
2+
use std::path::Path;
3+
4+
use rustpython_vm::{scope::Scope, VirtualMachine};
5+
6+
type OtherError = Box<dyn std::error::Error>;
7+
type OtherResult<T> = Result<T, OtherError>;
8+
9+
pub enum ReadlineResult {
10+
Line(String),
11+
EOF,
12+
Interrupt,
13+
IO(std::io::Error),
14+
EncodingError,
15+
Other(OtherError),
16+
}
17+
18+
#[allow(unused)]
19+
mod basic_readline {
20+
use super::*;
21+
22+
pub struct BasicReadline<'vm> {
23+
vm: &'vm VirtualMachine,
24+
}
25+
26+
impl<'vm> BasicReadline<'vm> {
27+
pub fn new(vm: &'vm VirtualMachine, _scope: Scope) -> Self {
28+
BasicReadline { vm }
29+
}
30+
31+
pub fn load_history(&mut self, _path: &Path) -> OtherResult<()> {
32+
Ok(())
33+
}
34+
35+
pub fn save_history(&mut self, _path: &Path) -> OtherResult<()> {
36+
Ok(())
37+
}
38+
39+
pub fn add_history_entry(&mut self, _entry: &str) -> OtherResult<()> {
40+
Ok(())
41+
}
42+
43+
pub fn readline(&mut self, prompt: &str) -> ReadlineResult {
44+
use std::io::prelude::*;
45+
print!("{}", prompt);
46+
if let Err(e) = io::stdout().flush() {
47+
return ReadlineResult::IO(e);
48+
}
49+
50+
match io::stdin().lock().lines().next() {
51+
Some(Ok(line)) => ReadlineResult::Line(line),
52+
None => ReadlineResult::EOF,
53+
Some(Err(e)) => match e.kind() {
54+
io::ErrorKind::Interrupted => ReadlineResult::Interrupt,
55+
io::ErrorKind::InvalidData => ReadlineResult::EncodingError,
56+
_ => ReadlineResult::IO(e),
57+
},
58+
}
59+
}
60+
}
61+
}
62+
63+
#[cfg(not(target_os = "wasi"))]
64+
mod rustyline_readline {
65+
use super::{super::rustyline_helper::ShellHelper, *};
66+
67+
pub struct RustylineReadline<'vm> {
68+
repl: rustyline::Editor<ShellHelper<'vm>>,
69+
}
70+
71+
impl<'vm> RustylineReadline<'vm> {
72+
pub fn new(vm: &'vm VirtualMachine, scope: Scope) -> Self {
73+
use rustyline::{At, Cmd, CompletionType, Config, Editor, KeyPress, Movement, Word};
74+
let mut repl = Editor::with_config(
75+
Config::builder()
76+
.completion_type(CompletionType::List)
77+
.tab_stop(8)
78+
.build(),
79+
);
80+
repl.bind_sequence(
81+
KeyPress::ControlLeft,
82+
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
83+
);
84+
repl.bind_sequence(
85+
KeyPress::ControlRight,
86+
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
87+
);
88+
repl.set_helper(Some(ShellHelper::new(vm, scope)));
89+
RustylineReadline { repl }
90+
}
91+
92+
pub fn load_history(&mut self, path: &Path) -> OtherResult<()> {
93+
self.repl.load_history(path)?;
94+
Ok(())
95+
}
96+
97+
pub fn save_history(&mut self, path: &Path) -> OtherResult<()> {
98+
if !path.exists() {
99+
if let Some(parent) = path.parent() {
100+
std::fs::create_dir_all(parent)?;
101+
}
102+
}
103+
self.repl.save_history(path)?;
104+
Ok(())
105+
}
106+
107+
pub fn add_history_entry(&mut self, entry: &str) -> OtherResult<()> {
108+
self.repl.add_history_entry(entry);
109+
Ok(())
110+
}
111+
112+
pub fn readline(&mut self, prompt: &str) -> ReadlineResult {
113+
use rustyline::error::ReadlineError;
114+
match self.repl.readline(prompt) {
115+
Ok(line) => ReadlineResult::Line(line),
116+
Err(ReadlineError::Interrupted) => ReadlineResult::Interrupt,
117+
Err(ReadlineError::Eof) => ReadlineResult::EOF,
118+
Err(ReadlineError::Io(e)) => ReadlineResult::IO(e),
119+
#[cfg(unix)]
120+
Err(ReadlineError::Utf8Error) => ReadlineResult::EncodingError,
121+
#[cfg(windows)]
122+
Err(ReadlineError::Decode(_)) => ReadlineResult::EncodingError,
123+
Err(e) => ReadlineResult::Other(e.into()),
124+
}
125+
}
126+
}
127+
}
128+
129+
#[cfg(target_os = "wasi")]
130+
type ReadlineInner<'vm> = basic_readline::BasicReadline<'vm>;
131+
132+
#[cfg(not(target_os = "wasi"))]
133+
type ReadlineInner<'vm> = rustyline_readline::RustylineReadline<'vm>;
134+
135+
pub struct Readline<'vm>(ReadlineInner<'vm>);
136+
137+
impl<'vm> Readline<'vm> {
138+
pub fn new(vm: &'vm VirtualMachine, scope: Scope) -> Self {
139+
Readline(ReadlineInner::new(vm, scope))
140+
}
141+
pub fn load_history(&mut self, path: &Path) -> OtherResult<()> {
142+
self.0.load_history(path)
143+
}
144+
pub fn save_history(&mut self, path: &Path) -> OtherResult<()> {
145+
self.0.save_history(path)
146+
}
147+
pub fn add_history_entry(&mut self, entry: &str) -> OtherResult<()> {
148+
self.0.add_history_entry(entry)
149+
}
150+
pub fn readline(&mut self, prompt: &str) -> ReadlineResult {
151+
self.0.readline(prompt)
152+
}
153+
}

src/shell/rustyline_helper.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,11 @@ impl Completer for ShellHelper<'_> {
152152
pos: usize,
153153
_ctx: &Context,
154154
) -> rustyline::Result<(usize, Vec<String>)> {
155-
if pos != line.len() {
156-
return Ok((0, vec![]));
157-
}
158155
Ok(self
159-
.complete_opt(line)
156+
.complete_opt(&line[0..pos])
160157
// as far as I can tell, there's no better way to do both completion
161158
// and indentation (or even just indentation)
162-
.unwrap_or_else(|| (line.len(), vec![" ".to_string()])))
159+
.unwrap_or_else(|| (line.len(), vec!["\t".to_string()])))
163160
}
164161
}
165162

0 commit comments

Comments
 (0)