Skip to content

Commit 8c1a8a1

Browse files
Merge pull request #1469 from RustPython/coolreader18/tabcomplete
Add tab autocompletion to the REPL
2 parents dccc22d + 1a68c86 commit 8c1a8a1

4 files changed

Lines changed: 443 additions & 234 deletions

File tree

src/main.rs

Lines changed: 6 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,24 @@ extern crate env_logger;
55
extern crate log;
66

77
use clap::{App, AppSettings, Arg, ArgMatches};
8-
use rustpython_compiler::{compile, error::CompileError, error::CompileErrorType};
9-
use rustpython_parser::error::ParseErrorType;
8+
use rustpython_compiler::compile;
109
use rustpython_vm::{
1110
import, match_class,
1211
obj::{objint::PyInt, objtuple::PyTuple, objtype},
1312
print_exception,
14-
pyobject::{ItemProtocol, PyObjectRef, PyResult},
13+
pyobject::{ItemProtocol, PyResult},
1514
scope::Scope,
1615
util, PySettings, VirtualMachine,
1716
};
18-
use std::convert::TryInto;
1917

18+
use std::convert::TryInto;
2019
use std::env;
2120
use std::path::PathBuf;
2221
use std::process;
2322
use std::str::FromStr;
2423

24+
mod shell;
25+
2526
fn main() {
2627
#[cfg(feature = "flame-it")]
2728
let main_guard = flame::start_guard("RustPython main");
@@ -365,7 +366,7 @@ fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> {
365366
} else if let Some(filename) = matches.value_of("script") {
366367
run_script(&vm, scope, filename)?
367368
} else {
368-
run_shell(&vm, scope)?;
369+
shell::run_shell(&vm, scope)?;
369370
}
370371

371372
Ok(())
@@ -457,220 +458,3 @@ fn test_run_script() {
457458
let r = run_script(&vm, vm.new_scope_with_builtins(), "tests/snippets/dir_main");
458459
assert!(r.is_ok());
459460
}
460-
461-
enum ShellExecResult {
462-
Ok,
463-
PyErr(PyObjectRef),
464-
Continue,
465-
}
466-
467-
fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> ShellExecResult {
468-
match vm.compile(source, compile::Mode::Single, "<stdin>".to_string()) {
469-
Ok(code) => {
470-
match vm.run_code_obj(code, scope.clone()) {
471-
Ok(value) => {
472-
// Save non-None values as "_"
473-
if !vm.is_none(&value) {
474-
let key = "_";
475-
scope.globals.set_item(key, value, vm).unwrap();
476-
}
477-
ShellExecResult::Ok
478-
}
479-
Err(err) => ShellExecResult::PyErr(err),
480-
}
481-
}
482-
Err(CompileError {
483-
error: CompileErrorType::Parse(ParseErrorType::EOF),
484-
..
485-
}) => ShellExecResult::Continue,
486-
Err(err) => ShellExecResult::PyErr(vm.new_syntax_error(&err)),
487-
}
488-
}
489-
490-
#[cfg(not(target_os = "wasi"))]
491-
fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
492-
use rustyline::{error::ReadlineError, Editor};
493-
494-
println!(
495-
"Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}",
496-
crate_version!()
497-
);
498-
499-
// Read a single line:
500-
let mut input = String::new();
501-
let mut repl = Editor::<()>::new();
502-
503-
// Retrieve a `history_path_str` dependent on the OS
504-
let repl_history_path = match dirs::config_dir() {
505-
Some(mut path) => {
506-
path.push("rustpython");
507-
path.push("repl_history.txt");
508-
path
509-
}
510-
None => ".repl_history.txt".into(),
511-
};
512-
513-
if !repl_history_path.exists() {
514-
if let Some(parent) = repl_history_path.parent() {
515-
std::fs::create_dir_all(parent).unwrap();
516-
}
517-
}
518-
519-
if repl.load_history(&repl_history_path).is_err() {
520-
println!("No previous history.");
521-
}
522-
523-
let mut continuing = false;
524-
525-
loop {
526-
let prompt_name = if continuing { "ps2" } else { "ps1" };
527-
let prompt = vm
528-
.get_attribute(vm.sys_module.clone(), prompt_name)
529-
.and_then(|prompt| vm.to_str(&prompt));
530-
let prompt = match prompt {
531-
Ok(ref s) => s.as_str(),
532-
Err(_) => "",
533-
};
534-
let result = match repl.readline(prompt) {
535-
Ok(line) => {
536-
debug!("You entered {:?}", line);
537-
538-
repl.add_history_entry(line.trim_end());
539-
540-
let stop_continuing = line.is_empty();
541-
542-
if input.is_empty() {
543-
input = line;
544-
} else {
545-
input.push_str(&line);
546-
}
547-
input.push_str("\n");
548-
549-
if continuing {
550-
if stop_continuing {
551-
continuing = false;
552-
} else {
553-
continue;
554-
}
555-
}
556-
557-
match shell_exec(vm, &input, scope.clone()) {
558-
ShellExecResult::Ok => {
559-
input.clear();
560-
Ok(())
561-
}
562-
ShellExecResult::Continue => {
563-
continuing = true;
564-
Ok(())
565-
}
566-
ShellExecResult::PyErr(err) => {
567-
input.clear();
568-
Err(err)
569-
}
570-
}
571-
}
572-
Err(ReadlineError::Interrupted) => {
573-
continuing = false;
574-
input.clear();
575-
let keyboard_interrupt = vm
576-
.new_empty_exception(vm.ctx.exceptions.keyboard_interrupt.clone())
577-
.unwrap();
578-
Err(keyboard_interrupt)
579-
}
580-
Err(ReadlineError::Eof) => {
581-
break;
582-
}
583-
Err(err) => {
584-
eprintln!("Readline error: {:?}", err);
585-
break;
586-
}
587-
};
588-
589-
if let Err(exc) = result {
590-
if objtype::isinstance(&exc, &vm.ctx.exceptions.system_exit) {
591-
repl.save_history(&repl_history_path).unwrap();
592-
return Err(exc);
593-
}
594-
print_exception(vm, &exc);
595-
}
596-
}
597-
repl.save_history(&repl_history_path).unwrap();
598-
599-
Ok(())
600-
}
601-
602-
#[cfg(target_os = "wasi")]
603-
fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
604-
use std::io::prelude::*;
605-
use std::io::{self, BufRead};
606-
607-
println!(
608-
"Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}",
609-
crate_version!()
610-
);
611-
612-
// Read a single line:
613-
let mut input = String::new();
614-
let mut continuing = false;
615-
616-
loop {
617-
let prompt_name = if continuing { "ps2" } else { "ps1" };
618-
let prompt = vm
619-
.get_attribute(vm.sys_module.clone(), prompt_name)
620-
.and_then(|prompt| vm.to_str(&prompt));
621-
let prompt = match prompt {
622-
Ok(ref s) => s.as_str(),
623-
Err(_) => "",
624-
};
625-
print!("{}", prompt);
626-
io::stdout().flush().ok().expect("Could not flush stdout");
627-
628-
let stdin = io::stdin();
629-
630-
let result = match stdin.lock().lines().next().unwrap() {
631-
Ok(line) => {
632-
debug!("You entered {:?}", line);
633-
let stop_continuing = line.is_empty();
634-
635-
if input.is_empty() {
636-
input = line;
637-
} else {
638-
input.push_str(&line);
639-
}
640-
input.push_str("\n");
641-
642-
if continuing {
643-
if stop_continuing {
644-
continuing = false;
645-
} else {
646-
continue;
647-
}
648-
}
649-
650-
match shell_exec(vm, &input, scope.clone()) {
651-
ShellExecResult::Ok => {
652-
input.clear();
653-
Ok(())
654-
}
655-
ShellExecResult::Continue => {
656-
continuing = true;
657-
Ok(())
658-
}
659-
ShellExecResult::PyErr(err) => {
660-
input.clear();
661-
Err(err)
662-
}
663-
}
664-
}
665-
Err(err) => {
666-
eprintln!("Readline error: {:?}", err);
667-
break;
668-
}
669-
};
670-
671-
if let Err(exc) = result {
672-
print_exception(vm, &exc);
673-
}
674-
}
675-
Ok(())
676-
}

0 commit comments

Comments
 (0)